Placed copies and assemblies
Draw a structure more than once, at different positions, and lay out a biological assembly.
Scene.place makes a copy of a structure's molecular source at a column-major 4×4
affine matrix. The copy is a structure of its own: it has its own identity, its own
representations, and its own picks, and the source it copies is not modified.
import molframe
import molgfx
structure = molframe.read("4hhb.cif").infer_bonds()
scene = molgfx.Scene(structure)
shift = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 25.0, 0, 0, 1]
copy = scene.place(shift)
scene.add(molgfx.rep.spacefill(target=molgfx.sel.all()).on(copy))A matrix that is not finite, not affine (bottom row 0 0 0 1), or not invertible is
rejected when it is placed rather than when it is drawn.
Superposition and the matrix convention
Scene.place reads its 16 floats column-major, while NumPy and MolFrame matrices are row-major. A row-major M must be passed as M.T.reshape(-1).tolist(). molgfx.place does that conversion, so a MolFrame superpose result (x' = R @ x + t) places a copy directly:
import numpy as np
rotation = np.array([[0.0, -1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]]) # 90 degrees about z
translation = np.array([10.0, 0.0, 0.0])
moved = scene.place(rotation=rotation, translation=translation) # no transposing by hand
scene.add(molgfx.rep.cartoon(target="protein").on(moved))
same = scene.place(molgfx.place.from_rotation_translation(rotation, translation)) # the same, as a matrix
# With a MolFrame fit: fit = molframe.geometry.superpose(mobile, reference)
# molgfx.place.apply(scene, fit) # or molgfx.place.from_superposition(fit)Many copies
Scene.place_many takes any number of matrices and adds them as one revision and one
patch. Its cost is linear in the number of copies and independent of the atom count: the
coordinates and atom tables are shared, and the content hash of the source is computed
once. Prefer it to a loop of place calls for a lattice or an ensemble.
row = [[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 40.0 * i, 0, 0, 1] for i in range(1, 5)]
copies = scene.place_many(row)Biological assemblies
Scene.assembly places one copy per assembly instance. Each instance carries a
matrix and the chains it applies to, which is what molframe.crystal.assembly
returns. This needs an entry that deposits assembly data, so the snippet is not run in the docs tests:
instances = molframe.crystal.assembly(structure, "1")
for copy in scene.assembly(instances):
scene.add(molgfx.rep.cartoon(target=copy.selection).on(copy.structure))Each returned AssemblyCopy names the placed structure, the chains, and a query that
selects exactly those chains. The identity transform comes back like any other, so the
copies replace the deposited asymmetric unit rather than add to it.
What is shared and what is not
In the browser viewer the encoded source crosses to the page once however many copies are placed; each copy names the structure that owns the bytes. The page still parses one source per copy.
Current limits
A picked atom in a placed copy resolves against that copy's own structure. Bonds are not generated between copies, and MolViewSpec export does not carry placements.