Synteny visualization (OrthoFinder orthogroups)
The tutorials target the JBrowse v5 beta, and the v4.3.0 release
on the download page lacks some of what they show. To install the
beta of JBrowse Web, run npm install -g @jbrowse/cli@next, then
jbrowse create jbrowse2 --branch v5.0.0-beta.13. Desktop beta builds are coming soon.
Genomes too far apart to align base by base still keep their genes in order
along the chromosomes. We stack related genomes on OrthoFinder orthogroups, sets
of genes descended from one ancestral gene and inferred from protein similarity
alone, and read where gene order holds and where a duplication doubles it. The
page converts the orthogroups to a .blocks table that JBrowse reads and builds
the wheat set through to a stacked view, then opens four more sets:
vertebrates, drosophila, nightshades (solanaceae) and grasses.
Prerequisites
- OrthoFinder with
DIAMOND on
PATHasorthofinder - MCScanX (
MCScanX_h; biocondamcscanx), for the collinear blocks python3, htslib (bgzip,tabix),wgetnode, for the JBrowse CLI- NCBI datasets CLI
(
datasets,dataformat), forwheat,drosophilaandsolanaceae - gffread, for your own genomes only
- A running JBrowse instance (web or desktop)
Where the data comes from
Each set takes its protein FASTA and GFF3 files from one Ensembl division.
The build script takes these files from their URLs, so there is nothing to download by hand.
vertebrates, Ensembl release 113: ftp.ensembl.org/…/release-113https://ftp.ensembl.org/pub/release-113/wheat,solanaceaeandgrasses, Ensembl Plants release 63: ftp.ensemblgenomes.ebi.ac.uk/…/release-63https://ftp.ensemblgenomes.ebi.ac.uk/pub/plants/release-63/drosophila, Ensembl Metazoa release 63: ftp.ensemblgenomes.ebi.ac.uk/…/release-63https://ftp.ensemblgenomes.ebi.ac.uk/pub/metazoa/release-63/- Sequence reports, fetched from NCBI by accession: T. timopheevii GCA_963921465.1, tomato GCA_000188115.5, and the non-melanogaster flies GCA_016746395.2, GCA_016746365.2, GCA_009870125.2 and GCA_030788295.1: ftp.ncbi.nlm.nih.gov/…/GCAhttps://ftp.ncbi.nlm.nih.gov/genomes/all/GCA/
The wheat set: six genomes of one polyploid history
The wheat set is six genomes of wheat's polyploid history:
- Aegilops tauschii, diploid D donor
- bread wheat, hexaploid, A+B+D
- durum, domesticated tetraploid, A+B
- wild emmer, durum's wild tetraploid ancestor
- Triticum urartu, diploid A donor
- T. timopheevii, a second tetraploid also tracing to the A donor
Running OrthoFinder and building the blocks table
The build commands here produce the wheat set; the other four differ only in
the proteomes used:
# -og stops after the orthogroups, skipping the gene trees and the species
# tree, which this table does not use and which are most of the runtime.
# -S diamond picks the aligner; -t is threads.
orthofinder -f proteomes -og -S diamond -t "$(getconf _NPROCESSORS_ONLN)"The run writes
proteomes/OrthoFinder/Results_<date>/Orthogroups/Orthogroups.tsv, which the
next command reduces to one gene id per cell:
curl -fO https://raw.githubusercontent.com/GMOD/jbrowse-components/main/scripts/orthogroups_to_blocks.py
python3 orthogroups_to_blocks.py Orthogroups.tsv -o tauschii.blocks \
--bed tauschii=tauschii.bed --bed wheat=wheat.bed --bed durum=durum.bed \
--bed emmer=emmer.bed --bed urartu=urartu.bed --bed timopheevii=timopheevii.bed \
--max-copies 4blockAssemblies must match the column order the script prints.
--assembly COLUMN=NAME renames a column; a misspelled column there renames
nothing and raises no error, while a typo in --bed is an error:
python3 orthogroups_to_blocks.py Orthogroups.tsv -o tauschii.blocks \
--bed tauschii=tauschii.bed --bed wheat=wheat.bed \
--assembly wheat=bread_wheatDuplicated genes: one ribbon, two ribbons or none
In Orthogroups.tsv each cell lists one genome's genes in an orthogroup, so two
genes in one cell have no single ribbon. --pick chooses among three
treatments:
--pick | A rice gene with two maize orthologs | Use when |
|---|---|---|
first | one ribbon, to whichever maize gene OrthoFinder listed first | you want maximum coverage and accept the arbitrary choice |
expand (default) | two ribbons, one per maize copy | the duplication is part of what you are looking at |
single | no ribbon | you want a strictly one-to-one table |
A cell over --max-copies counts as a family and contributes no row:
python3 orthogroups_to_blocks.py Orthogroups.tsv -o tauschii.blocks \
--bed tauschii=tauschii.bed --bed wheat=wheat.bed \
--pick single --max-copies 2Open the grasses set's hosted config
(https://jbrowse.org/demos/orthofinder_grasses/config.json) as sorghum over rice
over maize, with each genome's gene track on, and navigate the rows to
1:5,934,000-6,126,000[rev], 3:31,590,000-31,775,000 and
1:286,676,000-287,665,000 5:6,261,000-6,790,000[rev]. Sorghum is the control:
it shares the ancestry and lacks the duplication.
Matching gene ids between the table and the BEDs
Column 4 of each BED must match the ids in the table exactly. The build script
keeps each gene's longest protein and names it by the gene id, so OrthoFinder
sees one protein per gene under the id the BED has. --bed name=file reports
the share of ids placed, and the conversion stops if it places none.
Loading the wheat orthogroups as a synteny track
One track backs each pair of adjacent rows in the stack:
Goes in the tracks array of config.json. See Tracks.
{
"type": "SyntenyTrack",
"trackId": "wheat_orthogroups",
"name": "Wheat orthogroups (OrthoFinder)",
"assemblyNames": [
"tauschii",
"wheat",
"durum",
"emmer",
"urartu",
"timopheevii"
],
"adapter": {
"type": "MCScanBlocksAdapter",
"uri": "tauschii.blocks.gz",
"blockAssemblies": [
"durum",
"emmer",
"tauschii",
"timopheevii",
"urartu",
"wheat"
],
"bedLocations": [
"durum.bed.gz",
"emmer.bed.gz",
"tauschii.bed.gz",
"timopheevii.bed.gz",
"urartu.bed.gz",
"wheat.bed.gz"
]
}
}jbrowse add-track-json '{
"type": "SyntenyTrack",
"trackId": "wheat_orthogroups",
"name": "Wheat orthogroups (OrthoFinder)",
"assemblyNames": [
"tauschii",
"wheat",
"durum",
"emmer",
"urartu",
"timopheevii"
],
"adapter": {
"type": "MCScanBlocksAdapter",
"uri": "tauschii.blocks.gz",
"blockAssemblies": [
"durum",
"emmer",
"tauschii",
"timopheevii",
"urartu",
"wheat"
],
"bedLocations": [
"durum.bed.gz",
"emmer.bed.gz",
"tauschii.bed.gz",
"timopheevii.bed.gz",
"urartu.bed.gz",
"wheat.bed.gz"
]
}
}'In JBrowse Desktop, or in any running JBrowse Web session, open a view on this track’s assembly, then File → Open track..., choose Add track from pasted JSON, and paste:
{
"type": "SyntenyTrack",
"trackId": "wheat_orthogroups",
"name": "Wheat orthogroups (OrthoFinder)",
"assemblyNames": [
"tauschii",
"wheat",
"durum",
"emmer",
"urartu",
"timopheevii"
],
"adapter": {
"type": "MCScanBlocksAdapter",
"uri": "tauschii.blocks.gz",
"blockAssemblies": [
"durum",
"emmer",
"tauschii",
"timopheevii",
"urartu",
"wheat"
],
"bedLocations": [
"durum.bed.gz",
"emmer.bed.gz",
"tauschii.bed.gz",
"timopheevii.bed.gz",
"urartu.bed.gz",
"wheat.bed.gz"
]
}
}tauschii.blocks.gz, durum.bed.gz, emmer.bed.gz, tauschii.bed.gz, timopheevii.bed.gz, urartu.bed.gz, wheat.bed.gz are relative to a config.json. Replace each with its URL or its path on this computer.
That shows it in the linear view. For the synteny view, open Add → Linear synteny view, pick the track under Quick start, and click Launch.
A mismatch between blockAssemblies and the column order the conversion printed
raises a track error naming both lists.
Assemblies from a chrom.sizes
A gene-level synteny view reads no sequence, so each assembly is a
ChromSizesAdapter over a .chrom.sizes file
(a name and a length per line) built from the ##sequence-region header in its
GFF3. We'll load wheat; the other genomes repeat the block under their own
names:
Goes in the assemblies array of config.json. See Assemblies.
{ "name": "wheat", "uri": "wheat.chrom.sizes" }jbrowse add-assembly wheat.chrom.sizes \
--name wheat \
--load copyIn JBrowse Desktop, Open new genome on the start screen (or File → Open genome... in a session), then Open from a URL and paste:
wheat.chrom.sizesJBrowse reads the format off the file name. Then fill in:
- Genome name:
wheat
wheat.chrom.sizes is relative to a config.json. Replace it with its URL or its path on this computer.
Gene tracks, one per genome
The multi-way display draws a lane per genome, with gene models from a gene
track on that genome's assembly. The grasses session below names a hosted one
(rice_genes). For your own annotation, add one per genome from a bgzipped,
tabix-indexed GFF3 (prep) that uses the assembly's
refNames:
Goes in the tracks array of config.json. See Tracks.
{
"type": "FeatureTrack",
"trackId": "wheat_genes",
"name": "wheat genes",
"assemblyNames": ["wheat"],
"adapter": {
"type": "Gff3TabixAdapter",
"uri": "wheat.gff.gz"
}
}jbrowse add-track wheat.gff.gz \
--trackId wheat_genes \
--name "wheat genes" \
--assemblyNames wheat \
--load copyIn JBrowse Desktop, or in any running JBrowse Web session, open a view on this track’s assembly, then File → Open track... and, in Add a track from file or URL, enter:
- Main file:
wheat.gff.gz
Click Next. JBrowse reads the adapter and track type off the file name. Then fill in:
- Track name:
wheat genes - Assembly:
wheat
Click Add.
wheat.gff.gz is relative to a config.json. Replace it with its URL or its path on this computer.
Stacking the wheat genomes
Add → Linear synteny view, pick wheat_orthogroups and add the six genomes
in the order tauschii, wheat, durum, emmer, urartu, timopheevii; the
all-vs-all tutorial walks the
dialog. Two view-menu items shape the stack, and the session below sets both:
- Show all regions - same bp per pixel (
sameScalein a session spec) puts the rows on one bp/px, so row length on screen matches genome size. - Rows → Re-order chromosomes (
autoDiagonalizein a session spec) sorts each row's chromosomes against its neighbour so the links run along a diagonal.
The wheat stack as a session over the hosted wheat config, the page's heaviest, so the bands take a while to draw:
Goes at the top level of config.json, replacing any defaultSession there. See Default session.
{
"defaultSession": {
"name": "Wheat lineage stack",
"views": [
{
"type": "LinearSyntenyView",
"views": [
{ "assembly": "tauschii" },
{ "assembly": "wheat" },
{ "assembly": "durum" },
{ "assembly": "emmer" },
{ "assembly": "urartu" },
{ "assembly": "timopheevii" }
],
"tracks": [
["wheat_orthogroups"],
["wheat_orthogroups"],
["wheat_orthogroups"],
["wheat_orthogroups"],
["wheat_orthogroups"]
],
"sameScale": true,
"autoDiagonalize": true,
"collapseEmptyRows": true,
"opacity": 0.15
}
]
}
}jbrowse set-default-session --session - << 'EOF'
{
"name": "Wheat lineage stack",
"views": [
{
"type": "LinearSyntenyView",
"views": [
{ "assembly": "tauschii" },
{ "assembly": "wheat" },
{ "assembly": "durum" },
{ "assembly": "emmer" },
{ "assembly": "urartu" },
{ "assembly": "timopheevii" }
],
"tracks": [
["wheat_orthogroups"],
["wheat_orthogroups"],
["wheat_orthogroups"],
["wheat_orthogroups"],
["wheat_orthogroups"]
],
"sameScale": true,
"autoDiagonalize": true,
"collapseEmptyRows": true,
"opacity": 0.15
}
]
}
EOFColoring wheat 4A by its tauschii chromosomes
We'll open tauschii over wheat from Add → Linear synteny view, with
wheat_orthogroups between them. Then:
- Navigate the wheat row to
4A. - Pick Query in the palette button menu. The palette sets the links'
color, a color scale from a field to colors, and Query paints each link by its tauschii chromosome.
Other chromosomes appear on 4A only as single genes. The 5D and 7D runs are the 4AL/5AL and 4AL/7BS translocations (Devos et al. 1995; Dvorak et al. 2018).
Vertebrates: runs of conserved genes out to zebrafish
The other four sets build with bash build_orthofinder_synteny.sh <set>
(Reproduce it end to end), and each has a hosted
config at https://jbrowse.org/demos/orthofinder_<set>/config.json. Open the
vertebrates config and stack human over chicken, frog, gar and zebrafish as
for wheat, with vertebrates_orthogroups in each
band, and pick Reference in the palette button menu.
The human HOXD cluster against four vertebrates
Open human at 2:176,090,000-176,290,000, the HOXD cluster of body-axis
patterning genes, with the human gene track and the orthogroups track on the
multi-way synteny display.
A lane (one horizontal strip per genome) for each of chicken, frog, gar and
zebrafish draws under it.
Drosophila: chromosome arms keep their genes while gene order changes
The drosophila set stacks D. melanogaster with close relatives D. simulans
and D. yakuba and the distant D. pseudoobscura and D. virilis. Across
these flies the chromosome arms (Muller elements) keep their gene content, while
inversions reorder the genes inside each arm.
In the drosophila config, stack the flies as simulans, melanogaster, yakuba,
melanogaster, pseudoobscura, melanogaster, virilis, so every band sets one fly
against melanogaster. Pick Reference in the palette button menu to paint
each ribbon by the melanogaster arm it leaves.
Gene order in one 3L window across the flies
Open melanogaster at 3L:5,789,000-5,931,000 with the
multi-way synteny display,
which draws a lane per fly. Every fly keeps this window's melanogaster genes.
Collinear blocks from the same orthogroups
An orthogroup says which genes are related, and nothing about which of them
still sit in order. MCScanX's MCScanX_h
chains ortholog pairs that keep their order into collinear blocks. Start by
writing every gene pair an orthogroup holds between two flies:
# every melanogaster-simulans pair in an orthogroup, each copy of a duplicated
# gene included, so MCScanX_h picks the collinear copy by gene order
python3 orthogroups_to_blocks.py Orthogroups.tsv -o melanogaster.blocks \
--homology melanogaster:simulans=x.homologyMCScanX_h reads those pairs and x.gff, four columns per gene (chromosome,
gene id, start, end) with each chromosome name prefixed by a two-letter code for
its genome:
# -s 10: a block holds at least 10 gene pairs
# -m 24: at most 24 genes skipped between two pairs of a block
# -g 0: a skipped gene costs nothing, so -s alone sets the size cutoff
# -b 2: blocks between the two genomes only, none within one
# -a: write the .collinearity file and skip the HTML
MCScanX_h x -a -b 2 -s 10 -m 24 -g 0Convert the blocks to PAF, one line per block, with each sequence named for its
assembly (melanogaster#1#2L):
curl -fO https://raw.githubusercontent.com/GMOD/jbrowse-components/main/scripts/mcscanx_to_anchors.py
python3 mcscanx_to_anchors.py --gff x.gff --collinearity x.collinearity \
--species aa=melanogaster --species bb=simulans \
--paf melanogaster.simulans.paf \
--chrom-sizes melanogaster=melanogaster.chrom.sizes \
--chrom-sizes simulans=simulans.chrom.sizesThe PAF files of every pair, concatenated, back every band of a stack from one track:
Goes in the tracks array of config.json. See Tracks.
{
"type": "SyntenyTrack",
"trackId": "drosophila_blocks",
"name": "Drosophila collinear blocks (MCScanX)",
"assemblyNames": [
"melanogaster",
"simulans",
"yakuba",
"pseudoobscura",
"virilis"
],
"adapter": {
"type": "MultiGenomePAFAdapter",
"uri": "https://jbrowse.org/demos/orthofinder_drosophila/drosophila.blocks.paf.gz",
"assemblyNames": [
"melanogaster",
"simulans",
"yakuba",
"pseudoobscura",
"virilis"
]
}
}jbrowse add-track https://jbrowse.org/demos/orthofinder_drosophila/drosophila.blocks.paf.gz \
--trackId drosophila_blocks \
--name "Drosophila collinear blocks (MCScanX)" \
--assemblyNames melanogaster,simulans,yakuba,pseudoobscura,virilis \
--adapterType MultiGenomePAFAdapterIn JBrowse Desktop, or in any running JBrowse Web session, open a view on this track’s assembly, then File → Open track..., choose Add track from pasted JSON, and paste:
{
"type": "SyntenyTrack",
"trackId": "drosophila_blocks",
"name": "Drosophila collinear blocks (MCScanX)",
"assemblyNames": [
"melanogaster",
"simulans",
"yakuba",
"pseudoobscura",
"virilis"
],
"adapter": {
"type": "MultiGenomePAFAdapter",
"uri": "https://jbrowse.org/demos/orthofinder_drosophila/drosophila.blocks.paf.gz",
"assemblyNames": [
"melanogaster",
"simulans",
"yakuba",
"pseudoobscura",
"virilis"
]
}
}That shows it in the linear view. For the synteny view, open Add → Linear synteny view, pick the track under Quick start, and click Launch.
Stack the flies in the same order as above and pick drosophila_blocks for
every band:
Nightshades: the same genes over genomes of very different sizes
The solanaceae set is tomato, potato, pepper and Nicotiana attenuata (the
row labelled tobacco), with coffee as the outgroup. Open the solanaceae config
and stack them as for wheat with Show all
regions - same bp per pixel, so row length matches genome size.
Grasses: the maize whole-genome duplication
Open the grasses config and stack the five grasses as for
wheat, with grasses_orthogroups in each band.
A rice window against four grasses
Open the same five grasses as a multi-way synteny track, under the rice gene track:
Goes at the top level of config.json, replacing any defaultSession there. See Default session.
{
"defaultSession": {
"name": "Grasses multi-way synteny track",
"views": [
{
"type": "LinearGenomeView",
"assembly": "rice",
"loc": "3:31,590,000-31,775,000",
"tracks": [
{
"trackId": "rice_genes",
"type": "LinearBasicDisplay",
"showOnlyGenes": true,
"displayMode": "compact"
},
{
"trackId": "grasses_orthogroups",
"type": "MultiWaySyntenyDisplay",
"rows": {
"domain": ["sorghum", "brachypodium", "setaria", "maize"]
},
"height": 320
}
]
}
]
}
}jbrowse set-default-session --session - << 'EOF'
{
"name": "Grasses multi-way synteny track",
"views": [
{
"type": "LinearGenomeView",
"assembly": "rice",
"loc": "3:31,590,000-31,775,000",
"tracks": [
{
"trackId": "rice_genes",
"type": "LinearBasicDisplay",
"showOnlyGenes": true,
"displayMode": "compact"
},
{
"trackId": "grasses_orthogroups",
"type": "MultiWaySyntenyDisplay",
"rows": {
"domain": ["sorghum", "brachypodium", "setaria", "maize"]
},
"height": 320
}
]
}
]
}
EOFA stacked view shows both maize copies at once. Launch → Linear synteny view (visible region) in the lane track menu offers a full row per grass.
Reproduce it end to end
build_orthofinder_synteny.sh
runs everything above and writes a config.json with the assemblies, gene
tracks, the synteny track and a stacked default session:
- Download each genome's proteome and GFF3, keeping each gene's longest protein under its gene id so the table and the BEDs agree, and write a gene BED and chrom.sizes per genome.
- Run OrthoFinder to the orthogroups only.
- Convert the orthogroups to a
.blockstable, dropping cells overMAXCOPIESas gene families, and print how one-to-one each adjacent pair of genomes is. - Chain every pair of genomes into collinear blocks with
MCScanX_h, and collect the blocks into one PAF. - Write the assemblies, gene tracks, the orthogroups and blocks tracks and a stacked session.
curl -fO https://raw.githubusercontent.com/GMOD/jbrowse-components/main/scripts/build_orthofinder_synteny.sh
bash build_orthofinder_synteny.sh wheat # or: vertebrates, grasses, drosophila, solanaceae
npx --yes serve orthofinder_wheat_build/jbrowse2 # then open the printed URLThe sets the script accepts, and what each costs to build:
| Set | Genomes | DIAMOND runs | Annotation source |
|---|---|---|---|
vertebrates | human, chicken, frog, gar, zebrafish | 25 | Ensembl 113 |
grasses | rice, sorghum, maize, brachypodium, setaria | 25 | Ensembl Plants 63 |
wheat | tauschii, wheat, durum, emmer, urartu, timopheevii | 36 | Ensembl Plants 63 |
drosophila | melanogaster, simulans, yakuba, pseudoobscura, virilis | 25 | Ensembl Metazoa 63 |
solanaceae | tomato, potato, pepper, tobacco, coffee | 25 | Ensembl Plants 63 |
Two cuts are environment variables:
| Variable | Default | What it cuts |
|---|---|---|
MAXSEQ | 30 | sequence regions kept per genome, the ones carrying the most genes |
MAXCOPIES | 4 | genes in one orthogroup cell past which it is a gene family rather than a set of copies |
MAXSEQ=60 MAXCOPIES=6 bash build_orthofinder_synteny.sh wheatThe NCBI datasets CLI names chromosomes that a GFF3 gives as INSDC accessions,
each from its sequence report.
Your own genomes
A set name tells the script which Ensembl files to download. Your own genomes need a FASTA and a GFF3 per genome. gffread translates each CDS into the proteome and prints the transcript-to-gene map and FASTA index alongside.
Name the files in a manifest, one line per genome:
cat > my_genomes.tsv <<'EOF'
# name genome annotation aliases
speciesA data/speciesA.fa.gz data/speciesA.gff3.gz
speciesB https://host/B.fa.gz https://host/B.gff3.gz GCF_000001405.40
EOF
bash build_orthofinder_synteny.sh my_genomes.tsv
npx --yes serve orthofinder_my_genomes_build/jbrowse2 # then open the printed URL- Column 1 names the assembly, and columns 2 and 3 take a local path or URL.
- Column 4 is optional. An INSDC accession fetches NCBI's sequence report for chromosome names, and a file name supplies a two-column alias table.
- Column 2 also takes a proteome directly, which skips translation; its headers
then need a
gene:<id>tag matchingID=gene:<id>in the GFF3. - Two genomes make a valid manifest.
See also
- Tutorial: Synteny from an ortholog table (grape, peach, cacao)
- Tutorial: Synteny from MCScan anchors (grape, peach)
- Tutorial: Synteny visualization (pairwise minimap2)
- User guide: Linear synteny view
- Config guide: Synteny track
- Reference: MCScanBlocksAdapter
Citations
- Emms and Kelly (2019). OrthoFinder: phylogenetic orthology inference for comparative genomics
- Wang et al. (2012). MCScanX: a toolkit for detection and evolutionary analysis of gene synteny and collinearity
- Devos et al. (1995). Structural evolution of wheat chromosomes 4A, 5A, and 7B and its impact on recombination
- Dvorak et al. (2018). Reassessment of the evolution of wheat chromosomes 4A, 5A, and 7B
Feedback on this tutorial is welcome: contact us.