Figure gallery
==============
Every panel below is one call to :func:`tcren.viz.pymol.render` on a scene from the same module,
ray-traced by a headless PyMOL. They are library functions rather than notebook snippets, so a
figure in a paper, in a notebook and in a script are the same figure.
.. code-block:: bash
pip install "tcren[viz]" # matplotlib + py3Dmol; PyMOL itself is a separate install
tcren fetch-data # populates data/Canonical2026
Reading the axis gizmo
----------------------
Every panel carries a thin, arrow-headed triad in its bottom-left corner. A canonically-oriented
structure is not interpretable unless the reader can tell which way the frame points, and ``x/y/z``
does not tell them, so the arrows are named for what they mean:
.. list-table::
:header-rows: 1
:widths: 8 14 78
* - axis
- label
- direction
* - ``x``
- ``width``
- groove width, across the cleft (α1↔α2)
* - ``y``
- ``N→C``
- groove axis, signed toward the peptide C-terminus
* - ``z``
- ``TCR``
- docking normal, MHC floor → TCR
The triad turns with the camera. An axis pointing at the viewer foreshortens to a dot, and its
label drops to the lower left of that dot — the usual convention for an axis normal to the page.
So in a top-down view ``TCR`` sits at the origin, and in a side-on view ``N→C`` does.
These are the three directions the docking-geometry literature uses; the principal-component
*ranking* differs from it because :mod:`tcren.orient.frame` fits the whole complex where the
groove-only conventions fit the MHC alone. :data:`tcren.viz.pymol.CANONICAL_AXES` carries the
names, the definitions and the correspondence.
The peptide in the groove
-------------------------
Looking down the docking normal, from where the TCR sits: the peptide as sticks in the cleft, the
MHC coloured by domain — helices salmon, β-sheet floor cyan.
.. image:: _static/gallery/groove.png
:width: 60%
:align: center
:alt: Peptide as sticks in the MHC groove, viewed from above
.. code-block:: python
from tcren.viz.pymol import render, groove_scene
render(groove_scene("1ao7", "data/Canonical2026"), "groove.png")
With ``surface=True`` the MHC gains a translucent molecular surface, which is how `histo.fyi
`_ presents a structure — the cleft reads as a cleft rather than as ribbon.
.. image:: _static/gallery/surface.png
:width: 60%
:align: center
:alt: The same view with a translucent molecular surface over the MHC
.. code-block:: python
render(groove_scene("1ao7", "data/Canonical2026", surface=True), "surface.png")
Which residues carry the score
------------------------------
The interface energy Φ is a sum over residue–residue contacts, so it decomposes exactly: each
residue's share is the sum of ``φ(a_i, a_j)`` over the contacts it makes across the interface. That
is the quantity worth colouring by — the total says how large the score is, the decomposition says
what it is *made of*.
:func:`tcren.viz.pymol.residue_importance` computes it and
:func:`tcren.viz.pymol.importance_scene` paints it: CDR3 and peptide residues as sticks on a ramp,
everything else pale.
.. image:: _static/gallery/importance.png
:width: 60%
:align: center
:alt: CDR3 and peptide residues coloured by their share of the interface energy
.. code-block:: python
from tcren.viz.pymol import render, residue_importance, importance_scene
imp = residue_importance(structure) # per-residue phi and contact count
render(importance_scene("1ao7", "data/Canonical2026", imp), "importance.png")
**Blue is favourable, red unfavourable.** The ramp is centred on zero rather than fitted to the
observed range, so the colours keep that meaning: a range-fitted ramp would paint the
least-favourable residue red even in an interface where every contact is stabilising.
The same call colours by the *geometric* share instead — how much of the interface a residue
physically occupies, rather than how much energy it contributes. A residue can be large on one and
small on the other, and that difference is usually the interesting part.
.. image:: _static/gallery/contacts.png
:width: 60%
:align: center
:alt: The same residues coloured by contact count
.. code-block:: python
render(importance_scene("1ao7", "data/Canonical2026", imp,
by="n_contacts", spectrum="white_red"), "contacts.png")
.. note::
Each contact is attributed to **both** residues it joins, so the per-residue values sum to
twice Φ. It is an attribution, not a partition — the energy of a contact belongs to the pair,
not to either partner.
The recognition interface
-------------------------
Peptide plus the CDR1–3 loops that reach it, over a pale MHC, seen side-on — the plane the
crossing and incident angles live in.
.. image:: _static/gallery/interface.png
:width: 60%
:align: center
:alt: Peptide and CDR loops over the MHC, side-on
.. code-block:: python
from tcren.viz.pymol import render, interface_scene
render(interface_scene("1ao7", "data/Canonical2026", cdr_resi), "interface.png")
An ensemble in one frame
------------------------
Because orientation puts every structure in the same frame, an overlay is meaningful: the MHC
superposes and the spread you see is real variation in how the receptors dock.
.. image:: _static/gallery/overlay.png
:width: 60%
:align: center
:alt: Eight oriented complexes superposed
.. code-block:: python
from tcren.viz.pymol import render, overlay_scene
render(overlay_scene(pdb_ids, "data/Canonical2026"), "overlay.png")
Exploring interactively
-----------------------
Two notebooks in ``notebooks/`` drive all of the above:
``pymol_canonical_figures.ipynb``
The static gallery — every view family over the four MHC class × species groups.
``pymol_interactive.py``
A `marimo `_ app: pick a structure and a scene, swing the camera and watch
the gizmo follow, restyle it, and rotate a live 3Dmol.js view with the mouse.
.. code-block:: bash
pip install marimo
marimo run notebooks/pymol_interactive.py # or `marimo edit` to change the code
For a viewer inside a Jupyter notebook without leaving Python,
:func:`tcren.viz.view_pocket_cdr` returns a ``py3Dmol`` view of the same oriented groove.