Skip to content

16S rRNA (bacteria and archaea)

16S is the default marker: config/config.yaml ships with amplicon.type: 16S.

Where 16S settings come from

The YAML blocks on this page are examples of what to put in your own config; the page itself sets nothing. Two files decide how a 16S run behaves, and they do not overlap:

  • workflow/markers/16S.yaml — the marker pack. Facts about the marker: which databases to use, that the amplicon length is measured by in-silico PCR against SILVA, that Metaxa2 is the region extractor, which ranks the tables have (eight slots on SILVA 144, Domain to Species; seven on SILVA 138.2, which has no kingdom; the last slot, Species, only the separate species database can fill). It holds no run settings, and you normally do not edit it.
  • config/config.yaml, plus any run config merged on top — the choices about your experiment: primers, expected length, truncation, taxon filter, paths.

The pack is read separately and is never merged into the config, so almost no setting can come from both. The exceptions sit under references:. A database path there wins over the pack default — that is how you point at a shared copy of SILVA instead of downloading your own — and references.silva.release replaces the pack's default SILVA release. The full picture is in which file a setting actually comes from.

Setting up a run

Set type: 16S and supply the forward and reverse primer FASTAs for the region amplified. The rest of the block below reproduces the shipped defaults for the keys you are most likely to want to set on a 16S run — not every key the run touches; cutadapt, trunc_len and length_filter are covered further down and in the configuration reference. You do not need to copy any of it into your own config to get these values, since the template already provides them. The two exceptions are keep and discard: those ship commented out, so the values shown are the recommended 16S starting point rather than something already in effect.

amplicon:
  type: 16S

  primers:
    fwd: /path/to/fwd_primer.fasta
    rev: /path/to/rev_primer.fasta
    orientation: fixed            # fixed | mixed

  expected_length: auto           # in-silico PCR against SILVA
  probe_length_stat:
    16S: p95                      # which statistic of the probe distribution to use
  min_overlap: 12                 # bp of R1/R2 overlap the truncLen pair must preserve

  extraction:
    enabled: true                 # Metaxa2

  taxonomy:
    method: sintax                # rdp | sintax
    min_boot: 80                  # rdp only
    sintax_cutoff: 0.8            # sintax only
    try_rc: true                  # rdp only
    silva_other: verbatim         # SILVA 144 only: verbatim | strip
    filter:
      enabled: true
      keep:    [d__Bacteria, d__Archaea]          # recommended, ships commented out
      discard: [o__Chloroplast, f__Mitochondria]  # recommended, ships commented out
      discard_min_support: 50                     # best-hit check for discard; null = off
      organelles: metaxa2                         # drop what Metaxa2 calls organelles; off = keep

Note

amplicon.probe_length_stat.16S must be present for a 16S run, whether or not expected_length is auto — MetaFlux checks the key at start-up and exits with a message naming it if it is missing. The shipped config/config.yaml already has it.

Reference databases

16S is classified against SILVA. MetaFlux ships two SILVA releases: SILVA 144, the default, and the previous release, SILVA 138.2, which a run can still choose. None of the files needs to be listed in the config: the marker pack carries the file names, download URLs and md5 checksums, and MetaFlux fetches what the run needs on first use into references.refdb_root (default refdb), where it stays cached for later runs. Every download is checked against its md5 before anything uses it; on a mismatch the file is deleted and the run stops.

SILVA 144 (default)

The DADA2 files for SILVA 144 are built and published by the SILVA team itself (arb-silva.de); the 138.2 files come from the DADA2 author's own Zenodo record (below). MetaFlux downloads the 144 files from the release folder, release_144, rather than from "current-release", so the address does not change when SILVA 145 comes out.

