Symmetry restraints

You can restrain two groups of atoms to be the same during optimization of the objective function. This is achieved by adding the sum of squares of the differences between the equivalent distances (similar to distance RMS deviation) to the objective function being optimized. See Equation A.99.

After creating a Symmetry object, you can call its append function to add additional pairs of groups. This allows some equivalent atoms to be weighted more strongly than others. Finally, add the Symmetry object to the Restraints.symmetry list.

Symmetry(set1, set2, weight)
Creates a new symmetry restraint which will constrain the interatomic distances in set1 to be the same as in set2. (The append function takes the same parameters.) Both sets are just lists of atoms or objects which contain atoms, such as Residue or Selection objects. Note that each set must contain the same number of atoms. Note also that the order is important. (If using Selection objects, the atoms are always sorted in the same order as seen in the PDB file.)

See Section 2.2.12 for an example of using symmetry restraints with the AutoModel class.

Example: examples/commands/define_symmetry.py

# Example for: Model.symmetry.define()

# This will force two copies of 1fas to have similar mainchain
# conformation.

from modeller import *
from modeller.scripts import complete_pdb
from modeller.optimizers import ConjugateGradients, MolecularDynamics

log.level(1, 1, 1, 1, 0)
env = Environ()
env.io.atom_files_directory = ['../atom_files']
env.libs.topology.read(file='$(LIB)/top_heav.lib')
env.libs.parameters.read(file='$(LIB)/par.lib')

def defsym(mdl, seg1, seg2):
    sel1 = Selection(mdl.residue_range(*seg1)).only_mainchain()
    sel2 = Selection(mdl.residue_range(*seg2)).only_mainchain()
    mdl.restraints.symmetry.append(Symmetry(sel1, sel2, 1.0))

# Generate two copies of a segment:
mdl = complete_pdb(env, '2abx', model_segment=('1:A', '74:B'))
mdl.rename_segments(segment_ids=('A', 'B'), renumber_residues=(1, 1))

myedat = EnergyData(dynamic_sphere = False)
atmsel = Selection(mdl)
atmsel.energy(edat=myedat)
atmsel.randomize_xyz(deviation=6.0)
# Define the two segments (chains in this case) to be identical:
defsym(mdl, seg1=('1:A', '74:A'), seg2=('1:B', '74:B'))

# Create optimizer objects
cg = ConjugateGradients()
md = MolecularDynamics(md_return='FINAL')

# Make them identical by optimizing the initial randomized structure
# without any other restraints:
atmsel.energy(edat=myedat)
mdl.write(file='define_symmetry-1.atm')
cg.optimize(atmsel, max_iterations=300, edat=myedat)
mdl.write(file='define_symmetry-2.atm')
atmsel.energy(edat=myedat)

# Now optimize with stereochemical restraints so that the
# result is not so distorted a structure (still distorted
# because optimization is not thorough):
myedat.dynamic_sphere = True
mdl.restraints.make(atmsel, restraint_type='stereo', spline_on_site=False,
                    edat=myedat)
atmsel.randomize_xyz(deviation=3.0)
for method in (cg, md, cg):
    method.optimize(atmsel, max_iterations=300, edat=myedat, output='REPORT')
mdl.write(file='define_symmetry-3.atm')
atmsel.energy(edat=myedat)

# Report on symmetry violations
mdl.restraints.symmetry.report(0.3)

# Create a blank alignment so that superpose uses its 1:1 default
aln = Alignment(env)

mdl = Model(env, file='define_symmetry-3.atm', model_segment=('1:A', '74:A'))
mdl2 = Model(env, file='define_symmetry-3.atm', model_segment=('1:B', '74:B'))
atmsel = Selection(mdl).only_mainchain()
atmsel.superpose(mdl2, aln)