Skip to content

Output files

Every simulate_* returns a result; result.write("out/", outputs=[...]) writes the files, and omitting outputs writes the default set. Trees are Newick, tables and logs are TSV, sequences are FASTA. Tree branch lengths are time everywhere except the sequence phylograms, whose lengths are in substitutions per site. The Default column says whether a file is written with no arguments (yes), only when you name its token (no), or is available in Python but has no file yet (Python).

A species-tree node is written n<id> everywhere it appears, and the column holding one is always called lineage (or donor / recipient where a row names two), so a node reads the same in any file of a run. A gene copy is written g<id> the same way — the same token the gene-tree Newick leaves and the alignment FASTA headers use, so a copy joins across files without translation — and the column holding one is always copy (or parent, the source copy of a duplication or transfer). In blocks.tsv alone, gene means something else — the genic classification of a block, 0 for spacer or the family id for a declared gene.

Where the files go

A zombi2 command groups what it writes, one directory per level, and gives every many-files-per-run output — one per gene family, or one per node — a directory of its own, so the level directory holds only its handful of tables and logs:

out/species/                species_complete.nwk · species_extant.nwk · species_events.tsv · species_fates.tsv
out/genomes/                genome_events.tsv · profiles.tsv · genomes.tsv · genomes.log
out/genomes/gene_trees/     gene_tree_fam<f>_complete.nwk · …_extant.nwk
out/genomes/homology/       homology_fam<f>.tsv                          (zombi2 tools format)
out/sequences/              clock_species_tree_complete.nwk · sequences.log
out/sequences/alignments/   fam<f>.fasta
out/sequences/phylograms/   phylogram_fam<f>_*.nwk
out/traits/                 trait_values.tsv · trait_tree.nwk · trait_events.tsv · traits.log

Filenames keep their prefix inside their directory — species/species_complete.nwk — so a file still names itself once it has been moved or copied somewhere else.

--flat on any command writes its files straight into the output directory instead, for a tool that expects one directory. The same files are written either way; only the directories differ. A run's files being grouped is a CLI matter, not the library's: result.write(dir) writes into whatever directory you hand it.

Every command that reads a prior level takes the run directory, and finds the file itself, in either layout: zombi2 genomes out/ and zombi2 traits out/ pick up that run's complete species tree, and zombi2 sequences out/ picks up its handoff files. --from reads from somewhere else — a Newick file, or another run — which is also how you write a run separate from the one you read.

Every zombi2 command also writes a run log (species.log, genomes.log, sequences.log, traits.log): the version, the timestamp, the command line, and every resolved parameter. Rates are recorded in their written formbirth<TAB>1.0 * OnTime({0: 1, 3: 0.3}) — so a line pastes straight back into the flag or a --params file. It is a CLI artifact, not a result.write() output, so it has no row in the tables below.

Species trees — simulate_species_tree

Output File Format Default Contents
Complete tree species_complete.nwk Newick yes every lineage, including extinct and unsampled
Extant tree species_extant.nwk Newick yes only the sampled survivors
Event log species_events.tsv TSV yes every speciation/extinction — time · kind · lineage · children
Tip fates species_fates.tsv TSV yes each tip's resolved fate — lineage · fate (extant / extinct / unsampled)
Fossils species_fossils.tsv TSV yes¹ sampled fossil lineages — lineage · time

¹ written only if fossil sampling recovered any.

Genomes, family — simulate_genomes_family

Output File Format Default Contents
Event log genome_events.tsv TSV yes the source of truth — time · kind · lineage · family · copy · parent · recipient · donor. A transfer writes two rows sharing a parent, and both name donor, the branch the material left, so either row gives the whole edge without pairing them. On the arriving row lineage and recipient are the same branch, so donor is what makes a self-transfer (donor == recipient) visible at all
Profiles profiles.tsv TSV yes family × extant-species copy counts
Genomes genomes.tsv TSV yes every node's gene content, ancestors includedlineage · family · copy. One row per gene copy, so a lineage holding six genes has six rows; two rows sharing a family are two copies of it. copy is the same identifier the event log uses, so a gene can be followed from the genome it sits in back to the event that made it. profiles.tsv is the same information counted, and only for the extant tips
Initial genome initial_genome.tsv TSV yes the genome the run started with, at the start of the root branch — family · copy. Its own file, with no lineage column, because it belongs to no node: every lineage elsewhere is a node, and a node sits at the end of its branch
Conditioning conditioned_on text conditioned written only when a rate was conditioned: the run's levels this run read via DrivenBy (one per line, e.g. traits). It records the dependency so re-running a driver level (say the trait) refuses to leave this run silently stale, or clears it under --force. A run with no driven rate writes no such file
Gene trees gene_tree_fam<f>_complete.nwk · …_extant.nwk Newick yes each family's true genealogy, in genomes/gene_trees/. A family with no surviving copy writes no _extant file
Family origination .gene_trees[f].origination float Python when the family was founded — where its gene tree's root branch begins