Pack symbol Path under refdb_root What it is How it arrives
silva_train_raw silva/silva_nr99_v144_toGenus_trainset.fa.gz the training set exactly as SILVA publishes it, seven ranks; also the in-silico PCR substrate download from SILVA (rule fetch_silva_train_raw)
silva_train silva/silva_nr99_v144_toGenus_7rank_other-verbatim_trainset.fa.gz the same sequences and the same seven ranks, with the kingdom names written without SILVA's --other suffix: RDP training set built locally (rule convert_silva_train)
silva_sintax silva/silva_nr99_v144_7rank_other-verbatim_sintax.fa.gz SINTAX database for VSEARCH built locally from silva_train (rule convert_silva_sintax)
silva_species silva/silva_v144_assignSpecies.fa.gz addSpecies reference (16S, rdp only) download from SILVA (rule fetch_silva_species)

With amplicon.taxonomy.silva_other: strip (see below) the two built files carry other-stripped instead of other-verbatim in their names, so the two settings never share a cached file. "7rank" in the names says that the files keep all seven SILVA ranks.

The headers look like this — the differences matter for what each step can return:

silva_train_raw  >Bacteria;Pseudomonadati--other;Bacteroidota;Bacteroidia;Bacteroidales;Odoribacteraceae;Odoribacter;
silva_train      >Bacteria;Pseudomonadati;Bacteroidota;Bacteroidia;Bacteroidales;Odoribacteraceae;Odoribacter;
silva_sintax     >25;tax=d:Bacteria,k:Pseudomonadati,p:Bacteroidota,c:Bacteroidia,o:Bacteroidales,f:Odoribacteraceae,g:Odoribacter
silva_species    >AM889137.2051270.2052798 Neisseria meningitidis

The training set is a toGenus release: its lineages stop at genus, with no rank prefixes and no species field. That single fact explains both the addSpecies step and the rank depth of the two taxonomy methods, below.

Domain and kingdom

SILVA 144 re-annotated all prokaryote sequences, using LPSN as the main source of names, and added a Kingdom rank between Domain and Phylum. Its lineages therefore have seven ranks where 138.2 had six, and MetaFlux classifies on all seven: on SILVA 144 the domain is written d__ and the kingdom k__, followed by p__ to g__ and, from addSpecies, s__.

d__Bacteria;k__Pseudomonadati;p__Pseudomonadota;c__Gammaproteobacteria;o__Pseudomonadales;f__Pseudomonadaceae;g__Pseudomonas
  • Both come from the classifier, with the same cutoff as every other rank (min_boot under rdp, sintax_cutoff under sintax). The lineage stops at the first rank below the cutoff, so an ASV that the classifier can place in a kingdom but not in a phylum reads, for example, d__Bacteria;k__Bacillati.
  • Nine kingdom names can occur: Pseudomonadati, Bacillati, Fusobacteriati and Thermotogati for Bacteria; Methanobacteriati, Thermoproteati, Nanobdellati and Promethearchaeati for Archaea; and Incertae Sedis, SILVA's placeholder for the few phyla it places in no named kingdom. The few eukaryote outgroup records of the file carry a domain only.
  • The kingdom never carries --other. SILVA files whole phyla under Pseudomonadati--other (all of Bacteroidota, for example) and under Methanobacteriati--other. That is a note about where they sit in SILVA's tree (see the --other suffix, below); at kingdom level it only splits one kingdom into two labels, and the taxon filter reads X--other as X anyway. So rule convert_silva_train writes every kingdom without the suffix, whichever silva_other is set. It rewrites the headers only, never the sequences, and it refuses a file that does not have the shape of a SILVA 144 toGenus file (a 138.2 file, a toSpecies file, a broken download) rather than guess.
  • The rank set is checked. Before any classification, the reference check makes sure that the file the classifier reads has exactly SILVA 144's seven ranks, with a kingdom name in the second one. A SILVA 138.2 file, whose phyla would land in the Kingdom column, or SILVA's own download with its --other kingdoms, stops the run there.

The --other suffix (amplicon.taxonomy.silva_other)

SILVA 144 writes X--other for a clade that does not contain the nomenclatural type of taxon X, but whose sequences SILVA still considers part of X — for example Bacillaceae--other next to Bacillaceae, or Bradyrhizobium--other next to Bradyrhizobium. About 40% of the reference sequences carry it somewhere in their lineage. One setting decides what MetaFlux does with it:

