Configuration¶
Everything MetaFlux does is decided by one YAML file. There are no command-line switches for analysis parameters — the run is fully described by its config, which makes it easy to archive next to the results and re-run later.
The shipped template is config/config.yaml. It is a template: the /path/to/...
entries are placeholders that must be replaced before anything runs.
How the file is loaded¶
The Snakefile hard-codes configfile: "config/config.yaml", so that file is
always read as a base layer. A file passed with --configfile is merged on top
of it, and --config key=value on the command line overrides both. A common pattern
is to copy the template to config/config.local.yaml (git-ignored) and run with
--configfile config/config.local.yaml, keeping the shipped template untouched.
Why the taxon filter lists ship commented out
Because the template is always loaded underneath the run config, any concrete
value left in it silently applies to runs that do not set their own. That is
harmless for a thread count and destructive for
amplicon.taxonomy.filter.keep: a 16S keep: [d__Bacteria, d__Archaea]
leaking into an 18S run would discard every eukaryotic ASV. The keep and
discard lines are therefore commented out in the template, so unset they
resolve to [] and match nothing, which fails safe. (filter.organelles ships
set: it needs no tokens and acts on 16S only, where it removes what Metaxa2 calls
mitochondria or chloroplast.)
Paths may be absolute or relative. Relative paths resolve against the directory snakemake is invoked from, not against the location of the config file. Absolute paths are safest, especially on a cluster.
Each section below is tagged with the mode that consumes it: [shared],
[amplicon], or [shotgun]. Keys belonging to the mode that is not running are
ignored entirely.
Which file a setting actually comes from¶
Four kinds of file are mentioned on this site; three of them can change a run:
| File | Where | Read by | Can it change a run? |
|---|---|---|---|
| These documentation pages | docs/**.md |
people | No |
| The config template | config/config.yaml |
Snakemake | Yes — always, as the base layer |
| A run config | any .yaml given to --configfile |
Snakemake | Yes — merged on top |
| A marker pack | workflow/markers/<type>.yaml |
Snakemake | Yes — but in its own namespace |
Documentation pages set nothing
Everything on this site is prose rendered from Markdown, including the per-marker
pages. Nothing under docs/ is ever read by Snakemake. The YAML blocks shown
on these pages are illustrations of what to put in your own config — they are not
in force anywhere, and they cannot override or be overridden by anything.
If two pages disagree, the documentation is wrong. What applies is
config/config.yaml plus whatever you merge on top of it.
The config layers¶
Three layers, each beating the one before it:
config/config.yaml— the Snakefile hard-codesconfigfile: "config/config.yaml". That path is resolved against the directory snakemake runs in, not against the Snakefile, so it loads on every run launched from the repository root — the supported way to invoke MetaFlux. Started from somewhere else, Snakemake either aborts (Workflow defines configfile config/config.yaml but it is not present) or, if you passed--configfile, silently uses that file alone with no template underneath.--configfile my_run.yaml— merged on top, key by key, at any depth.--config key=value— command line, beats both. Only top-level keys can be named this way:--config mode=shotgunworks,--config amplicon.dada2.merge.min_overlap=99is rejected outright. Nested settings belong in a--configfile.
The merge in step 2 is nested rather than wholesale, which is what makes short run configs practical. A run config naming two settings changes exactly those two:
# my_run.yaml — an overlay that changes two settings
amplicon:
dada2:
merge:
min_overlap: 10
output:
out_dir: /data/my_run_out
Running with --configfile my_run.yaml gives min_overlap: 10 and the new output
directory, while max_mismatch, just_concatenate, every trunc_len key and
everything else still come from the template. You never have to restate a block to
change one value inside it.
This is an overlay, not a complete run config: it inherits the template's
/path/to/... placeholders for input.fastq_dir and amplicon.primers, which still
have to be replaced before anything runs.
Only blocks are merged key by key
Key-by-key merging happens as long as what you write at each level is itself a block. Write a key with a list, a scalar, or no value at all, and it replaces that whole subtree in the template instead of merging into it.
The dangerous form is the last one, because it looks like an unfinished thought rather than an instruction:
That silently deletes all eight trunc_len keys the template provides, and the run
fails later with a KeyError from deep inside the workflow rather than a config
error. Same for max_ee: [1], which replaces [2, 5] outright rather than merging
element-wise. If you do not intend to change a block, leave it out entirely.
Marker packs live beside the config, not inside it¶
A marker pack holds facts about the marker: which database the classifier uses, which reference the amplicon-length probe measures, whether a region extractor exists, how many slots the marker's rank model has. A run config holds choices about this experiment: primers, expected length, truncation, taxon filter, paths.
The two are kept apart: a pack is read into MARKERS[type] and never merged into
config, so a setting cannot come from both places. No key of the shipped packs
appears under amplicon: in the config template; the only name they share is
references, described below.
If you genuinely need a different pack — another database release, a different rank
model — put a file of the same name in config/markers/. That is a whole-file
replacement, not a per-key merge: your file supersedes the shipped pack entirely, so it
must declare everything the pack needs, not just the part you wanted to change. That
includes reference_values, the rules the reference check tests the reference file
against before any classification; a pack for a marker of your own may write
reference_values: {} to have only the rank shape checked (see
the reference check).
A copy made for an older MetaFlux has no reference_values and stops every amplicon run
at start-up, naming the file, so after an upgrade copy the shipped pack again and
re-apply your change.
Where a pack and the config do interact¶
They interact in two ways, handled differently.
A config entry overriding a pack happens in one place: database locations. Every pack declares a default filename and download URL for each database it owns, and the run config only has to name one when you want to relocate it — to point at a copy already on a shared filesystem rather than let MetaFlux download its own:
A non-empty references.* entry wins. An entry left null or empty counts as
absent, so the pack default is used, without an error or a warning. The key that
relocates each database is listed in Markers.
Two 16S settings do not override the pack but choose between variants it declares:
references.silva.release picks the SILVA release (144 or 138.2), and
amplicon.taxonomy.silva_other picks which build of the SILVA 144 files is used
(other-verbatim or other-stripped in their names).
A pack overriding the config is the other direction, and the one case where the workflow does not do what the config says. It happens when a setting asks for something that does not exist for the chosen marker, typically in a config copied from another marker. There are two such cases, handled differently on purpose:
| You asked for | On these markers | What happens |
|---|---|---|
extraction.enabled: true |
18S, gyrB, rpoB, COI | warning, the run continues with extraction off |
taxonomy.method: sintax |
gyrB, rpoB | error, the run stops |
taxonomy.method: rdp |
COI | error, the run stops |
Region extraction¶
Metaxa2 cuts the ribosomal gene out of a 16S ASV; ITSx cuts ITS1 or ITS2 out of a
fungal one. gyrB, rpoB and COI are protein-coding genes, so neither tool applies to
them, and the 18S pack declares no extractor either. All four say extractor: none, and
the workflow builds no extraction step at all for them.
If your config still says extraction.enabled: true, the pipeline would go looking for
seqs_extracted.fasta — a file no rule produces — and Snakemake would stop with a
missing-input error naming a filename you never asked for. Instead MetaFlux turns the
setting off itself and tells you on stderr:
[MetaFlux] warning: marker 18S has no target-region extractor;
ignoring amplicon.extraction.enabled: true (no extraction step will run)
This one is a warning: the run continues without extraction, which is the intended result for a 16S config reused for 18S.
SINTAX classification¶
VSEARCH's --sintax needs a database in its own format. SILVA, UNITE and PR2 all have
one, so 16S, ITS and 18S can be classified either way. gyrB's DD7RZ8 trainset and
rpoB's FROGS build ship an RDP-style training set only, so those two packs leave
taxonomy_sintax_db empty. Asking for sintax there stops the run at once, naming the
alternative:
[MetaFlux] marker gyrB has no SINTAX reference database, so
amplicon.taxonomy.method: sintax is not available for it. Use method: rdp.
This case stops the run instead of falling back. Switching off a step that could not run changes no result; classifying with a method other than the one requested would change the results without notice.
COI is the mirror case: MIDORI2 is classified with SINTAX only, because DADA2's
assignTaxonomy would need about 225 GB of memory for it at species level, so the COI
pack leaves taxonomy_refdb empty and method: rdp stops the run in the same way:
[MetaFlux] marker COI has no rdp training set, so
amplicon.taxonomy.method: rdp is not available for it. Use method: sintax.
The five settings that define a run¶
Most of the file is tuning that can stay at its defaults. Five settings describe what the analysis actually is, the ones the box at the top of the template lists:
| Setting | Choices |
|---|---|
mode |
amplicon or shotgun |
amplicon.type |
16S, ITS, 18S, gyrB, rpoB, COI (amplicon mode only) |
shotgun.profiler |
kraken2 (the default), sylph or both (shotgun mode only; see Profiler below) |
input.fastq_dir |
where the paired FASTQs are |
output.out_dir |
where results go |
Mode selection [shared]¶
| Key | What it does | Default | Notes |
|---|---|---|---|
mode |
Chooses which set of rules is built into the workflow graph | amplicon |
Case-insensitive. Anything other than amplicon or shotgun stops the run at start-up. |
The mode is chosen here, never auto-detected from the data. Amplicon mode requires primer FASTAs; shotgun mode supports fetching reads from an NCBI BioProject and removing an arbitrary host genome. See choosing a mode.
The mode must match the data
Both modes accept {sample}_R1/_R2 filenames, so pointing amplicon rules at
shotgun reads does not necessarily error — it just runs the wrong analysis.
The mode can be flipped for a single run without editing the file:
--config mode=shotgun.
Input [shared]¶
input:
fastq_dir: /path/to/fastq_dir
bioproject: null # e.g. PRJNA603575 — shotgun only
accession_list: null # e.g. [SRR11605259, SRR11605260]
| Key | What it does | Default | Notes |
|---|---|---|---|
fastq_dir |
Directory holding the paired FASTQs for every sample | /path/to/fastq_dir (placeholder) |
Created if it does not exist, because BioProject mode downloads into it. Sample names come from globbing this directory. |
bioproject |
NCBI BioProject accession to fetch from SRA instead of using local files | null |
Shotgun only. null means use whatever is already in fastq_dir. |
accession_list |
Restricts a BioProject fetch to named runs | null |
A YAML list of run accessions. Useful when a BioProject mixes WGS with amplicon or RNA-Seq runs. |
File naming. Amplicon mode requires {sample}_R1.fastq.gz /
{sample}_R2.fastq.gz and stops with a clear message if it finds SRA-style
{sample}_1/_2 naming instead. Shotgun mode accepts either convention and detects
which one is in use. The reads themselves must be .fastq.gz or the uncompressed
.fastq: fq and fq.gz pass the start-up check but are not resolved by the
rules afterwards. The extension check only looks at the R1 files — a mixture
there is rejected rather than half-processed, while a mismatched R2 extension is
not caught, so the whole directory is best kept consistent.
Sample names are taken from the part of the filename before _R1/_1. Names
containing any of * # @ % ^ / ! ? & : ; | < > or a space are rejected at
start-up, since Snakemake treats those characters specially. Underscores are fine.
BioProject mode. When bioproject is set, MetaFlux queries NCBI e-utils,
keeps only runs whose library layout is PAIRED, and caches the run table as
{bioproject}_runinfo.csv inside fastq_dir so later invocations work offline.
The download_sra rule then fetches each run with prefetch plus fasterq-dump
and renames SRA's _1/_2 output to _R1/_R2.
Output [shared]¶
| Key | What it does | Default | Notes |
|---|---|---|---|
out_dir |
Root directory for every result, log and benchmark of the run | /path/to/output_dir (placeholder) |
Logs land in out_dir/logs/, benchmark timings in out_dir/benchmarks/. |
The directory layout inside out_dir differs by mode and is described in
output files.
References¶
references:
refdb_root: refdb
phix:
fasta: refdb/phix/phix.fna
fetch_url: https://ftp.ncbi.nlm.nih.gov/genomes/all/GCF/000/819/615/GCF_000819615.1_ViralProj14015/GCF_000819615.1_ViralProj14015_genomic.fna.gz
kraken_db: /path/to/kraken2_db
sylph_databases: []
sylph_taxonomy_dir: null
| Key | What it does | Default | Notes |
|---|---|---|---|
refdb_root |
Cache directory for the marker reference databases MetaFlux downloads itself | refdb |
Amplicon only. Shared across runs and across output directories, so a database is fetched once and reused. |
phix.fasta |
Local path of the PhiX genome | refdb/phix/phix.fna |
Amplicon mode also builds a bowtie2 index next to it (same basename, .bt2 files). |
phix.fetch_url |
Where the PhiX genome is downloaded from if missing | NCBI FTP, GCF_000819615.1_ViralProj14015 |
Fetched once by the fetch_phix rule and gunzipped. |
kraken_db |
Directory of a prebuilt Kraken2/Bracken index | /path/to/kraken2_db (placeholder) |
Shotgun only, user-supplied — MetaFlux never builds or downloads it. Needed when Kraken2 runs (shotgun.profiler: kraken2 or both); the run then stops immediately if the path does not exist. |
sylph_databases |
The sylph database files, one entry each with path and taxonomy |
[] |
Shotgun only, needed with shotgun.profiler: sylph or both, user-supplied. taxonomy is the sylph-tax name that goes with the file (GTDB_r232, FungiRefSeq-latest, …) or the path of your own lineage table. Checked at start-up; see sylph. |
sylph_taxonomy_dir |
Folder of sylph-tax lineage tables, filled once with sylph-tax download |
null |
Needed when a taxonomy above is a sylph-tax name. MetaFlux passes it to sylph-tax itself, so nothing in ~/.config/sylph-tax is read or written. |
PhiX is used by both modes, but differently. In amplicon mode the FASTA above is
fetched and indexed, and bowtie2 maps reads against it. In shotgun mode BBDuk uses
phix2.fa.gz, BBTools' own corrected copy of the PhiX genome, bundled inside the
BBTools conda package, so neither the download nor the index is needed there.
Marker databases are declared by the marker packs, not here¶
SILVA, UNITE, PR2, SILVA-Eukaryotic, DD7RZ8, FROGS, MIDORI2 and the set of bacterial COI
homologues are not listed in the config. Each marker pack (workflow/markers/<type>.yaml) declares its own database
filenames and download URLs, and MetaFlux fetches (and where needed converts) them
on first use into refdb_root. An entry under references is needed only to
choose the SILVA release for 16S (see SILVA release below) or to
relocate a database — for instance to point at a copy already on a shared
filesystem:
When such a key is present it wins; otherwise the path is refdb_root joined with
the pack's declared filename. The full set of relocatable keys:
| Config key | Marker | Default location under refdb_root |
|---|---|---|
silva.train_raw |
16S | silva/silva_nr99_v144_toGenus_trainset.fa.gz (SILVA 144 as published, seven ranks) |
silva.train |
16S | silva/silva_nr99_v144_toGenus_7rank_other-verbatim_trainset.fa.gz (built from train_raw: the same seven ranks, with the kingdom names written without --other; a built file, see below) |
silva.species |
16S | silva/silva_v144_assignSpecies.fa.gz |
silva.sintax |
16S | silva/silva_nr99_v144_7rank_other-verbatim_sintax.fa.gz (built from the trainset, not downloaded; see below) |
unite.fasta |
ITS | unite/sh_general_release_dynamic_s_all_19.02.2025.fasta |
unite.sintax |
ITS | unite/utax_reference_dataset_all_19.02.2025.fasta.gz |
unite.uchime_its1 |
ITS | unite/uchime_its1.fasta |
unite.uchime_its2 |
ITS | unite/uchime_its2.fasta |
silva_euk.fasta |
18S | silva_euk/silva_132.18s.dada2.fa.gz |
pr2.dada2 |
18S | pr2/pr2_SSU_dada2.fasta.gz |
pr2.utax |
18S | pr2/pr2_SSU_UTAX.fasta.gz |
pr2.sintax |
18S | pr2/pr2_SSU_UTAX_domain-first.fasta.gz (built from pr2.utax with the domain moved to the SINTAX letter d:, not downloaded; method: sintax only; see below) |
gyrb.dada2 |
gyrB | gyrb/train_set_gyrB_v6.fa.gz |
rpob.frogs |
rpoB | rpob/rpob_refseq_cc_20240707_domain-d_dada2.fa.gz (the DADA2 trainset converted from the FROGS archive, with the domain written d__; see below) |
midori2.raw |
COI | midori2/GB273/MIDORI2_UNIQ_NUC_SP_GB273_CO1_SINTAX.fasta.gz (MIDORI2's SINTAX file as published, extracted from its zip) |
midori2.taxonomy |
COI | midori2/GB273/taxonomy/nodes.dmp (MIDORI2's NCBI taxonomy snapshot; names.dmp is read from the same folder) |
midori2.sintax |
COI | midori2/GB273/midori2_gb273_uniq_nuc_sp_co1_metaflux_sintax.fasta.gz (built from the three files above and the MIDORI2 list of the bacterial set, not downloaded; see below) |
bacterial_cox1.fasta |
COI | bacterial_cox1/gtdb-r232.v1/bacterial_cox1_gtdb-r232.v1.fasta.gz (whole genes of the set of bacterial COI homologues) |
bacterial_cox1.fragments |
COI | bacterial_cox1/gtdb-r232.v1/bacterial_cox1_gtdb-r232.v1_fragments.fasta.gz |
bacterial_cox1.midori2_list |
COI | bacterial_cox1/gtdb-r232.v1/midori2_gb273_bacterial_records.tsv (the MIDORI2 records at least 90 % identical to a gene that passes checks 5.1–5.3 of the set's build, 186 in GB273, each marked removed (170, judged bacterial) or listed (16, kept); those marked removed are left out of midori2.sintax) |
The silva.* paths are those of SILVA 144 with silva_other: verbatim; with
silva_other: strip, other-verbatim becomes other-stripped.
Reference files MetaFlux builds itself¶
Five of these keys point at files MetaFlux builds rather than downloads:
| Key | Built from | By rule |
|---|---|---|
silva.train (SILVA 144 only) |
silva.train_raw, SILVA's own download |
convert_silva_train |
silva.sintax |
silva.train, the training set of the chosen release |
convert_silva_sintax |
pr2.sintax |
pr2.utax, PR2's UTAX download |
convert_pr2_sintax |
rpob.frogs |
the FROGS rpoB archive, downloaded by fetch_rpob_archive |
convert_rpob_to_dada2 |
midori2.sintax |
midori2.raw, midori2.taxonomy and bacterial_cox1.midori2_list |
prepare_midori2_coi_sintax |
A path given for one of them is where MetaFlux writes the file it builds. MetaFlux
rebuilds such a file whenever its input or its conversion script is newer than it (a
fresh clone, or an update that changes the script), or when the step's input or
software changes. So never keep a file of your own at such a path; a write-protected
file there stops the run as soon as MetaFlux wants to rebuild it. If you pin the input
of a built file, pin the built file to a path of its own too (pr2.sintax next to
pr2.utax, say): otherwise the default file is rebuilt from your input, or an older
build from another input is reused. Whichever path the classifier reads, the reference
check tests that very file before any classification (see
the reference check).
A reference that has neither a config entry nor a pack default stops the run with a message naming both places it could be fixed.
SILVA release (16S)¶
| Key | What it does | Default | Notes |
|---|---|---|---|
silva.release |
Which SILVA release a 16S run classifies against | 144 |
144 or 138.2. Each release has its own files under refdb_root, so switching never overwrites the other. SILVA 144 lineages have seven ranks, with a Kingdom between Domain and Phylum, and MetaFlux classifies on all of them: the 16S tables have the slots Domain, Kingdom, Phylum … Species. SILVA 138.2 has no kingdom, so its tables have Domain, Phylum … Species. See 16S rRNA. |
Amplicon parameters [amplicon]¶
Ignored entirely when mode: shotgun. The one exception: if amplicon.primers.fwd
or .rev is filled in during a shotgun run, MetaFlux prints a warning that they are
being ignored — so a leftover value is visible rather than silently misleading.
Marker, region and seed¶
amplicon:
type: 16S # 16S | ITS | 18S | gyrB | rpoB | COI
its_region: ITS2 # ITS1 | ITS2 (ignored unless type == ITS)
seed: 42
| Key | What it does | Default | Notes |
|---|---|---|---|
type |
Selects the marker pack: which databases are used, whether a region extractor runs, how the amplicon length is measured, and which taxonomic ranks the output carries | 16S |
Matched case-insensitively, so gyrb, GYRB and gyrB all resolve to the shipped gyrB pack. An unknown value lists the available markers and stops. |
its_region |
Which ITS subregion was amplified | ITS2 |
Only read when type: ITS. Selects the UNITE UCHIME probe reference and the ITSx region kept after extraction. |
seed |
Fixes the random number generator for the stochastic steps of the amplicon path | 42 |
Passed to dada_seqtab, assign_taxonomy and, when phylogeny is enabled, phylo_tree (all three backends). Keep it a positive number: VSEARCH reads 0 as "pick a random seed", and sintax results would then change from run to run. |
Each marker has its own page with the databases it uses and its quirks: 16S, ITS, 18S, gyrB, rpoB, COI.
How far the seed goes
DADA2's learnErrors(randomize = TRUE) and the bootstrap confidence of both
taxonomy methods are reproducible with this seed at any thread count (for sintax
from v2.5.0 on, which runs VSEARCH 2.32), except that assignTaxonomy breaks exact
ties between equally good reference lineages with its own random number, which can
move a bootstrap value by a point. Read order used to escape the seed — multithreaded bowtie2 phiX
removal emitted the surviving pairs in a run-dependent order, so ASVs with equal
total abundance could swap IDs — but bowtie2 now runs with --reorder, so the reads
and ASVs come out the same in every run. See
Troubleshooting.
PhiX removal¶
| Key | What it does | Default | Notes |
|---|---|---|---|
remove_phix |
Maps reads against the PhiX genome with bowtie2 and keeps the pairs that do not align | true |
Set false to skip. The 02.no_phix/ stage, its read count and the nophix Falco QC stage then do not exist, and Cutadapt reads the raw FASTQs directly. |
PhiX is spiked into most Illumina runs as a low-diversity balancing control; those reads are not part of the sample and would be carried into ASV inference.
Primers¶
primers:
fwd: /path/to/fwd_primer.fasta
rev: /path/to/rev_primer.fasta
orientation: fixed # fixed | mixed
| Key | What it does | Default | Notes |
|---|---|---|---|
fwd |
FASTA with the forward primer sequence | placeholder | Must exist — amplicon mode stops at start-up if either file is missing. |
rev |
FASTA with the reverse primer sequence | placeholder | Reverse complements of both are generated by the revcomp_primers rule; nothing needs to be supplied pre-complemented. |
orientation |
Whether R1 always carries the forward primer (fixed) or reads may come in either orientation (mixed) |
fixed |
mixed runs a second Cutadapt pass with the primers swapped, swaps the two mates of each pair it keeps so that its forward read is in R1, and concatenates the two passes. Both 5′ calls then need at least 10 bases of their primer instead of Cutadapt's default of 3, so that a pair is not kept by both passes by chance; this drops the pairs whose primer is found from only 3 to 9 of its bases, 0.006–0.15 % in the 16S, ITS, 18S, gyrB and rpoB test sets and at most 0.06 % in the COI mock libraries. fixed keeps the default and runs as before. For a library in which every pair is swapped, swap the two input files and keep fixed instead. See the mixed-orientation second pass. Anything other than fixed/mixed stops the run. |
The primer pair also drives the probe cache. The cache key is a short hash of both primer files, so changing a primer invalidates the cached amplicon-length measurement automatically, and reusing the same primers across runs reuses it.
Amplicon length¶
expected_length: auto # int | [min, max] | "auto"
probe_length_stat:
16S: p95
18S: p95
rpoB: p95
COI: p95
min_overlap: 12
expected_length is the length of the amplicon the primers produce. It is not used
to filter anything directly — it is the number that decides whether the chosen R1
and R2 truncation points still leave the pair able to merge.
| Key | What it does | Default | Notes |
|---|---|---|---|
expected_length |
Amplicon size used for the truncation overlap check | auto |
auto measures it from the reference database; an integer states it directly; a [min, max] pair states a window and the maximum is used for the overlap check. |
probe_length_stat.<key> |
Which statistic of the measured length distribution auto should take |
p95 for 16S, 18S, rpoB and COI |
One of min, q1, median, q3, p95, p99, max. |
min_overlap |
Bases of forward/reverse overlap the truncation picker must preserve | 12 |
A design constraint only. The overlap actually enforced when pairs are merged is dada2.merge.min_overlap. |
What auto does per marker. Markers whose reference is a full-length gene are
measured by in-silico PCR: the primers are matched against the reference and the
lengths of the surviving products form the distribution. Markers whose reference is
already cut to amplicon length are measured directly.
| Marker | How auto measures the amplicon |
Probe reference | Statistic key read |
|---|---|---|---|
| 16S | In-silico PCR; chloroplast, mitochondrial and eukaryote references are left out of the statistics | SILVA trainset | 16S |
| ITS | Direct read of pre-extracted subregion lengths | UNITE UCHIME ITS1 or ITS2 | not used |
| 18S | In-silico PCR | SILVA-Eukaryotic 18S | 18S |
| gyrB | Direct read — DD7RZ8 already ships trimmed amplicons | DD7RZ8 | 16S (deliberate reuse) |
| rpoB | In-silico PCR — FROGS ships full-length genes | FROGS RefSeq rpoB | rpoB |
| COI | In-silico PCR — MIDORI2 holds whole barcodes and whole genes | the converted MIDORI2 (midori2.sintax) |
COI |
Keep all four probe_length_stat keys
For every marker except ITS, MetaFlux checks at start-up that the key its
pack names is present in probe_length_stat, whatever expected_length is set
to, and exits with the missing key name if it is not. gyrB reads the 16S
key — that is a deliberate reuse, not a typo. Deleting any of the four
entries breaks the markers that point at it.
18S V9 must be sized manually
V4 primer pairs work with auto: in silico against SILVA-Euk, the probe
reference, the median core amplicon ranges from 381 bp (TAReuk454FWD1 /
TAReukREV3) to 596 bp (566F / 1200R, Hadziavdic et al. 2014). V9 (1391F/EukBr)
does not, because EukBr sits at the 3′ end of the gene, where most references are
truncated: only 41% of SILVA-Euk references (24% of PR2's) give a product, so
auto would size the window from a biased minority. Set it manually, for example
expected_length: [115, 150].
An integer must be between 50 and 10000 bp; a [min, max] pair must have exactly
two positive numbers with min <= max. Out-of-range or malformed values are
rejected with an explicit message rather than quietly used.
Details of the measurement and how it feeds truncation are on amplicon length and truncation.
Primer trimming (Cutadapt)¶
| Key | What it does | Default | Notes |
|---|---|---|---|
max_error_rate |
Cutadapt's -e — the fraction of the matched primer length that may differ |
0.2 |
Cutadapt reads IUPAC codes in the primer natively, so a degenerate primer costs nothing from this budget; it covers sequencing errors in the first cycles and genuine primer/template mismatch, which is common with universal primers. Cutadapt's own default is 0.1 — 0.2 is deliberately more permissive because the 5′ pass runs with --discard-untrimmed, so an unmatched primer costs the whole pair. Check the per-sample match rates in stats/cutadapt/ rather than assuming. The same value is reused by the in-silico PCR probe. |
min_length |
Reads shorter than this after trimming are discarded, together with their mate | 50 |
Cutadapt's --minimum-length, with --pair-filter=any. |
No quality trimming happens at this stage. DADA2's error model is fitted from the whole run's quality scores, and pre-trimming by quality would remove the low-quality bases it needs in order to learn the quality-to-error relationship.
Truncation¶
trunc_len:
mode: auto # auto | manual
q_threshold: 20
q_floor: 15
resolve_policy: raise_trunc # raise_trunc | relax_q | error
subsample_files: 12
manual_r1: # e.g. 260
manual_r2: # e.g. 240
min_read_coverage_pct: 95 # auto only
truncLen is one length per read direction; everything past it is discarded. A shorter
cut removes more of the low-quality 3′ tail, which would otherwise degrade the error
model; a cut that is too short leaves R1 and R2 without overlap, and a pair that cannot
merge is lost.
| Key | What it does | Default | Notes |
|---|---|---|---|
mode |
auto derives the cuts from observed quality; manual uses the two numbers below |
auto |
Manual mode skips the quality analysis completely. |
q_threshold |
Quality below which auto cuts |
20 |
The picker reads the lower quartile (Q1) of the per-base quality at each cycle from Falco's report, takes the median across samples, and cuts just before the first position where that value falls below the threshold. |
q_floor |
Lowest threshold relax_q may descend to |
15 |
Only used by the relax_q policy. |
resolve_policy |
What to do when the quality-based cuts leave too little overlap | raise_trunc |
See below. |
subsample_files |
Carried in the template but not read by any rule currently | 12 |
The picker uses every sample's Falco report, not a subsample. |
manual_r1 / manual_r2 |
Explicit R1 and R2 truncation lengths | unset | Required when mode: manual — the run fails if either is missing. Ignored for ITS. |
min_read_coverage_pct |
auto only: a cut is never placed past the length this percentage of a sample's reads still reach |
95 |
Read from Falco's "Sequence Length Distribution". See the note below — this is the guard against auto discarding an entire library. |
What min_read_coverage_pct is protecting against
Falco reports quality for every position up to the longest read in a
file, however few reads reach it. filterAndTrim discards any read shorter
than truncLen instead of padding it, so a cut placed where only a few reads
reach discards almost the whole library. On a run whose quality never drops below
q_threshold, nothing else stops the cut landing there.
Before quality is looked at, each sample's quality table is therefore cut off at the length
min_read_coverage_pct of its reads still reach. On the 18S
test dataset this moves the R1 ceiling from 288 bp to
271 bp. At 288 / 239, the cut MetaFlux v2.1.0 and earlier chose there, only 9 of the
908,768 primer-trimmed read pairs are long enough to pass; at 271 / 239, 90.9% of
the raw read pairs reach the final table.
The value is a guarantee about reads: raising it asks for a position that more
reads reach, which gives a shorter cut that discards fewer reads; lowering it
gives a longer cut that discards more. Between about 80 and 99 the cut barely
moves, because read lengths cluster. 100 does not switch the guard off: it
requires every read to reach the cut, which puts the ceiling at the shortest read
in the file. No value disables the guard.
logs/pick_trunclen.log prints each sample's longest read next to the length the
requested percentage reaches, and emits a NOTE when the two differ by 10 bp or
more.
The overlap constraint is truncR1 + truncR2 >= expected_length + min_overlap.
When the quality-based cuts break it:
| Policy | Behaviour |
|---|---|
raise_trunc |
Extends the cuts beyond the quality drop one base at a time, each time on the read with the higher aggregated lower quartile at the position it would gain (ties go to R1). Stops with an error if R1 and R2 together cannot gain the missing bases before reaching their ceilings. |
relax_q |
Lowers the quality threshold by 1 at a time, down to q_floor, re-cutting until the overlap holds. Errors if the floor is reached without success. |
error |
Stops immediately and reports the shortfall in bp. |
The decision, including which policy resolved it and how much slack is left, is
written to stats/trunclen.json.
ITS ignores truncLen
ITS amplicon length varies genuinely between taxa, so a fixed cut would throw
away real short amplicons. For ITS the picker still computes and records the
quality-based cuts, but dada_filter overrides truncLen to c(0, 0). The
3′ tail is not left untrimmed: dada2.trunc_q still trims each read
adaptively, with dada2.max_ee and the per-read minimum length as the quality
gate. This override also beats mode: manual, so manual_r1/manual_r2 have
no effect for ITS.
On very clean runs the auto cut is the read-length limit
If quality never drops below q_threshold, the cut is placed at the length reached
by min_read_coverage_pct (95%) of the reads in the strictest sample. Without this
limit, the cut could fall above most trimmed reads, and almost every read would be discarded
at the filtering step; see
troubleshooting.
On binned data, the cut can also be placed at this limit when quality declines towards
the end of the reads; see
binned quality scores.
ASV length filter¶
This filter runs on the ASVs, after target extraction, so the lengths it sees are directly comparable to the probe distribution the window was built from.
| Key | What it does | Default | Notes |
|---|---|---|---|
mode |
auto builds the window from the probe; manual requires an explicit range |
auto |
|
window_margin |
Bases added below and above the probe window | 50 |
The auto window is [probe_q1 − margin, probe_p95 + margin]. |
range |
Explicit [min, max] in bp |
null |
When set it wins, whatever mode says. |
When mode: auto runs without a probe (because expected_length was given
manually), the window falls back to the resolved expected length ±15%.
The filter writes stats/dada2/asv_length_stats.json — pre-extraction,
filter-source and kept distributions side by side — plus a histogram with the window
drawn on it. If the window removes every ASV the run stops rather than writing an
empty table.
ITS auto needs an auto expected length
type: ITS with length_filter.mode: auto also requires
expected_length: 'auto', since ITS never resolves an expected length through
the truncation picker and the fallback would have nothing to size from.
MetaFlux catches this combination at start-up, before the expensive DADA2
run, and the message offers two fixes: set expected_length: 'auto', or
switch to length_filter.mode: manual with an explicit range.
Target-region extraction¶
| Key | What it does | Default | Notes |
|---|---|---|---|
enabled |
Runs the marker's region extractor over the ASVs, trimming each to the target region and dropping ASVs the extractor cannot place | true |
Metaxa2 for 16S, ITSx for ITS. 18S, gyrB, rpoB and COI have no extractor. For 16S the organelle filter (taxonomy.filter.organelles) works from Metaxa2's call, so with false it does nothing and metaxa2_origin in asv_table_unfiltered.txt is NA. |
Markers with no extractor (18S, gyrB, rpoB, COI) should be run with
enabled: false. Left true, MetaFlux prints a warning and forces it off rather
than asking the workflow graph for a file no rule produces. The ASV length filter
still runs either way.
Taxonomy¶
taxonomy:
method: sintax # rdp | sintax
min_boot: 80 # rdp only
sintax_cutoff: 0.8 # sintax only
try_rc: true # rdp only
silva_other: verbatim # 16S on SILVA 144 only: verbatim | strip
| Key | What it does | Default | Notes |
|---|---|---|---|
method |
Which classifier assigns taxonomy | sintax |
rdp is DADA2's assignTaxonomy (plus addSpecies for 16S); sintax is vsearch --sintax, which uses roughly 2–3 GB regardless of ASV count (about 8 GB against MIDORI2 for COI). Both paths apply the same taxon filter and write the same tables. |
min_boot |
RDP bootstrap confidence a rank must reach to be reported | 80 |
Read only by the rdp path. Range 0–100. |
sintax_cutoff |
SINTAX confidence a rank must reach to be reported | 0.8 |
Read only by the sintax path. Range 0–1; 0.8 is roughly equivalent to min_boot: 80. |
try_rc |
Also tries the reverse complement of each ASV during classification | true |
Read only by the rdp path. The sintax path always searches both strands. |
silva_other |
What to do with SILVA 144's X--other names (a clade that does not contain the type of taxon X but belongs to it) |
verbatim |
verbatim keeps the names; the filter and addSpecies then treat X--other as X. strip removes the suffix before training, so the two become one taxon. The kingdom is always written without the suffix, whichever value is set. No effect with SILVA 138.2 or other markers. See 16S rRNA. |
gyrB and rpoB are rdp-only, COI is sintax-only
The DD7RZ8 and FROGS releases ship a DADA2 trainset with no SINTAX build, so
those packs declare no SINTAX reference. method: sintax for either marker is
refused at start-up with a message naming the marker, rather than failing later
on a missing path. The COI pack declares no rdp trainset (one trained on MIDORI2
would need about 225 GB of memory at species level), so method: rdp is refused
for COI in the same way.
Taxon filter¶
filter:
enabled: true
keep: #[d__Bacteria, d__Archaea]
discard: #[o__Chloroplast, f__Mitochondria]
discard_min_support: 50
organelles: metaxa2 # metaxa2 | off; acts on 16S only
| Key | What it does | Default | Notes |
|---|---|---|---|
enabled |
Master switch | true |
false keeps every ASV regardless of the lists, and switches organelles off too. For COI the frame check and the off-target guard have their own switches (amplicon.coi) and are not affected by this one. |
keep |
Tokens defining what survives | unset → [] |
An ASV is kept only if one of its ranks matches a token. An empty list does nothing. |
discard |
Tokens pruning what keep left |
unset → [] |
An ASV is removed if any of its ranks matches a token. Runs after keep. |
discard_min_support |
Best-hit check for discard, in percent |
50 |
An ASV is also removed when the classifier's best hit carries a discard token at that token's rank with at least this support (rdp bootstrap; SINTAX confidence ×100), even if the rank is below min_boot / sintax_cutoff and missing from the reported lineage. Catches mitochondrial 16S that is reported only to phylum or class. A number from 0 to 100. null (or false / off / no) switches it off, which gives the v2.4.0 behaviour. Anything else stops the run at start-up: a value outside 0–100, text that is not a number, and also true / on / yes, which YAML reads as a yes/no value rather than a number. It does nothing while discard is empty. See the best-hit check. |
organelles |
Also removes the ASVs that Metaxa2 calls mitochondria or chloroplast | metaxa2 |
16S only, and only with extraction.enabled: true, since Metaxa2 has to have run; for every other marker it is inactive and the run prints one info line. Runs after keep, discard and the best-hit check, and catches plant mitochondria the classifier reports as bacteria (e.g. Enterobacterales), which no token can match. Metaxa2's other calls (domains, eukaryota, uncertain) remove nothing. off (or null) switches it off; any other value stops the run at start-up. Every ASV, organelles included, is still listed in 06.taxonomy/asv_table_unfiltered.txt. See the organelle filter. |
Each token is matched against a whole segment of the taxonomy string
(d__Bacteria, o__Chloroplast, g__Ralstonia), so it targets exactly one rank
and never matches loosely somewhere else in the lineage. d__ is always the domain and
k__ always the kingdom, where the marker's reference has one: on 16S with SILVA 144,
and on ITS, whose top rank is the kingdom. There is no built-in default: whatever is
listed is exactly what is applied.
The tokens are checked when the run starts, before any step, also with
enabled: false. A token that can never match this run's taxonomy strings stops the run,
and the message names the token to write instead where there is one: a prefix the
strings never have (k__ on SILVA 138.2, d__ on ITS, s__ on 16S under sintax), a
domain under k__ (k__Bacteria, written d__Bacteria), or a name that the marker's
reference cannot hold in that slot (k__Fungi on 16S). See
when MetaFlux refuses a token at start-up.
| Marker | Suggested keep |
Suggested discard |
|---|---|---|
| 16S | [d__Bacteria, d__Archaea] |
[o__Chloroplast, f__Mitochondria] |
| ITS | [k__Fungi] |
[] |
| 18S | [d__Eukaryota] (PR2 ranks) — note this also drops PR2's organelle-tagged domains (d__Eukaryota:plas, :mito, :nucl, :apic, :chro) and the few ASVs left unclassified at Domain; see 18S |
[] |
| gyrB | no recommended default — paralogs are removed with discard: [other] on the DD7RZ8 gene-tag rank, the same keep/discard mechanism as above, not a separate filter |
|
| rpoB | no recommended default — FROGS RefSeq has no equivalent contamination to remove | |
| COI | [k__Metazoa] for animal surveys; empty to see everything MIDORI2 can name |
[] — bacterial COI homologues are moved by the off-target guard (amplicon.coi), not by a token |
Contaminants a blank extraction control reveals go in discard, e.g.
discard: [o__Chloroplast, f__Mitochondria, g__Ralstonia, g__Bradyrhizobium]. A
bare string is accepted where a list is expected — keep: k__Fungi is treated as
keep: [k__Fungi] — so a single token cannot be split into characters by accident.
The full treatment is on
keeping and discarding taxa.
The old regex keys are refused
Configs written before the token lists used include_pattern /
exclude_pattern regexes. Those keys now stop the run with instructions,
rather than being ignored and leaving the filter silently disabled.
COI screens¶
Used only when type: COI (the value of length_tolerance is checked at start-up for
every marker). Rule coi_screens measures every ASV for both screens and
writes 06.taxonomy/coi_screens.tsv; the taxonomy step applies them before the taxon
filter. A screen switched off still measures, so the table says what it would have done.
Its verdict column applies only the screens that are on, the frame check first (an ASV
that fails both is frame). For a screen that is off, read frame_ok and length_ok
(frame check) or compare B with E (off-target guard).
See COI.
| Key | What it does | Default | Notes |
|---|---|---|---|
frame_check |
Removes ASVs with TAA or TAG in the run's reading frame (or in the frame an offset of the ASV's start implies), or whose length is too far from the run's reference length (pseudogenes, NUMTs, artefacts) | true |
The run's frame and reference length are taken from the ASVs themselves (the frame open in most reads; the most frequent length among open ASVs). Removed ASVs stay in asv_table_unfiltered.txt as removed_frame. |
length_tolerance |
Largest length difference from the run's reference length, in bases, that the frame check accepts | 9 |
Three codons. Must be a whole number of bases, a multiple of 3 and not negative (9.0 and "9" are read as 9); anything else, a fraction, true/false or other text included, stops the run at start-up. |
offtarget_guard |
Moves ASVs closer to the set of bacterial COI homologues (B) than to MIDORI2 (E) out of the main tables, into 06.taxonomy/bacterial_cox1_asvs.tsv |
true |
Wolbachia and other bacterial COI homologues. Moved ASVs stay in asv_table_unfiltered.txt as moved_bacterial_cox1. |
DADA2 — tier 1, operational parameters¶
dada2:
pool: false # false | "pseudo" | true
max_ee: [2, 5]
trunc_q: 2
filter:
min_len: 20
min_len_stat: null
max_len: null
learn_errors:
nbases: 1e8
max_consist: 999
| Key | What it does | Default | Notes |
|---|---|---|---|
pool |
How samples are pooled during ASV inference | false |
false treats each sample independently (fastest); "pseudo" runs a second pass informed by ASVs seen across samples; true pools all samples (most sensitive to rare variants, most expensive). Strings are read case-insensitively. |
max_ee |
filterAndTrim's maxEE — the expected-error ceiling per read, as [R1, R2] |
[2, 5] |
R2 is looser because reverse reads degrade faster on Illumina chemistry. |
trunc_q |
filterAndTrim's truncQ — trims each read at the first base at or below this quality |
2 |
The DADA2 default. This is the adaptive 3′ trim, distinct from the fixed truncLen cut. It is applied before truncLen; see the note below. |
filter.min_len |
Per-read length floor after trimming | 20 |
DADA2's own filterAndTrim default. Used for every marker; for ITS it is the fallback when min_len_stat is unset. |
filter.min_len_stat |
Optionally derives min_len from the probe distribution instead |
null |
ITS only, opt-in. Left null, every marker uses filter.min_len. Setting a statistic (e.g. q1) puts the floor near 175 bp, above ~24% of the UNITE reference — see ITS. The other markers never take this path: their probe measures the full amplicon, which exceeds read length. |
filter.max_len |
Per-read length ceiling | null |
null means no upper limit. |
learn_errors.nbases |
Bases sampled to train the error model | 1e8 |
The DADA2 default. Reads are drawn in random order, seeded by amplicon.seed. |
learn_errors.max_consist |
Most rounds of error learning per read direction: DADA2 denoises the reads with its current error rates, estimates the rates again from the result, and stops when a round gives exactly the rates of an earlier one | 999 |
Not unlimited. Most models settle within 10 rounds and the slowest seen took about 100. A model that has not settled by the last round is used as it is, and logs/dada_seqtab.log says Self-consistency loop terminated before convergence. On a large library that never settles, 999 rounds can take days (see Troubleshooting). DADA2's own default is 10. |
truncQ runs before truncLen, and the leftovers are discarded rather than kept short
This is DADA2's own ordering inside filterAndTrim, not something MetaFlux changes,
but it produces a failure that is easy to misdiagnose. truncQ trims each read at its
first low-quality base; truncLen is then applied, and any read that truncQ has
already shortened below truncLen is dropped, not passed through at its
shorter length.
So a run that loses an unexpected share of reads at dada_filter may be losing them
to truncQ, while the number that looks wrong is truncLen. Before lowering
truncLen, check the filtering columns in stats/read_tracking.txt and consider
whether a lower trunc_q is the real cause — particularly on a run whose 3′ quality
degrades early.
DADA2 — tier 2, advanced tuning¶
dada:
omega_a: 1e-40
merge:
min_overlap: 12
max_mismatch: 0
just_concatenate: false
trim_overhang: true
chimera:
method: consensus
min_fold_parent: 4
allow_one_off: false
| Key | What it does | Default | Notes |
|---|---|---|---|
dada.omega_a |
OMEGA_A — how strong the evidence must be before a sequence is called a new ASV rather than an error of an existing one |
1e-40 |
The DADA2 default. A lower value is stricter; a higher one yields more ASVs. |
merge.min_overlap |
mergePairs minimum overlap, in bp, enforced at runtime |
12 |
DADA2's default. This is the one that actually decides whether a pair merges; amplicon.min_overlap is the design constraint used earlier when choosing truncation lengths. |
merge.max_mismatch |
Mismatches tolerated inside the overlap | 0 |
DADA2's default, and deliberate: after denoising, a mismatch in the overlap is evidence the pair should not merge rather than a sequencing error. |
merge.just_concatenate |
Joins R1 and R2 with a run of Ns instead of merging on overlap | false |
A safety valve for ITS amplicons too long to overlap at all. The joined sequence is R1 + R2 + spacer, not the amplicon length, so length_filter.mode: auto is no longer sizing a meaningful window — switch to manual with an explicit range sized for the concatenated length. |
merge.trim_overhang |
Trims bases that read past the far end of the amplicon | true |
Safe for 16S; necessary for short ITS amplicons where the read runs off the end into primer sequence. |
chimera.method |
removeBimeraDenovo method |
consensus |
Chimeras are called per sample and the verdicts pooled. |
chimera.min_fold_parent |
How much more abundant a parent must be than its suspected chimera | 4 |
DADA2's minFoldParentOverAbundance, raised above the package default. Higher is more permissive: parents must be more abundant to qualify, so fewer sequences are called chimeric and more ASVs survive. See the tip on the chimera settings below the table. |
chimera.allow_one_off |
Also flags sequences one mismatch or indel away from an exact chimera | false (DADA2's own default) |
With false, only exact chimeras of two parents are flagged. true also flags sequences one mismatch or indel away from an exact chimera, so more sequences are removed. |
What the min_overlap and max_mismatch defaults cost in practice
Looser values, such as 10 and 2, merge more pairs, but the extra pairs mostly turn into distinct, low-abundance ASVs, so the stricter defaults cost far more rare ASVs than reads. Filtering and denoising are not affected; the whole difference appears at the merge step. The measured cost for each marker is on the Test datasets page. Loosening them recovers those ASVs, at the cost of accepting overlap disagreements that DADA2 treats as evidence against merging.
ITS loses much more. ITS2 length varies widely between fungi, so with 2×300
reads the longer amplicons overlap by only a little — exactly the pairs a
10 → 12 bp requirement drops, and their short overlap sits in the
low-quality 3′ tails where max_mismatch: 0 then removes more. On an ITS run the
two settings together can cost a large share of the merged reads, so for ITS on a
variable community min_overlap: 10 with max_mismatch: 2 is a defensible
setting.
Where to look before changing the chimera settings
min_fold_parent: 4 is a deliberate departure from the DADA2 default, in the
direction of removing fewer sequences. The reasoning, and the values DADA2's
authors and users have discussed for pooled and amplicon-specific data (typically
4 to 8), are in these DADA2 issues; the right value depends on the marker and on
how samples were pooled:
- DADA2 issue: Questions about chimera filtering
- DADA2 issue: High loss of reads following chimera removal
- DADA2 issue: Remove chimeras, consensus vs. pooled
Before changing these settings, check that primers were fully removed: residual primer sequence is a common cause of apparent chimeras.
Phylogeny (16S)¶
amplicon.phylogeny. Off by default, and 16S only — enabling it for any other marker is
forced off with a warning at start-up. Builds a tree from the contaminant-filtered ASVs
in 06.taxonomy/ for phylogenetic-diversity analysis you perform yourself; MetaFlux
computes no diversity statistics. The full discussion, including why the other markers are
excluded and how to use the tree in R, is on the
Phylogeny page.
| Key | What it does | Default | Notes |
|---|---|---|---|
enabled |
Master switch | false |
With it off, the workflow has no phylogeny steps and builds no phylogeny conda environment. |
backend |
Which tree program runs | iqtree |
iqtree (full ML, model selection available) | fasttree (approximate ML, much faster, no model selection) | raxml-ng (full ML alternative). All three produce the same canonical outputs. |
aligner_strategy |
Which MAFFT strategy | auto |
auto: <200 ASVs → L-INS-i, ≥200 → FFT-NS-2, at MAFFT's own documented ceiling for its accuracy strategies. linsi / fftns2 force one — useful to keep runs of different sizes comparable. MAFFT's own --auto is never used: its thresholds are unpublished and size-dependent. The resolved choice is recorded in phylogeny.params.json. |
model |
Substitution model | auto |
auto → GTR+F+G4 (iqtree), GTR+Γ (fasttree, raxml-ng). Pinned rather than searched because IQ-TREE's own -m MFP default makes runtime data-dependent and re-decides on every run. Model selection is still reachable: MFP (IQ-TREE ModelFinder) or DNA (RAxML-NG MOOSE), and what it picks is recorded. FastTree has no model selection — only gtr or jc are accepted, anything else is a hard error rather than a silently ignored value. |
support |
Branch support values | false |
Off because neither UniFrac nor Faith's PD uses them and they dominate runtime. true → -B 1000 --alrt 1000 (iqtree) or FastTree's SH-like local supports (0–1, not bootstrap). Not available for raxml-ng: forced off with a warning. Raise mem_mb.phylo_tree if you enable it. |
extra_args.{mafft,iqtree,fasttree,raxml_ng} |
Verbatim passthrough | "" |
Appended to the tool's command line and recorded in phylogeny.params.json. Not validated — this is the one route by which machine-dependent behaviour MetaFlux otherwise blocks (-T AUTO, --threads auto) can re-enter a run. An unknown key here stops the run at start-up, so a typo cannot silently do nothing. |
The seed is amplicon.seed, shared with the rest of the amplicon path — there is no
separate phylogeny seed.
There is deliberately no rooting option: the tree is exported unrooted, only. You
will prune it in R (decontam, abundance filtering) before computing any metric, and a
root placed on the full ASV set stops being valid at the first pruned tip. A root: key
is rejected at start-up with a pointer to the docs rather than silently ignored.
The tree is reproducible at the shipped settings
Two runs on the same data give byte-identical trees when three settings are kept:
--reorder on the phiX step, an alignment built from a sequence-sorted FASTA, and
phylo_tree: 1. With more phylo_tree threads, two runs on the same alignment can
end on different trees of about equal likelihood. The taxonomy step that decides
which ASVs reach the tree needs no such setting: with a fixed amplicon.seed it
gives the same result at any thread count (sintax from v2.5.0 on; rdp apart from DADA2's rare random tie-breaks, see
the seed note above). Details in the
Reproducibility section of the
Phylogeny page.
Shotgun parameters [shotgun]¶
Ignored entirely when mode: amplicon.
Profiler¶
| Key | What it does | Default | Notes |
|---|---|---|---|
profiler |
Which profiler turns the cleaned reads into taxon tables | kraken2 |
kraken2: Kraken2 + Bracken, 03.abundance/otu_table.tsv. sylph: sylph + sylph-tax instead, 03.abundance/sylph_table.tsv, read pairs estimated from genome coverage plus an unassigned row. both: the two side by side on the same reads, never merged. Anything else stops the run at start-up. |
The PhiX, host and fastp steps below are the same for all three. Under sylph,
extract_taxa (it needs Kraken2's per-read output) and kmer_evidence (it filters the
Kraken2 report before Bracken) are switched off with a warning, and the kraken and
bracken settings are not used.
taxonomy_filter acts on otu_table.tsv only; with sylph or both it warns that the
sylph tables are written unfiltered. See sylph.
Decontamination¶
| Key | What it does | Default | Notes |
|---|---|---|---|
remove_phix |
BBDuk k-mer matching against the bundled phix2.fa.gz (BBTools' corrected PhiX genome), at k=31 hdist=1 |
true |
No download or index build is involved. Keep it on unless the lab is known to have skipped the spike-in. |
host_genomes |
YAML list of local paths and/or URLs to host FASTA(.gz) references | [] |
[] skips host removal. Multiple entries are concatenated and indexed once, so one BBMap index serves every sample. |
host_min_id |
BBMap minid — the alignment identity above which a read is called host-derived |
0.95 |
Reads that do not map are kept. |
Host removal is alignment-based rather than k-mer based, which is more sensitive to host reads that diverge from the reference assembly — the approach the BBTools authors recommend.
Use a masked reference for human
An unmasked human genome removes real microbial reads that happen to be
homologous to ribosomal or low-complexity human sequence. The masked reference
on Zenodo at https://zenodo.org/records/4116107 (Handley 2020,
human_virus_masked.fasta.gz, 889 MB) is a masked hg19 — ribosomal RNA,
plant, animal and fungal homology and low-entropy sequence removed — with a
further layer of viral masking added. The
first set of masks is what stops real microbial reads being discarded as host;
the viral layer additionally keeps human regions homologous to viruses out of
the filter, which is the right behaviour when the viral fraction is of
interest. Because the file is roughly 900 MB, download it once to a local path
and point host_genomes at it rather than re-fetching the URL on every fresh
run.
BBDuk and BBMap both need correctly paired files
Both tools read R1 and R2 in lockstep and require the two files to hold the
same number of records in the same order. Read length is not the issue: ragged
reads, and mates of one pair with different lengths, are processed normally.
With the bbmap release MetaFlux pins, a crash reading There appear to be
different numbers of reads in the paired input files (BBDuk or BBMap) means the
pairing is broken — usually because reads were filtered before deposition and
orphaned mates were left in one file. Repair the FASTQs with the tool shipped in
the same conda package rather than skipping the step.
repair.sh in1=R1.fastq.gz in2=R2.fastq.gz \
out1=fixed_R1.fastq.gz out2=fixed_R2.fastq.gz outs=singletons.fastq.gz
A crash reading java.lang.AssertionError: List size mismatch: N vs M is not a
pairing problem. It comes from newer bbmap releases (39.76 to 40.02), whose BBDuk
prints it on correctly paired reads too; run with MetaFlux's pinned environment
(--sdm conda) instead of repairing the files.
See troubleshooting and decontamination.
Adapter and quality trimming¶
| Key | What it does | Default | Notes |
|---|---|---|---|
min_read_length |
fastp's --length_required — reads shorter than this after trimming are dropped |
100 |
Comfortable for 2×150 bp reads; use around 70 for HiSeq 2×100. |
fastp runs with --detect_adapter_for_pe, plus --cut_front and --cut_right
sliding-window trimming from both ends. Its JSON report is read later by Bracken to
pick a read-length model, and by the read-tracking table.
Kraken2¶
| Key | What it does | Default | Notes |
|---|---|---|---|
confidence |
Fraction of a read's k-mers that must fall inside the clade of the taxon it is assigned to | 0.15 |
Higher is stricter. Kraken2 does not simply reject a read that falls short — it moves the label up the taxonomy until the score clears the threshold, and calls the read unclassified only if even root fails. Raising it therefore mostly pushes assignments to coarser ranks, leaving more for Bracken to redistribute, and only secondarily increases the unclassified fraction. Wright et al. (2023) report two optima on simulated data: 0.60 by mean F1, and 0.15 by L1 distance and by recall/precision over classified reads. The default of 0.15 favours accurate relative abundances; raise it towards 0.60 to favour F1. |
hit_groups |
--minimum-hit-groups — distinct k-mer hit groups a read needs to keep its classification |
3 |
|
memory_mapping |
Pages the database from disk on demand instead of loading it into RAM | true |
See below. |
threads |
Pins Kraken2's thread count | null |
null falls back to resources.threads.kraken2. |
Kraken2 is also run with --use-names, --paired, --gzip-compressed,
--report-minimizer-data and --classified-out, so classified read pairs are kept
alongside the report.
Memory mapping decides how much RAM a Kraken2 job asks for
Kraken2's memory request is computed at start-up, not taken from the
resources.mem_mb.kraken2 entry, which is not read.
memory_mapping: true— the kernel pages the database on demand and shares those pages between concurrent Kraken2 processes through the OS file cache, so running several samples in parallel does not multiply RAM use. Each job is budgeted a fixed 20000 MB of private workspace.memory_mapping: false— every process loads the whole hash privately. The budget becomes the size ofhash.k2dplus 10% headroom, so it self-adjusts to the database in use (a 16 GB PlusPF, a ~100 GB PlusPF, a 200+ GB core_nt). Ifhash.k2dcannot be read yet — while the database is still downloading, say — a conservative 110000 MB is used instead. (With memory mapping on the fixed 20000 MB applies either way, since the index size is never consulted.)
Bracken¶
| Key | What it does | Default | Notes |
|---|---|---|---|
tax_lev |
Rank at which abundance is re-estimated: P, C, O, F, G or S |
S |
Read only when Kraken2 runs (profiler: kraken2 or both). Drop to G for a shallow database, where species-level calls are not supported by the reference content. The OTU table then has one row per taxon at this rank, and the lineage below it is left empty (…;g__Bacteroides;s__). D and K stop the run at start-up: at those ranks Bracken counts the reads of viruses (D) or of protists (K) towards a neighbouring domain or kingdom, and on a GTDB database it finds no kingdom at all. For domain or kingdom totals, add up a lower-rank table by its d__ or k__ slot; see Troubleshooting. P, C, O and F print a warning at start-up: at those ranks Bracken can add the reads of taxa that lack the rank to a row they do not belong to (same Troubleshooting entry), so S or G is recommended. Up to v2.4.0, any value other than S gave an empty or nearly empty table; see Troubleshooting. |
threshold |
Minimum Kraken2 read count a taxon needs at tax_lev to enter Bracken's estimate |
auto |
Applied to Kraken2's own count for the taxon, before Bracken re-estimates anything. Taxa below it are removed from the model and their reads discarded rather than redistributed (Bracken reports these as "reads discarded"). auto computes it per sample from that sample's own classified read count, at run time, rather than using one fixed number for every sample regardless of depth; see Choosing confidence and threshold for the benchmark behind this. On the D6331 mock, the fixed default of 10 costs 0.427 F1 against the best threshold. Set an integer instead of auto to bypass this and use a fixed floor as Bracken does natively. |
threshold_alpha |
Scaling coefficient used only when threshold: auto |
5.0e-05 |
threshold = max(threshold_min, threshold_alpha × classified_read_pairs). Set from the benchmark data. On the CAMI II marine, rhizosphere and strain-madness samples the optimum is a broad plateau around this value; on the ZymoBIOMICS D6331 standard the best value is several times higher. See Choosing confidence and threshold before changing it. |
threshold_min |
Floor for the auto-computed threshold | 10 |
Prevents a near-zero threshold on a very shallow or heavily filtered sample. Also Bracken's own historic default. |
Bracken redistributes the reads Kraken2 assigned to internal nodes down to the target rank. Bracken databases ship k-mer distributions for several read lengths (50, 75, 100, 150, 200, 250, 300); MetaFlux reads the post-trimming mean read length out of the fastp JSON and picks the closest one, because applying a 150 bp distribution to 100 bp reads biases the redistribution.
Species-level k-mer evidence¶
kmer_evidence:
enabled: false
min_distinct_minimizers: 300
min_reads: 0
never_prune_above_fraction: 0.01
| Key | What it does | Default | Notes |
|---|---|---|---|
enabled |
Whether Bracken reads the gated report (the Kraken2 report without the taxa that have too little k-mer evidence) instead of Kraken2's own | false |
The evidence table, the gated report and the plot are written either way, so false changes nothing in the pipeline. Switched off with a warning under profiler: sylph. |
min_distinct_minimizers |
The floor: how many distinct minimizers a taxon at tax_lev needs to stay |
300 |
A whole number is one floor for every sample of the run; 300 is the published 1,000 unique k-mers converted to Kraken2's minimizers and rounded down. auto gives each sample its own floor, from its depth: (classified read pairs − Homo sapiens read pairs) / 1,000, at most 6,500, worked out after Kraken2 and written to logs/kmer_evidence/{sample}.log. auto is opt-in: its gain on MetaFlux's benchmark rests mostly on one real mock community; it changes the table much more than 300 does, and read pairs leave the table (2.7–15.1 % on the test dataset, against at most 0.09 % at 300); and it assumes paired 2×150 bp reads. Any other value stops the run at start-up. See Scaling the floor with depth. |
min_reads |
A clade-read floor, applied together with the one above: a taxon must clear both | 0 |
0 judges on k-mer evidence alone; 200 reproduces aMeta's pair of filters. |
never_prune_above_fraction |
Safety rail: a taxon holding more than this share of the classified reads is never removed | 0.01 |
The share is Kraken2's clade reads over all classified reads, not the taxon's share of the final Bracken table, so the rail rarely fires. |
The gate sits between Kraken2 and Bracken: it filters Bracken's input and does not
replace Bracken, so with enabled: true the corrected counts reach otu_table.tsv. A
removed taxon's reads go to its parent, for Bracken to share among the relatives that
passed. See Species-level k-mer evidence.
OTU-table taxon filter¶
| Key | What it does | Default | Notes |
|---|---|---|---|
enabled |
Master switch | false |
Off by default: metagenomic sample types vary too much for a universal default to be safe. |
keep |
Tokens defining what survives | [] |
e.g. [d__Bacteria, d__Archaea] for prokaryotes only; this drops viruses too, because a virus carries its realm in d__ (d__Duplodnaviria), or nothing there. |
discard |
Tokens pruning what keep left |
[] |
e.g. [g__Homo] as an explicit host safety net. |
This is the shotgun counterpart of amplicon.taxonomy.filter and uses the same
rank-aware token matching: keep first, then discard, empty list means that
direction does nothing. It is applied to the final OTU table after Bracken, so it
changes which OTUs appear in the table and never how reads were classified. A token
must equal one whole slot of the table's eight, d__ k__ p__ c__ o__ f__ g__ s__.
When Kraken2 runs, the tokens are checked at start-up, also with enabled: false,
against those slots and against the Kraken2 database: a wrong prefix, a domain under
k__, Viruses under any prefix, a virus realm under k__, and (with the filter on) a
keep list that no row of this database can match stop the run; a token this database
merely lacks, such as any k__ token on a GTDB database, gives a warning. See
keeping and discarding taxa.
If the filter removes every taxon from a table that had some, rule finalize_otu_table
stops with an error that names the tokens and lists the d__ values the table holds,
and no table is written; see
Troubleshooting.
A table that is empty before the filter, because no sample has a taxon at tax_lev, is
still written, with a warning in logs/finalize_otu_table.log.
Per-taxon read extraction¶
| Key | What it does | Default | Notes |
|---|---|---|---|
extract_taxa |
NCBI scientific names whose reads should be pulled out into their own FASTQ files | [] |
[] skips extraction entirely. Example: [Bacteria, Archaea]. |
Each name must be exactly the name of one row in the sample's Kraken2 report
(02.classification/kraken2/{sample}_report.txt), and it is looked up at whatever rank it
appears, so a broad group (Bacteria) and a single species (Escherichia coli) are
named the same way. KrakenTools then extracts every read under that taxid, including
its children, strains among them. A name found in several rows of a report
stops the run, unless the rows are a genus and a subgenus of the same name (then the
genus is extracted), and a single species gives only an approximate read set; the read
extraction page explains both.
Output files are named after a slug of the taxon (lower-case, spaces to
underscores). A taxon absent from a sample yields empty files rather than a failure.
See per-taxon read extraction.
Resources [shared]¶
resources:
threads_default: 4
mem_mb_default: 2000
threads:
kraken2: 16
# … one entry per rule
mem_mb:
kraken2: 20000
# … one entry per rule
Per-rule CPU and RAM live in one block with global fallbacks. Rules look their
values up by name; a name that is absent falls back to threads_default or
mem_mb_default. These values feed straight into Snakemake's resource accounting,
so they are what a cluster executor turns into job requests.
| Key | What it does | Default |
|---|---|---|
threads_default |
Threads for any rule with no threads entry |
4 |
mem_mb_default |
Memory in MB for any rule with no mem_mb entry |
2000 |
Threads¶
| Rule name | Mode | Default | Notes |
|---|---|---|---|
multiqc |
shared | 2 | |
falco |
amplicon | 2 | |
bowtie2 |
amplicon | 8 | Covers both build_phix_index and the per-sample rm_phix mapping. |
cutadapt |
amplicon | 4 | Covers both trim_primers and the in-silico PCR probe. |
pick_trunclen |
amplicon | 4 | |
dada_quality_plots |
amplicon | 4 | |
dada_filter |
amplicon | 4 | |
dada_seqtab |
amplicon | 8 | |
dada_length_filter |
amplicon | 2 | |
target_extract |
amplicon | 8 | Metaxa2 or ITSx. |
assign_taxonomy |
amplicon | 8 | With a fixed amplicon.seed the result does not depend on this value (for sintax from v2.5.0 on). |
coi_screens |
amplicon | 8 | COI only: VSEARCH against MIDORI2 (E) and the set of bacterial COI homologues (B). |
aggregate_read_counts |
shared | 2 | Present in the config template but not read — the rule declares no threads; see the note below. |
phylo_input |
amplicon | 1 | Reads two text tables, writes a FASTA. |
phylo_align |
amplicon | 4 | MAFFT. |
phylo_tree |
amplicon | 1 | Applies to the iqtree and raxml-ng backends only. One thread is a reproducibility setting, not a performance one: IQ-TREE's search depends on when each thread finishes, so two runs of the same alignment on several threads can end on different, about equally good trees, while two runs on one thread give the same tree. At amplicon scale one thread costs little: a minute or two for a few hundred ASVs. Raise it if you have thousands of ASVs and can accept a tree that is not exactly reproducible; the gain is limited anyway, because IQ-TREE parallelises across alignment columns and a 16S alignment is only a few hundred wide. This is the one key whose fallback is not threads_default — a rule that does not name it here gets 1, not 4. RAxML-NG clamps it further to what its own --parse step recommends (usually 1 for an amplicon alignment; it scales with the number of distinct alignment patterns) because it terminates when given far too many threads for a short alignment. The fasttree backend ignores this key — see the note below. |
phylo_export |
amplicon | 1 | Validates the tree, writes the Newick. |
phylo_qc |
amplicon | 1 | Reads the tree once for the QC report. |
decontam_phix |
shotgun | 6 | BBDuk scales poorly past 4–6 worker threads on a 5 kb reference; run more samples in parallel instead. |
build_host_index |
shotgun | 16 | |
decontam_host |
shotgun | 16 | |
fastp |
shotgun | 16 | Read by the trim_adapters rule. fastp scales poorly past about 16 threads. |
kraken2 |
shotgun | 16 | Overridden by shotgun.kraken.threads when that is not null. |
kmer_evidence |
shotgun | 1 | Only when Kraken2 runs, whether the k-mer gate is on or off. One job per sample; the script reads that sample's Kraken2 report and uses one thread. |
bracken |
shotgun | 4 | |
finalize_otu_table |
shotgun | 1 | One pass over the Bracken reports, building otu_table.tsv and otu_table.biom. |
extract_taxon_reads |
shotgun | 4 | |
compress_extracted |
shotgun | 6 | pigz parallelism for the extracted read files. |
download_sra |
shotgun | 6 | fasterq-dump parallelism; past about 8 the bottleneck is disk, not CPU. |
sylph_sketch |
shotgun | 1 | Only when sylph runs. One thread is a reproducibility setting: with two or more threads sylph removes duplicate k-mers in parallel and the sketch differs slightly from run to run, while one thread gives the same sketch every time and still takes only seconds per sample. Snakemake sketches several samples at once. Like phylo_tree, it falls back to 1, not threads_default. |
sylph_profile |
shotgun | 16 | Only when sylph runs. One job for all samples and databases; the thread count changes no number. |
sylph_taxprof |
shotgun | 1 | Only when sylph runs. sylph-tax uses one thread. |
sylph_tables |
shotgun | 1 | Only when sylph runs. |
sylph_read_tracking |
shotgun | 1 | Only with profiler: sylph; under kraken2 and both, aggregate_read_counts writes read_tracking.txt. |
Memory¶
| Rule name | Mode | Default (MB) | Notes |
|---|---|---|---|
multiqc |
shared | 2000 | |
bowtie2 |
amplicon | 4000 | |
pick_trunclen |
amplicon | 4000 | |
dada_quality_plots |
amplicon | 4000 | |
dada_filter |
amplicon | 2000 | |
dada_seqtab |
amplicon | 16000 | The heaviest amplicon step: error learning, denoising, merging and chimera removal all happen here across every sample at once. |
dada_length_filter |
amplicon | 4000 | |
target_extract |
amplicon | 8000 | |
assign_taxonomy |
amplicon | 16000 | The rdp path is the demanding one; sintax stays around 2–3 GB whatever the ASV count, except against MIDORI2 for COI, where it holds 8.43 GB at the peak. That figure is set by the reference, not by the ASVs: it was the same, to within 1 MB, for 767, 804 and 888 ASVs on 8 threads. The step took 71 to 86 s for 683 to 909 ASVs in the validation runs. |
coi_screens |
amplicon | 12000 | COI only: VSEARCH holds MIDORI2 GB273 (2.1 billion bases) and its k-mer index, 8.51 to 8.57 GB at the peak for 767 to 888 ASVs on 8 threads; the step takes about a minute (55 to 87 s for 683 to 909 ASVs in the validation runs). |
aggregate_read_counts |
shared | 2000 | Present in the config template but not read — the rule declares no mem_mb; see the note below. |
phylo_input |
amplicon | 2000 | Two small text tables in, one FASTA out. |
phylo_align |
amplicon | 4000 | |
phylo_tree |
amplicon | 8000 | Sized for the most demanding backend. FastTree was measured at ~181 MB on a 10,000-sequence, 400-column alignment, one thread; RAxML-NG estimates ~3.3 GB at 30,000 ASVs by its own formula and prints its estimate in the phylo_tree log; IQ-TREE is not separately measured but its likelihood vectors scale comparably. Raise this if you set support: true, which was not part of that sizing. |
phylo_export |
amplicon | 2000 | |
phylo_qc |
amplicon | 2000 | |
decontam_phix |
shotgun | 4000 | |
build_host_index |
shotgun | 30000 | BBMap index build for the host reference; a masked human genome needs roughly 24–28 GB. |
decontam_host |
shotgun | 30000 | Mapping against that index. |
fastp |
shotgun | 8000 | Read by the trim_adapters rule. |
kraken2 |
shotgun | 20000 | Not read — the real figure is computed at start-up from memory_mapping and the actual hash.k2d size. |
kmer_evidence |
shotgun | 2000 | Only when Kraken2 runs. Holds one sample's Kraken2 report and draws its evidence plot. |
bracken |
shotgun | 4000 | |
finalize_otu_table |
shotgun | 2000 | |
extract_taxon_reads |
shotgun | 2000 | |
sylph_sketch |
shotgun | 4000 | Only when sylph runs. Making a sketch uses a few tens of MB of memory for a few million read pairs (the sketch file itself is a few MB); deep libraries need more, hence the headroom. |
sylph_profile |
shotgun | null |
Only when sylph runs. null means worked out at start-up: 1.2 × the size of the database files plus 4096 MB, because sylph holds every database in memory (about 34,600 MB for GTDB r232 c200 with the fungal database). The start-up line prints the figure. A number here overrides it, for example on a cluster. |
sylph_taxprof |
shotgun | 2000 | Only when sylph runs. |
sylph_tables |
shotgun | 2000 | Only when sylph runs. |
sylph_read_tracking |
shotgun | 2000 | Only with profiler: sylph. |
Names here are lookup labels, not always rule names
Most entries match a rule name exactly. A few are lookup labels rather than
rule names: bowtie2 covers build_phix_index and rm_phix, cutadapt
covers trim_primers and amplicon_probe, and fastp is the label used by
the trim_adapters rule — there is no rule called fastp, and a
trim_adapters key here is ignored. Rules that declare no threads or
mem_mb at all — link_reads, count_reads_*, revcomp_primers, the
fetch_* and convert_* reference rules, prepare_midori2_coi_sintax,
check_taxonomy_reference, samples_manifest and aggregate_read_counts — take
Snakemake's own defaults and
ignore any entry of the same name. Adding a key for a rule that does not look it up
has no effect. Two keys break the other way: phylo_tree and sylph_sketch
fall back to 1, not threads_default, because there the thread count changes
the result itself (the tree, the sketch) and not just how long it takes.
One more special case: threads.phylo_tree is read by the iqtree and
raxml-ng backends but ignored by fasttree, which is pinned to a single
thread in the rule itself. This is deliberate: FastTree's parallel build is
documented as non-deterministic, both between runs and against the serial build,
and it does not speed up the maximum-likelihood phase beyond about three cores.
The pin also keeps Snakemake from reserving cores the job cannot use.
mem_mb.phylo_tree applies to all three backends normally.