Hemoglobin’s two subunits and the interfaces between them
Hemoglobin is a tetramer of two alpha and two beta chains, held together at two kinds of interface: alpha1beta1 within a dimer, and alpha1beta2, which moves when the tetramer binds oxygen. The viewer holds one sequence per row, so each of eleven vertebrates gets one row with both chains concatenated. We:
- align each subunit and concatenate the pair per species
- mark the subunit spans and infer a tree
- draw the cross-chain residue pairs of PDB 2HHB as arcs
- compare conservation at the interfaces, the core and the surface
- open the result from one link
Prerequisites
curl- ClustalW,
apt install clustalwon Debian or Ubuntu,brew install clustal-won macOS. ClustalW both aligns and infers a tree, so this page needs no other aligner. - python3 with Biopython,
pip install biopython, for the contacts and the solvent exposure
The data
Twenty-two globin proteins as NCBI RefSeq holds them, and one crystal structure from RCSB. Nothing to download: the commands below fetch both.
| What it is | File |
|---|---|
| All 22 protein sequences in one request, shown here with the human pair only | efetch.fasta |
| The human deoxyhemoglobin tetramer, PDB 2HHB, as mmCIF | 2HHB.cif |
The files this page writes, hosted for the figure links
| What it is | File |
|---|---|
| The row table the commands below start from | hemoglobin-rows.tsv |
| The alignment they write | hemoglobin.afa |
| Its tree | hemoglobin.nwk |
| The subunit spans | hemoglobin-subunits.gff |
| The contacts and the chain mappings, the layers the links below carry | hemoglobin-layers.json |
1. Name the rows
One species per line: the label the viewer draws, then a RefSeq protein per
subunit. The label becomes the FASTA defline, the tree tip, the GFF seq_id and
the row of every layer.
Human NP_000549.1 NP_000509.1
Macaque NP_001038189.1 NP_001157900.1
Mouse NP_032244.2 NP_001188320.1
Rat NP_001007723.1 NP_942071.2
Cow NP_001070890.2 NP_776342.1
Pig NP_001432111.1 NP_001138313.1
Horse NP_001078901.1 NP_001157490.1
Platypus XP_028905054.1 XP_028913684.1
Chicken NP_001004376.1 NP_990820.1
Zebrafish NP_571332.3 NP_001003431.2
Trout NP_001118023.1 XP_021413554.1
2. Fetch the sequences
One efetch request takes every accession, and a script splits the result into
one FASTA per subunit, labeled by species:
ids=$(awk -F'\t' '{printf "%s%s,%s", sep, $2, $3; sep=","}' rows.tsv)
curl -sf "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=protein&id=$ids&rettype=fasta&retmode=text" \
-o globins-ncbi.fasta
Human alpha NP_000549.1 142 aa beta NP_000509.1 147 aa
Macaque alpha NP_001038189.1 142 aa beta NP_001157900.1 147 aa
Mouse alpha NP_032244.2 142 aa beta NP_001188320.1 147 aa
Rat alpha NP_001007723.1 142 aa beta NP_942071.2 147 aa
Cow alpha NP_001070890.2 142 aa beta NP_776342.1 145 aa
Pig alpha NP_001432111.1 142 aa beta NP_001138313.1 147 aa
Horse alpha NP_001078901.1 142 aa beta NP_001157490.1 147 aa
Platypus alpha XP_028905054.1 141 aa beta XP_028913684.1 147 aa
Chicken alpha NP_001004376.1 142 aa beta NP_990820.1 147 aa
Zebrafish alpha NP_571332.3 143 aa beta NP_001003431.2 147 aa
Trout alpha NP_001118023.1 143 aa beta XP_021413554.1 147 aa
Every alpha is 141 to 143 residues and every beta 145 to 147, initiator methionine included, so the two subunits differ in length per species. Each row is one species’ adult alpha and adult beta chain, HBA1 and HBB in human.
3. Align the subunits separately
Alpha and beta are paralogs, so an aligner given both would put them in shared columns. ClustalW aligns each subunit on its own:
for sub in alpha beta; do
clustalw -INFILE=$sub.fasta -ALIGN -TYPE=PROTEIN -OUTORDER=INPUT \
-OUTPUT=FASTA -OUTFILE=$sub.afa
done
-OUTORDER=INPUT keeps the rows in the table’s order, so concatenating the
alignments is a loop over the labels:
alpha = read('alpha.afa')
beta = read('beta.afa')
with open('hemoglobin.afa', 'w') as fh:
for name, a in alpha.items():
fh.write(f'>{name}\n{a}{beta[name]}\n')
alpha: 143 columns, beta: 147 columns
concatenated: 290 columns, beta starts at column 144
Eleven rows of 290 columns, the alpha block left of column 144 and the beta
block right of it, with the tree from step 5.
4. Mark the subunits
Each row’s two spans go in a GFF in that row’s own residue numbering, so the
boundary lands correctly where the alpha chain is shorter or longer. A color=
attribute sets the overlay’s color:
alen = len(a.replace('-', ''))
blen = len(b.replace('-', ''))
g.write(f'{name}\tRefSeq\tpolypeptide_region\t1\t{alen}\t.\t.\t.\t'
f'Name=alpha;signature_desc=hemoglobin subunit alpha;color=%234e79a7\n')
g.write(f'{name}\tRefSeq\tpolypeptide_region\t{alen + 1}\t{alen + blen}\t.\t.\t.\t'
f'Name=beta;signature_desc=hemoglobin subunit beta;color=%23e15759\n')
Human RefSeq polypeptide_region 1 142 . . . Name=alpha;signature_desc=hemoglobin subunit alpha;color=%234e79a7
Human RefSeq polypeptide_region 143 289 . . . Name=beta;signature_desc=hemoglobin subunit beta;color=%23e15759