silva_other Effect
verbatim (default) Names are kept exactly as SILVA writes them, apart from the kingdom, which is always written without the suffix (above). The keep/discard filter treats X--other as X (so g__Bradyrhizobium also removes g__Bradyrhizobium--other), and so does the genus check of addSpecies; the species is written with the plain genus (g__Lactobacillus--other;s__Lactobacillus iners).
strip The suffix is removed from every name before the classifier is trained, so X and X--other become one taxon.

On the 16S test set (PRJNA305879, SILVA 144, expected_length: 384, 229 ASVs), the two settings gave the same lineages apart from the suffix itself and the depth some of them reached. Under sintax, strip took 7 ASVs one rank further, 4 of them to genus (124 ASVs with a genus, against 120). Under rdp it took 19 ASVs one rank further and 3 one rank less far, a net 3 more with a genus (104 against 101). Most changes add a rank, because the classifier no longer has to choose between two labels for one taxon.

SILVA 144 also merges a few intermixed taxa into one label joined by +, each part with its own suffix: Enterobacter--other+Klebsiella--other, Rubrivivax--other+Ideonella--other (about 0.2% of the reference sequences). MetaFlux keeps these labels as they are: neither the filter nor addSpecies splits them, so a token g__Klebsiella does not remove an ASV labelled g__Enterobacter--other+Klebsiella--other. Add the full label as a token if you need to.

Choosing SILVA 138.2

To run on the previous release, add this to the run config:

references:
  silva:
    release: "138.2"

The 138.2 files are Benjamin Callahan's DADA2 build (Zenodo 14169026): six ranks, Domain to Genus, with no kingdom and no --other suffix. The training set is used as downloaded, with no conversion step, and silva_other has no effect. The tables then have seven slots, d__ Domain, p__ Phylum … s__ Species, and no k__:

d__Bacteria;p__Pseudomonadota;c__Gammaproteobacteria;o__Pseudomonadales;f__Pseudomonadaceae;g__Pseudomonas

The 138.2 files keep their own file names, so switching back and forth never overwrites the other release's files:

Pack symbol Path under refdb_root How it arrives
silva_train silva/silva_nr99_v138.2_toGenus_trainset.fa.gz download from Zenodo
silva_species silva/silva_v138.2_assignSpecies.fa.gz download from Zenodo
silva_sintax silva/silva_nr99_v138.2_sintax.fa.gz built locally from silva_train

Using a shared copy

Apart from the release, an entry under references: in the run config is needed only to relocate a file that already exists elsewhere, for instance on a shared filesystem. Under SILVA 144, relocate the files that belong together, so the training set and its SINTAX build always come from the same conversion:

references:
  refdb_root: refdb
  silva:
    train_raw:   /shared/dbs/silva_nr99_v144_toGenus_trainset.fa.gz
    train:       /shared/dbs/silva_nr99_v144_toGenus_7rank_other-verbatim_trainset.fa.gz
    sintax:      /shared/dbs/silva_nr99_v144_7rank_other-verbatim_sintax.fa.gz
    species:     /shared/dbs/silva_v144_assignSpecies.fa.gz

train and sintax are files MetaFlux builds itself, so a path given for them is where MetaFlux writes them, and it rebuilds them there whenever their input or the conversion script is newer (see reference files MetaFlux builds itself).

Amplicon length probe (probe_mode: pcr)

SILVA sequences are whole 16S genes (the SILVA 144 files are trimmed to the gene itself, 138.2 still carries some flanking sequence), so the length of the amplicon the primers actually produce has to be worked out rather than read off the database. The amplicon_probe rule does an in-silico PCR with two Cutadapt passes over SILVA's training set as downloaded (silva_train_raw on SILVA 144, silva_train on 138.2):

  1. keep the reference sequences in which the forward primer matches, trimming it and everything upstream of it (-g file:<fwd> --discard-untrimmed);
  2. of those, keep the ones in which the reverse complement of the reverse primer matches, trimming it and everything downstream (-a file:<rev_rc> --discard-untrimmed --minimum-length 30).

