##
Specification of pseudo atoms

There are virtual and pseudo atoms. A virtual atom is an atom that occurs in the actual molecule, but whose position is not represented explicitly in the MODEL and topology file. A pseudo atom is a position that does not correspond to an actual atom in a molecule, but is some sort of an average of positions of real atoms. Pseudo atoms can be added to the list of restraints by adding the objects below to the Restraints.pseudo_atoms list. Atom ids are as for features, above. The MODELLER pseudo and virtual atom types follow closely the GROMOS definitions.

`pseudo_atom.gravity_center(*atom_ids)`
Gravity center of all of the supplied atoms.

`pseudo_atom.ch2(*atom_ids)`
Pseudo aliphatic proton on a tetrahedral carbon ( CH2). Not assigned stereospecifically; its position is between the two real protons; defined by the central C and the other two substituents (specified by atom_ids).

`pseudo_atom.ch31(*atom_ids)`
Pseudo aliphatic proton on a tetrahedral carbon (-CH3), defined by the central C and the heavy atom X in X-CH3 (specified by atom_ids); its position is the average of the three real protons.

`pseudo_atom.ch32(*atom_ids)`
Pseudo aliphatic proton between two unassigned -CH3 groups; defined by X in CH3 - X - CH3 and the two C atoms from the two CH3 groups (specified by atom_ids). Its position is the average of the six real protons.

`virtual_atom.ch1(*atom_ids)`
Virtual aliphatic proton on a tetrahedral carbon (- CH), defined by the central C and the three other substituents (specified by atom_ids).

`virtual_atom.ch1a(*atom_ids)`
Virtual aromatic proton on a trigonal carbon (=CH), defined by the central C and the two C atoms bonded to the central C (specified by atom_ids).

`virtual_atom.ch2(*atom_ids)`
Virtual aliphatic proton on a tetrahedral carbon ( CH2) assigned stereospecifically; defined by the central tetrahedral atom and the other two substituents on it (specified by atom_ids).

Example: examples/python/pseudo_atoms.py

      from modeller import *
      from modeller.scripts import complete_pdb
      from modeller.optimizers import conjugate_gradients

      env = environ()

      env.io.atom_files_directory = ['../atom_files']
      log.verbose()
      env.libs.topology.read(file='$(LIB)/top_heav.lib')
      env.libs.parameters.read(file='$(LIB)/par.lib')

      # Read in the model
      mdl = complete_pdb(env, "1fdn")
      rsr = mdl.restraints

      # Select all C-alpha atoms
      allat = selection(mdl)
      allca = allat.only_atom_types('CA')

      # Create a pseudo atom that is the center of all C-alphas, and activate it
      center = pseudo_atom.gravity_center(allca)
      rsr.pseudo_atoms.append(center)

      # Constrain every C-alpha to be no more than 10 angstroms from the center
      for at in allca:
          r = forms.upper_bound(group=physical.xy_distance,
                                feature=features.distance(at, center),
                                mean=10.0, stdev=0.1)
          rsr.add(r)

      # Constrain the gravity center to the x=0 plane
      r = forms.gaussian(group=physical.xy_distance,
                         feature=features.x_coordinate(center),
                         mean=0.0, stdev=0.1)
      rsr.add(r)

      # Keep sensible stereochemistry
      rsr.make(allat, restraint_type='stereo', spline_on_site=False)

      # Optimize with CG
      cg = conjugate_gradients()
      cg.optimize(allat, max_iterations=100, output='REPORT')
      mdl.write(file='1fas.ini')