5. Infer a tree
clustalw -INFILE=hemoglobin.afa -TREE -TYPE=PROTEIN -OUTPUTTREE=phylip
tr -d '[:space:]' < hemoglobin.ph > hemoglobin.nwk
(((Human:0.02366,Macaque:0.02133):0.03786,((Mouse:0.04913,Rat:0.11350):0.04393,
(Platypus:0.14248,(Chicken:0.15042,(Zebrafish:0.18011,Trout:0.16126):0.13737):0.03453)
:0.02900):0.01972):0.00663,(Cow:0.07886,Pig:0.07794):0.00138,Horse:0.07648);
Zebrafish and Trout sit at the end of the longest branch, then Chicken, then Platypus, with the seven placentals filling the rest.
6. Read the contacts off the structure
In PDB 2HHB, the human deoxy tetramer at 1.74 Å, chain A touches chain B across the alpha1beta1 interface and chain D across alpha1beta2:
curl -sf https://files.rcsb.org/download/2HHB.cif -o 2hhb.cif
Biopython’s NeighborSearch finds every residue pair with a heavy atom of each
within 4 Å:
from Bio.PDB import MMCIFParser, NeighborSearch
def contacts(c1, c2, cutoff=4.0):
ns = NeighborSearch([a for r in c2 for a in r])
pairs = set()
for r in c1:
for atom in r:
for other in ns.search(atom.coord, cutoff):
pairs.add((r.id[1], other.get_parent().id[1]))
return sorted(pairs)
model = MMCIFParser(QUIET=True).get_structure('2HHB', '2hhb.cif')[0]
alpha1beta1 = contacts(model['A'], model['B'])
alpha1beta2 = contacts(model['A'], model['D'])
The RefSeq rows keep the initiator methionine and the crystal’s mature chains do not, so the script locates each chain inside the Human row:
chain A (141 aa) is row residues 2-142 of Human, chain B (146 aa) is 144-289
the Human row is 289 residues: alpha 1-142, beta 143-289
alpha1beta1: 32 residue pairs within 4.0 A, 14 alpha residues and 17 beta residues
alpha1beta2: 26 residue pairs within 4.0 A, 14 alpha residues and 14 beta residues
Chain residue n is row residue n + 1 within its subunit, and beta residue n is
row residue 143 + n. Each pair becomes an arc in an arc column track, and a
text track classes every other residue as buried, exposed or in between, from
ShrakeRupley over the tetramer divided by the residue type’s maximum
accessible area.
residue classes on the Human row: 29 at alpha1beta1, 28 at alpha1beta2, 73 buried, 107 exposed, 52 in between

7. Compare conservation across the classes
Per class, the mean share of the ten other rows with the Human letter:
alpha1beta1 interface 29 residues, mean identity 0.77, 10 identical in all 11 rows
alpha1beta2 interface 28 residues, mean identity 0.92, 20 identical in all 11 rows
buried, no interface 73 residues, mean identity 0.78, 21 identical in all 11 rows
exposed, no interface 107 residues, mean identity 0.69, 22 identical in all 11 rows
23 of 58 contact pairs have both residues identical in all 11 rows
The alpha1beta2 residues are the most conserved class, with 20 of the 28 identical from human to trout.