Neither match is anchored, because SILVA holds whole 16S genes and the primers bind inside them; a match anchored to the sequence ends would find nothing. Each pass strips the flanking gene sequence the primers would never have amplified, so what survives both is the amplicon body itself. Primer mismatches are tolerated at amplicon.cutadapt.max_error_rate (default 0.2).

Chloroplast, mitochondrial and eukaryote references are then left out of the length statistics (marker pack field probe_exclude_taxa). Bacterial primers amplify organelle SSU in silico too, often with long insertions, but those are not what a 16S survey measures, and the taxon filter removes them from the results anyway. SILVA 144 holds about eight times more organelle sequences than 138.2, enough to drag the statistic along: for 799F/1175R, p95 is 397 bp with them and 386 bp without. With 138.2 it is 384 bp either way. The amplicons FASTA still contains every product.

The rule then writes the length distribution — n_amplicons, min, q1, median, q3, p95, p99, max, mean, stdev, plus excluded_taxa and n_excluded — to a JSON file in the project-level cache:

refdb/cache/probe_16S_silva_v144_toGenus_excl-chloroplast-eukaryota-mitochondria_<primer_hash>.json
refdb/cache/probe_16S_silva_v144_toGenus_excl-chloroplast-eukaryota-mitochondria_<primer_hash>.amplicons.fa.gz

The excluded lineages are part of the name, so a probe computed before v2.5.0 (or with a different list) is never reused by mistake.

<primer_hash> is the first 12 hex characters of a SHA-256 over the two primer files. Different primers give a different filename, so the probe re-runs; the same primers hit the cache, which lives inside the repository and therefore survives across runs and across output directories.

Two steps consume the probe:

  • pick_trunclen takes expected_length from the statistic named in probe_length_stat.16S (default p95) and uses it in the overlap constraint.
  • dada_length_filter builds its ASV length window as [q1 − window_margin, p95 + window_margin], with window_margin defaulting to 50 bp.

The probe only runs when expected_length: auto. A fixed integer or a [min, max] range skips it entirely.

Warning

If no reference sequence carries both primers, the probe stops with an error rather than returning an empty distribution. The usual causes are a primer written in the wrong orientation for the database, or a degenerate primer that was collapsed to a single consensus sequence before being written to the FASTA — keep the IUPAC ambiguity codes, Cutadapt matches them. A low cutadapt.max_error_rate on a mismatch-tolerant primer has the same effect.

Truncation

16S uses the standard quality-based truncation picker: truncLen is cut just before the position where the aggregated per-base lower quartile first falls below trunc_len.q_threshold, subject to keeping the pair mergeable —

truncR1 + truncR2 >= expected_length + min_overlap

— with trunc_len.resolve_policy deciding what happens when quality and overlap disagree.

Three checks run in a fixed sequence. First, a coverage gate caps each sample at the length trunc_len.min_read_coverage_pct (95%) of its reads still reach, so the quality search cannot pick a position most reads never get to. Only then is the quality threshold applied, and only then the overlap constraint. See Amplicon length and truncation for the full picture, and Troubleshooting for what a run looks like when the cut still lands past the real read length.

Region extraction with Metaxa2

With extraction.enabled: true (the default), the target_extract rule runs Metaxa2 over the ASV sequences to keep only the part of each ASV that is genuinely small-subunit rRNA. Anything that flanks it — primer remnants, chimeric tails, non-ribosomal carry-over — is trimmed away, so the ASV lengths that reach the length filter are directly comparable to the probe distribution measured on SILVA.

Input 05.dada2/seqs.fasta and 05.dada2/seqtab_head_names.txt, from dada_seqtab
Command metaxa2 -t all --fasta T --table T --plus T --silent T
Output 05.dada2/seqs_extracted.fasta, 05.dada2/seqtab_extracted_head_names.txt, 05.dada2/metaxa2_extraction.results.txt, 05.dada2/metaxa2_origin.tsv
Raw tool output kept under 05.dada2/metaxa2/ for inspection
Consumed by dada_length_filter; assign_taxonomy reads metaxa2_origin.tsv

