1. G-quadruplex Assembly¶
This tutorial is where the general ideas from the user guide are pushed into a
realistic object hierarchy. It is not a separate conceptual layer. It is a
worked example that shows why pressomancy builds from top-level objects,
propagates construction downward, and keeps ownership and connectivity in the
IO output rather than only particle coordinates. On the current branch, the
core build path is
Quartet ->
Quadriplex ->
Filament or
TelSeq.
The simulation never places quartets directly into the global box. It places a
top-level object and lets that object recursively build the lower levels
beneath it. The two sample workflows on main make that design concrete: a
poly-G4 filament built from solid quartets, and a folded telomeric sequence
built from broken quartets with additional top-level fold logic.
1.1. What Gets Built¶
In samples/poly_BRACO.py, quartets are the rigid building blocks. Three
quartets are grouped into one
Quadriplex, and several
quadriplexes are grouped into one
Filament. The filament is
therefore the object the simulation actually places in the box. Once
set_objects() assigns positions and
orientations to the filaments, each filament places its quadriplex children,
and each quadriplex places and bonds its quartet children.
In samples/folded_tel_sequence.py, the lower part of the hierarchy stays
recognizable, but the top-level container changes.
TelSeq replaces the plain
filament and adds fold-specific post-placement logic. Quartets and
quadriplexes are still built in the same general top-down style, but the final
local couplings depend on the chosen telomeric fold type.
1.2. Solid Quartets¶
The solid workflow begins by constructing quartets in type='solid' mode,
storing them, and grouping them three at a time into quadriplexes. This is the
first point where hierarchy really matters.
Quadriplex expects
exactly three associated quartets. Its
set_object()
method then places those quartets at pos, pos + r_0 * ori, and
pos - r_0 * ori before applying the selected bonding mode.
Once quadriplexes have been prepared, they are grouped into filaments. The
Filament configuration
uses n_parts and spacing to define the local chain geometry that its
build_function should generate when the simulation assigns a top-level
volume. At that point, one call to set_objects(filaments) is enough to
materialize the whole poly-G4 assembly recursively.
That is the key modeling shift. The script does not manually calculate global quartet coordinates or micromanage each quadriplex placement. It prepares the hierarchy, registers it with the simulation, and lets the top-level object class drive construction downward.
part_per_filament = 8
n_filaments = 4
n_quartets = 3 * part_per_filament * n_filaments
quartet_cfg = Quartet.config.specify(
espresso_handle=sim.sys,
type='solid',
)
quartets = [Quartet(config=quartet_cfg) for _ in range(n_quartets)]
sim.store_objects(quartets)
quadriplex_bond = BondWrapper(
espressomd.interactions.FeneBond(k=10., r_0=2., d_r_max=2 * 1.5)
)
filament_bond = BondWrapper(
espressomd.interactions.FeneBond(k=10., r_0=2., d_r_max=2 * 1.5)
)
grouped_quartets = [quartets[i:i + 3] for i in range(0, len(quartets), 3)]
quadriplex_cfgs = [
Quadriplex.config.specify(
size=np.sqrt(3) * 5.,
espresso_handle=sim.sys,
bond_handle=quadriplex_bond,
associated_objects=group,
)
for group in grouped_quartets
]
quadriplexes = [Quadriplex(config=cfg) for cfg in quadriplex_cfgs]
sim.store_objects(quadriplexes)
grouped_quadriplexes = [
quadriplexes[i:i + part_per_filament]
for i in range(0, len(quadriplexes), part_per_filament)
]
filament_cfgs = [
Filament.config.specify(
size=quadriplexes[0].params['size'] * part_per_filament
+ np.sqrt(3) * filament_bond.r_0 + (part_per_filament - 1),
n_parts=part_per_filament,
espresso_handle=sim.sys,
bond_handle=filament_bond,
associated_objects=group,
spacing=6.,
)
for group in grouped_quadriplexes
]
filaments = [Filament(config=cfg) for cfg in filament_cfgs]
sim.store_objects(filaments)
sim.set_objects(filaments)
for filament in filaments:
filament.bond_quadriplexes()
1.3. Broken Quartets And Telomeres¶
The folded telomeric example starts from the same quartet -> quadriplex logic,
but it changes both the quartet chemistry and the top-level object. Quartets
are created in type='broken' mode, which requires an explicit
bond_handle and changes the internal typing pattern used during quartet
construction. In broken mode, selected particles are promoted or reassigned to
roles such as real, circ, squareA, and squareB so that the
later fold logic has the expected local topology available.
Those broken quartets are still grouped into quadriplexes. The real change
comes one level higher, where the top-level object becomes
TelSeq. Like
Filament, it defines a
chain-like build_function so the simulation can place complete top-level
units in the box. After placement,
wrap_into_Tel()
interprets the requested fold type and adds the corresponding diagonal and
across bonds between corners.
The consequence is important. The folded example does not need a new global placement algorithm. It needs a different top-level object whose local post-placement logic is more specific.
part_per_filament = 8
fold_types = ['parallel', 'hybrid', 'antiparallel']
n_telomeres = len(fold_types)
n_quartets = 3 * part_per_filament * n_telomeres
quartet_bond = BondWrapper(espressomd.interactions.FeneBond(k=10., r_0=1., d_r_max=1.5))
quadriplex_bond = BondWrapper(espressomd.interactions.FeneBond(k=10., r_0=2., d_r_max=3.0))
diag_bond = BondWrapper(espressomd.interactions.FeneBond(k=10., r_0=1.0, d_r_max=1.5))
across_bond = BondWrapper(espressomd.interactions.FeneBond(k=10., r_0=1.0, d_r_max=1.5))
quartet_cfg = Quartet.config.specify(
bond_handle=quartet_bond,
type='broken',
espresso_handle=sim.sys,
)
quartets = [Quartet(config=quartet_cfg) for _ in range(n_quartets)]
sim.store_objects(quartets)
grouped_quartets = [quartets[i:i + 3] for i in range(0, len(quartets), 3)]
quadriplex_cfgs = [
Quadriplex.config.specify(
associated_objects=group,
espresso_handle=sim.sys,
bonding_mode='ftf',
bond_handle=quadriplex_bond,
size=np.sqrt(3) * 5,
)
for group in grouped_quartets
]
quadriplexes = [Quadriplex(config=cfg) for cfg in quadriplex_cfgs]
sim.store_objects(quadriplexes)
grouped_quadriplexes = [
quadriplexes[i:i + part_per_filament]
for i in range(0, len(quadriplexes), part_per_filament)
]
telseq_cfgs = [
TelSeq.config.specify(
n_parts=part_per_filament,
espresso_handle=sim.sys,
associated_objects=grouped_quadriplexes[idx],
size=quadriplexes[0].params['size'] * part_per_filament
+ np.sqrt(3) * quadriplex_bond.r_0 + (part_per_filament - 1),
bond_handle=quadriplex_bond,
diag_bond_handle=diag_bond,
across_bond_handle=across_bond,
spacing=6.,
type=fold_type,
)
for idx, fold_type in enumerate(fold_types)
]
telomeres = [TelSeq(config=cfg) for cfg in telseq_cfgs]
sim.store_objects(telomeres)
sim.set_objects(telomeres)
for telomere in telomeres:
telomere.wrap_into_Tel()
1.4. Key Options¶
Quartet
The first consequential choice is the quartet type. solid gives a
rigid-body style quartet with one real center and virtual sites. broken
gives a more chemically differentiated internal layout and is the mode used by
the telomeric sample. Broken quartets require bond_handle to be set at
construction time, so the choice is structural rather than cosmetic.
Quadriplex
For quadriplexes, the key option is bonding_mode. ftf is the default
and bonds compatible corner particles across quartets. ctc instead bonds
the central quartet to the top and bottom quartet through their main real
particles. This choice changes the bonded architecture and also affects later
mechanics such as
add_bending_potential().
Filament
For filaments, the practical parameters are n_parts, spacing, and
size. n_parts must match the number of associated quadriplexes in a
composite filament. spacing controls the chain geometry produced by the
filament build_function. size is what the simulation sees when it
decides whether whole filaments can be placed without overlap.
TelSeq
TelSeq adds the top-level
fold type. On the current branch, the supported values are parallel,
hybrid, and antiparallel. These do not alter the global placement
stage. They alter the local post-placement bond pattern applied by
wrap_into_Tel().
1.5. Common Mistakes¶
The most common failure in the solid workflow is a grouping mismatch.
Quadriplex construction assumes groups of exactly three quartets, and a
composite filament assumes n_parts == len(associated_objects). If the last
group in a sequence is shorter than expected, the visible failure often occurs
well after the original mistake, so it is worth validating group lengths
before configuration objects are created.
The second recurring pitfall is to conflate placement with higher-level
bonding. In these examples they are intentionally separate steps.
set_objects creates the hierarchy and gives every object a spatial context.
Methods such as
bond_quadriplexes()
and wrap_into_Tel()
then add the larger-scale couplings that depend on the already created
geometry.
Finally, ctc and ftf should be treated as genuinely different modeling
choices. They produce different bonded structures, and downstream mechanics
should be written with that choice in mind rather than assuming the two modes
are interchangeable.