8. Check it against the raw data
The letters in every row at the Tyr42 to Asp99 pair, and at the alpha1beta1 pair that varies most:
alpha1beta2 Tyr42-Asp99: row residues 43 and 242, columns 43 and 243
Human Y D
Macaque Y D
Mouse Y D
Rat Y D
Cow Y D
Pig Y D
Horse Y D
Platypus Y D
Chicken Y D
Zebrafish Y D
Trout Y D
alpha1beta1, most variable: row residues 112 and 258, columns 113 and 259
Human A A
Macaque A A
Mouse S G
Rat C G
Cow S A
Pig A A
Horse V A
Platypus R A
Chicken I A
Zebrafish M A
Trout I A
Alpha 111 and beta 115 pack against each other in the crystal, and the alpha side reads seven letters across the rows.
9. Open the result from one link
The alignment, the tree and the GFF go behind URLs, and the layers fit in the link:
{
"msaview": {
"type": "MsaView",
"colWidth": 4.4,
"rowHeight": 22,
"colorSchemeName": "clustalx_protein_dynamic",
"msaFilehandle": { "uri": "https://example.org/hemoglobin.afa" },
"treeFilehandle": { "uri": "https://example.org/hemoglobin.nwk" },
"gffFilehandle": { "uri": "https://example.org/hemoglobin-subunits.gff" },
"showDomains": false
}
}Add the columnTracks and residueMappings entries from
hemoglobin-layers.json beside those, then URL-encode the object as #data=.

ggmsaview reads the hosted alignment, tree
and subunit GFF, and takes both layers from hemoglobin-layers.json.
features = FALSE leaves the GFF overlay out, as the link does:
library(ggmsaview)
data <- "https://gmod.org/JBrowseMSA/demo/data/hemoglobin/"
layers <- jsonlite::fromJSON(paste0(data, "hemoglobin-layers.json"),
simplifyVector = FALSE)
w <- msaview(
msa = paste0(data, "hemoglobin.afa"),
tree = paste0(data, "hemoglobin.nwk"),
gff = paste0(data, "hemoglobin-subunits.gff"),
color_scheme = "clustalx_protein_dynamic",
column_tracks = layers$columnTracks,
residue_mappings = layers$residueMappings
)
w
ggplot2::ggsave("protein-complex-final-r.png",
msaview_plot(w, tracks = c("2hhb-contacts", "2hhb-class", "conservation"),
features = FALSE),
width = 12, height = 6)
w and draw nothing
here.The script writes a residueMappings record per chain for the Human row, and
the two share an entry id, so a host looking a residue up the other way passes a
chain:
model.structureResidue('Human', 43) // 2HHB chain A, residue 42
model.structureResidue('Human', 242) // 2HHB chain B, residue 99
model.structureResidue('Human', 143) // undefined: the beta initiator, in no chain
model.rowResidue('2HHB', 42) // undefined: both chains have a residue 42
model.rowResidue('2HHB', 42, 'A') // Human, residue 43
Reproduce it end to end
build_protein_complex.sh
runs the steps above in order:
- It fetches the 22 proteins from NCBI and writes one FASTA per subunit.
- ClustalW aligns each subunit, the script concatenates the pair per species with a GFF marking each span, and ClustalW infers the tree.
- It finds the residue pairs within 4 Å across the two interfaces of PDB 2HHB, classes the other human residues, and writes the layers.
- It reports the conservation of each class and the letters at one contact pair per interface.
The script needs the tools under Prerequisites.
curl -O https://raw.githubusercontent.com/GMOD/JBrowseMSA/main/docs/tutorials/scripts/build_protein_complex.sh
bash build_protein_complex.sh
With no arguments the script builds all four files and prints every number on this page. Pass your own table for another complex and structure:
bash build_protein_complex.sh my-rows.tsv out/
See also
- Data layers
- The SARS-CoV-2 furin insert and PDB 6VXX
- An alignment linked to its structure
- A protein family from a list of accessions
- User guide
References
- Fermi G, Perutz MF, Shaanan B, Fourme R. The crystal structure of human deoxyhaemoglobin at 1.74 Å resolution. J Mol Biol 175:159-174.
- Perutz MF. Stereochemistry of cooperative effects in haemoglobin. Nature 228:726-739.
- Baldwin J, Chothia C. Haemoglobin: the structural changes related to ligand binding and its allosteric mechanism. J Mol Biol 129:175-220.
- Tien MZ, Meyer AG, Sydykova DK, Spielman SJ, Wilke CO. Maximum allowed solvent accessibilites of residues in proteins. PLoS ONE 8:e80635.
- Cock PJA, Antao T, Chang JT, et al. Biopython: freely available Python tools for computational molecular biology and bioinformatics. Bioinformatics 25:1422-1423.