-t all means all domains are scanned and every extracted sequence is kept. Metaxa2 is not used to decide what is bacterial: that decision is made later, by the taxon filter, on the classified lineage. The filter does use one of Metaxa2's calls, the organelle one: ASVs that Metaxa2 places in mitochondria or chloroplast are removed (filter.organelles, below). ASVs Metaxa2 cannot extract are dropped from both the FASTA and the count table, and the drop is reported in the rule log.

For 16S the pre- and post-extraction length distributions usually look almost the same, because a clean 16S amplicon is SSU from end to end. Both are plotted in stats/dada2/asv_length_hist.png, which makes an unexpected difference easy to spot.

Metaxa2's two origin calls

The organelle filter uses the second answer only, collected per ASV in 05.dada2/metaxa2_origin.tsv. The difference matters most when you open the Metaxa2 files yourself to ask how much chloroplast and mitochondrial carry-over a run had, because the raw output under 05.dada2/metaxa2/ answers that question twice and the two answers can differ widely.

First pass — the HMM screen. Metaxa2 begins by sliding profile HMMs along each sequence to decide, cheaply, whether it is small-subunit rRNA at all and where the gene starts and stops. The job of this pass is to find the rRNA and mark its edges, not to identify it. Its verdict is the single letter in column 3 of seqs.extraction.results (B, A, E, C, M, O for bacteria, archaea, eukaryota, chloroplast, mitochondria, other) and the block of seqs.summary.txt headed "SSU rRNA sequences by preliminary origin". Metaxa2 itself labels this call preliminary.

Second pass — the BLAST refinement. The extracted regions are then aligned against Metaxa2's reference database and classified on the alignment. This is the call to use. It is what lands in seqs.taxonomy.txt, in the per-origin FASTAs (seqs.bacteria.fasta, seqs.mitochondria.fasta, and so on), and in the block of seqs.summary.txt headed "Number of SSU rRNA sequences assigned to each origin".

On the 16S test set (PRJNA305879, six samples, default settings), Metaxa2 recognised 228 ASVs as SSU rRNA:

Origin First pass, HMM screen Second pass, after BLAST
Bacteria 69 195
Chloroplast 55 0
Mitochondria 104 29
Uncertain — 4

By the first pass, 46% of these ASVs are mitochondrial and 24% chloroplast; by the second, 86% are bacterial and none is chloroplast.

The second reading is the correct one, and the disagreement is expected rather than a malfunction. Chloroplasts and mitochondria descend from free-living bacteria and their SSU genes still look bacterial, so the organellar HMM profiles and the bacterial ones overlap heavily. A fast profile scan cannot separate them; an alignment against full-length references can. The screen is deliberately permissive because its purpose is to avoid missing rRNA, not to name it.

These are ASV counts, not read counts

Every number above counts distinct sequences. On the same test set the 29 mitochondrial ASVs are 13% of the 226 ASVs that reach the taxon filter, but they carry 1.5% of the reads (2,984 of 196,081): organellar ASVs are often numerous but individually rare, and in a root or leaf sample it can be the reverse. To weight by reads, use 06.taxonomy/asv_table_unfiltered.txt: it has Metaxa2's call (metaxa2_origin) next to the per-sample counts of every ASV, the removed ones included (see the table with everything). asv_table.txt will not do, because the organelle filter has already removed the ASVs Metaxa2 calls mitochondria or chloroplast.

Taxonomy

amplicon.taxonomy.method picks one of two classifiers. Both read the length-filtered ASVs (05.dada2/seqs_lenfilt.fasta plus the matching seqtab_lenfilt_head_names.txt), both apply the same taxon filter, and both write the same four tables into 06.taxonomy/: the three filtered ones and asv_table_unfiltered.txt, which lists every ASV with Metaxa2's call and what the filter did to it.