Genomes, ordered — simulate_genomes_ordered

Output File Format Default Contents
Event log genome_events.tsv TSV yes the run's whole history, in one time-ordered table. The gene genealogy as at the family resolution, plus where each event happened and the ancestry-neutral rearrangements as kinds of their own — time · kind · lineage · family · copy · parent · recipient · donor · dest_lineage · chromosome · position · length · dest_chromosome · dest_position · flipped¹. position / length are coordinates in that branch's own genome just before the event, as gene_order numbers it, and are filled once per event — on its first row — because the arc belongs to the event, not to each copy it touched. Filtering on a non-empty position therefore gives one row per event that moved genes, which is what a replay walks. A transfer writes a row on each branch and each names the whole edge: donor is on both, and the departing row adds dest_lineage for where the material went
Profiles profiles.tsv TSV yes family × extant-species copy counts
Gene order gene_order.tsv TSV yes signed gene order of every node, ancestors included — lineage · chromosome · position · strand · family · copy
Initial genome initial_genome.tsv TSV yes the genome the run started with, at the start of the root branch — chromosome · position · strand · family · copy. Its own file, with no lineage column, because it belongs to no node: every lineage elsewhere is a node, and a node sits at the end of its branch
Chromosome events chromosome_events.tsv TSV yes chromosome-network edges — time · kind · lineage · parents · children
Gene trees gene_tree_fam<f>_complete.nwk · …_extant.nwk Newick yes as at the family resolution — position is orthogonal to genealogy

