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.11 for an example of using symmetry restraints with the automodel class.
# 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 conjugate_gradients, molecular_dynamics 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 = energy_data(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 = conjugate_gradients() md = molecular_dynamics(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)