rdp sintax
Tool DADA2 assignTaxonomy, then addSpecies vsearch --sintax
Reference silva_train + silva_species silva_sintax
Confidence setting min_boot: 80 sintax_cutoff: 0.8
Reverse complement try_rc: true --strand both, always
Deepest rank returned Species, as a binomial Genus
Conda environment workflow/envs/taxonomy.yaml workflow/envs/vsearch.yaml
Reproducibility fixed by amplicon.seed, apart from DADA2's random tie-breaking (rare, see Troubleshooting) fixed by amplicon.seed, at any thread count

The RDP path classifies the unique ASV sequences and then expands the result back so every ASV ID gets its own row — extraction can leave two ASVs with an identical trimmed sequence, and this keeps them from being classified twice.

VSEARCH SINTAX needs about 2–3 GB regardless of how many ASVs are in the table, which makes it the practical choice on large runs. With amplicon.seed fixed, its bootstrap confidence comes out the same at any thread count, because VSEARCH 2.32, the version MetaFlux uses from v2.5.0 on, gives every ASV its own random stream.

The species step (16S only)

silva_train stops at genus, so the RDP path alone cannot name a species. 16S is the only marker whose pack declares a taxonomy_species_db, and for it the rdp branch of assign_taxonomy runs DADA2's addSpecies against silva_species — a separate, species-annotated SILVA file — immediately after assignTaxonomy. MetaFlux calls it with DADA2's own defaults apart from tryRC, which follows taxonomy.try_rc (the same setting assignTaxonomy uses), so a species is added only on an unambiguous exact match; ASVs with no match, or with more than one, keep an empty species slot.

addSpecies returns the bare epithet (subtilis), not the full binomial, and the Species column of taxon_seq_table.txt keeps it as it is (see reading the tables in R for building the binomial there). The 16S pack sets prefix_style: bare, which tells the taxonomy-string builder two things: add the rank prefixes itself, and render the last slot as a Genus + species binomial by gluing the genus back on. A finished 16S lineage on SILVA 144 therefore reads (on SILVA 138.2 the same, without the k__ slot):

d__Bacteria;k__Bacillati;p__Bacillota;c__Bacilli;o__Bacillales;f__Bacillaceae;g__Bacillus;s__Bacillus subtilis

Under sintax there is no species rank at all. The SINTAX database is converted from the same toGenus training set and carries only the ranks down to genus (d: to g:; seven on SILVA 144, six on 138.2), so the Species column of taxon_seq_table.txt is NA for every ASV and no s__ segment appears in the taxonomy string. Species-level 16S calls require method: rdp, and under sintax a filter token with the s__ prefix stops the run at start-up, because it could never match.

Tip

Species assignment from a single 16S region is exact-match only and often absent — that is the method, not a failure of the run. Where species resolution is the point of the experiment, the two bacterial protein-coding markers are the alternative: gyrB has higher intra-genus resolution than 16S, and rpoB assigns far more ASVs to species than 16S V3–V4 does.

The prefixes are keyed to what a rank means: the domain is written d__ and, on SILVA 144, the kingdom k__, under both methods, whatever letters the SINTAX file uses internally. A keep: [d__Bacteria] token therefore behaves identically whichever method is selected (see domain and kingdom, above).

Keeping and discarding taxa

The keep and discard lists have no built-in default — they are commented out in the shipped config on purpose, so a run that forgets them applies no tokens rather than silently applying another marker's. The organelle filter (below) is on by default and needs no tokens, so it still runs. The recommended 16S starting point:

taxonomy:
  filter:
    enabled: true
    keep:    [d__Bacteria, d__Archaea]
    discard: [o__Chloroplast, f__Mitochondria]
  • keep runs first and defines what survives. [d__Bacteria, d__Archaea] keeps prokaryotes and drops both off-target eukaryotic hits and ASVs that stayed unclassified at domain level.
  • discard then prunes what is left. Chloroplast and mitochondrial 16S are amplified by most universal primers from any plant, algal or host material in the sample, and they are ribosomal enough to pass Metaxa2 — the filter is where they go.
  • Lab and kit contaminants a negative control revealed can be added to discard as further genus tokens, e.g. [o__Chloroplast, f__Mitochondria, g__Ralstonia].
  • ASVs that Metaxa2 calls mitochondria or chloroplast are removed as well (filter.organelles: metaxa2, the default; off keeps them), and every ASV is still listed, with Metaxa2's call, in 06.taxonomy/asv_table_unfiltered.txt — see the organelle filter.