¹ a run is named by start (its first position, in the chromosome's frame just before the event) and length (how many genes it covered), counted rightwards from start and wrapping past position 0 on a circular chromosome — so start + length greater than the chromosome's gene count means the run crossed the origin. dest_position is an index into what was left after the run was excised.

Genomes, nucleotide — simulate_genomes_nucleotide

From zombi2 genomes --resolution nucleotide or result.write(dir, outputs=[...]).

Output File Format Default Contents
Event log genome_events.tsv TSV yes the run's whole history, in one time-ordered table: the copy-lineage genealogy and the ancestry-neutral rearrangements — time · kind · lineage · chromosome · copy · parent · recipient · source · start · end · position · length · dest_chromosome · dest_position · flipped. One row per ancestral interval an event touched, so an event spanning several blocks writes several rows. kind initial marks the initial genome being laid down at time 0 (one per replicon) — what the run starts with, kept distinct from origination, the de-novo births the --origination rate makes, so counting one never counts the other. source/start/end are ancestral coordinates — which stretch of which source — while position/length are physical ones along the chromosome, as blocks.tsv numbers it: different frames, so different columns
Blocks blocks.tsv TSV yes every node's genome as its block mosaic, ancestors included — lineage · chromosome · position · source · start · end · strand · copy · gene. The rows of one chromosome tile it end to end from 0. The largest file this level writes: blocks are not kept maximal during a run, so it grows with their number × every node
Genes genes.tsv TSV yes the declared genes in initial coordinates — family · name · source · start · end · strand (the coding strand). Header-only when none were declared
Initial sequence initial_sequence.fasta FASTA yes¹ the initial DNA the run was given (--fasta), one >source<n> record per replicon. Written only when a FASTA was supplied; it is what lets a separate zombi2 sequences run found its blocks from the real sequence
Initial genome initial_genome.tsv TSV yes the genome the run started with, at the start of the root branch — chromosome · position · source · start · end · strand · copy · gene. Its own file, with no lineage column, because it belongs to no node: every lineage elsewhere is a node, and a node sits at the end of its branch
Chromosome events chromosome_events.tsv TSV yes chromosome-network edges — same format as ordered
Gene trees gene_tree_fam<f>_complete.nwk · …_extant.nwk Newick yes one tree per declared gene (else per recovered root-block)

The nucleotide log needs no separate positions file: its events carry ancestral coordinates already.

The events index against the species tree canonicalised so its n<id> labels match the lineage column, so a genomes run needs that exact tree to be replayable. A run grown by zombi2 species already has it; a run whose tree came from --from gets a copy written to its own species/species_complete.nwk, rather than a second file under another name. Either way zombi2 sequences can replay the gene genealogy from the run directory alone. Like names.tsv (external input trees) and the .log, that copy is a CLI artifact, not a result.write() output.

Sequences — simulate_sequences

The zombi2 sequences command replays a prior zombi2 genomes run — its own run directory, or --from another — its species tree and its genome_events.tsv. Gene outputs are written one file per gene family (<f> = family number); a family with no surviving copy writes none. Every node is labelled g<copy>, so a phylogram's tips pair with its alignment and its internal nodes with the ancestral sequences.

Output File Format Default Contents
Alignments fam<f>.fasta FASTA yes one row per extant gene copy — nucleotides or amino acids, following the model. The command puts these in sequences/alignments/, which is what lets the name be this short
Phylograms phylogram_fam<f>_complete.nwk · …_extant.nwk Newick (subs/site) yes the gene tree each family's sequences were drawn along, in sequences/phylograms/
Ancestral sequences_ancestral_fam<f>.fasta FASTA no the sequence at every node that is not an extant tip: internal nodes, and the tips where a copy was lost or its species died
Founding sequences_founding.fasta FASTA no one record fam<f> per family — the sequence it originated with, where its phylogram's root branch begins
Clock species tree clock_species_tree_complete.nwk · …_extant.nwk Newick (subs/site) yes the species tree with its branches in substitutions/site — the molecular clock made visible
Genomes genome_<lineage>.fasta FASTA yes one file per node of the complete tree — extant, extinct and ancestral alike — one record <lineage>_chr<c> per chromosome: the assembled genome, its blocks concatenated in physical order. Nucleotide genome runs only: a family or ordered run has gene families, not coordinates, so there is nothing to lay out. The biggest thing this level writes — a whole genome times every node
Initial genome genome_initial.fasta FASTA yes the genome the run started with, as sequence — the state the stem leads from, which is not any node's. Nucleotide runs only

On a nucleotide genome run every block evolves, spacer as well as gene, so a genome of b blocks writes b alignments and b phylograms — that is what makes the genomes assemblable. The number in those filenames is then a root block index, not a gene family id, and the files say so: block6.fasta and phylogram_block6_complete.nwk in place of fam6.…. The two numbering schemes are different, so go from a gene to its block with genomes.block_of(family) (Ch7). zombi2 sequences reads a nucleotide handoff too — it recognises one by its blocks.tsv — so all of this is reachable from the command line.

Joint — simulate_joint / zombi2 joint

A joint run grows two levels at once, so it writes both, each in the format its own command would give it: the species files, and then the driver's — the trait's or the genomes'. There is no output of its own beyond the run log.

Output File Format Default Contents
Species tree species/species_complete.nwk · …_extant.nwk · species_events.tsv Newick, TSV yes the grown tree — complete, so the extinct lineages the coupling decided the fate of are kept
The trait it grew traits/trait_values.tsv · trait_events.tsv · trait_tree.nwk TSV, Newick yes¹ as the traits level writes them
The genomes it grew genomes/genome_events.tsv · profiles.tsv · genomes.tsv · gene_trees/ TSV, Newick yes¹ as the genomes level writes them
Run log species/joint.log TSV yes the resolved parameters, as every command writes

¹ whichever driver the run used — one per run, never both.

Traits — simulate_continuous / simulate_discrete

Output File Format Default Contents
Values trait_values.tsv TSV yes value at each extant tip — node · trait
Events trait_events.tsv TSV yes (discrete) the trait's whole history — a root row giving the state at t=0, then every switch: time · kind · lineage · from · to, where kind is root · on_branch · on_speciation. Times are full precision (they drive a conditioned run's Gillespie). This is also the conditioning file: a genome/sequence run drives a rate with mod.DrivenBy("trait_events.tsv", …), replaying it against the shared tree. A continuous trait carries only the root row and any at_speciation jumps (a diffusion can't be rebuilt from events)
Trait tree trait_tree.nwk Newick no tree with every node annotated [&trait=…] (opens in FigTree / iTOL)

Coupling — no new files

Coupling adds no formats. A conditioned run writes the target level's files plus the driver file it read (above), keeping the pairing on disk; a joint run writes both levels, each in its own format.

The zombi2 tools commands write their own files — the homology matrix and the reconciliation/scoring outputs — catalogued in Appendix C.