Each token is matched against a whole rank segment of the taxonomy string, so g__Bacillus matches a genus named exactly that and never a substring somewhere else in the lineage (a SILVA 144 --other suffix is ignored, see above). A token that can never match a 16S string stops the run at start-up, before any step: one written for MetaFlux v2.4.0 or earlier (k__Bacteria, now d__Bacteria), a k__ token on SILVA 138.2, or a name that cannot stand in its slot (k__Fungi); see when MetaFlux refuses a token at start-up. If the filter removes every ASV after the classification, MetaFlux stops with an error instead of writing an empty table — that combination nearly always means the tokens belong to a different marker.

discard also looks one step past the reported taxonomy. The reported lineage stops at the last rank whose confidence reaches min_boot / sintax_cutoff, and mitochondrial 16S often stays below that at order and family level although the classifier's best hit is clearly Mitochondria — plant and algal mitochondria are diverse, and SILVA 144 holds many more of them than 138.2. Such an ASV is reported as p__Pseudomonadota, escapes f__Mitochondria, and turns up in the abundance table and, as a very long branch, in the tree. So an ASV is also discarded when its best hit carries a discard token at the token's own rank with at least filter.discard_min_support support (default 50, in percent; SINTAX confidences count ×100). Each such ASV is listed in the log with its support. discard_min_support: null switches the check off.

On the 16S test set, with the recommended filter above and otherwise default settings, this check removed 6 ASVs: all were reported only to phylum (p__Pseudomonadota), all had Mitochondria as best hit, and Metaxa2 called all of them mitochondrial. Full behaviour on Keeping and discarding taxa.

Plant mitochondria that no token catches

On the 16S test set (PRJNA305879, plant-associated), some mitochondrial ASVs got past every token: 13 ASVs (1,421 reads) with rdp on SILVA 144, 10 (1,292 reads) with either method on SILVA 138.2, and 3 (7 reads) with sintax on SILVA 144, the default. rdp reported most of them as Enterobacterales, some down to genus. Their best hit is another taxon, or Mitochondria with support below discard_min_support, so neither the tokens nor the best-hit check can catch them. Metaxa2 still calls them mitochondrial, and the organelle filter removes them on that call, so at the default settings they are gone whichever classifier and release you use. With filter.organelles: off they stay in the table.

What a 16S run produces

The final tables are in 06.taxonomy/:

File Rows Contents
asv_table.txt ASV_# IDs per-sample counts plus a taxonomy column
asv_table_seqs.txt ASV sequences the same table, keyed by sequence
taxon_seq_table.txt ASV_# IDs one column per rank plus the sequence: Domain, Kingdom, Phylum … Species on SILVA 144; Domain, Phylum … Species on SILVA 138.2
asv_table_unfiltered.txt ASV_# IDs, every ASV that reached the filter the same counts and taxonomy, plus Metaxa2's call (metaxa2_origin) and filter_status: kept, or the filter step that removed the ASV

Alongside them, stats/taxonomy_reference_check.json records what the reference check saw in the SILVA file, stats/trunclen.json records the truncation decision, stats/dada2/asv_length_stats.json and asv_length_hist.png record the ASV length distributions before extraction, after extraction and after the length filter, and stats/read_tracking.txt shows how many reads each sample kept at every stage. The full layout is in Output files.

16S is also the only marker with an optional phylogeny stage: switch it on and the run adds a maximum-likelihood tree of the retained ASVs, exported unrooted, for Faith's PD and UniFrac in R. It is off by default and adds nothing to the run when it stays off. See Phylogeny (16S).