Solve atoms with alternate locations#

choose coordinates for atoms with alternate locations.

Structures solved experimentally and deposited in the Protein Data Bank can have atoms with multiple locations. MolSysMT includes a function in the ‘build’ module to solve this ambiguity: molsysmt.build.solve_atoms_with_alternate_location()

To illustrate how this function works without a network dependency, we use the bundled 1BNF PDB file:

import molsysmt as msm
from importlib.resources import files
pdb_filename = str(files('molsysmt.data.pdb').joinpath('1bnf.pdb'))
msm.get(pdb_filename, alternate_location=True)
[{'480': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['481', '482'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[4.17   4.9768 4.186 ]
    [4.1655 4.9841 4.1835]], 'nanometer')>,
   'b_factor': <Quantity([0.2098 0.2083], 'nanometer ** 2')>},
  '481': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['483', '484'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[4.0741 5.0097 4.2933]
    [4.0777 5.0154 4.2938]], 'nanometer')>,
   'b_factor': <Quantity([0.192  0.1881], 'nanometer ** 2')>},
  '484': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['487', '488'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[4.1393 5.1182 4.3785]
    [4.1245 5.1383 4.3719]], 'nanometer')>,
   'b_factor': <Quantity([0.1883 0.1695], 'nanometer ** 2')>},
  '485': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['489', '490'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[4.2094 5.0309 4.5225]
    [4.0131 5.1802 4.5145]], 'nanometer')>,
   'b_factor': <Quantity([0.208  0.1601], 'nanometer ** 2')>},
  '667': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['672', '673'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[3.7765 5.0724 4.6054]
    [3.7766 5.0719 4.6078]], 'nanometer')>,
   'b_factor': <Quantity([0.1363 0.1315], 'nanometer ** 2')>},
  '668': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['674', '675'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[3.8278 4.9821 4.7071]
    [3.8143 4.9739 4.7107]], 'nanometer')>,
   'b_factor': <Quantity([0.1599 0.1496], 'nanometer ** 2')>},
  '671': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['678', '679'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[3.9783 4.961  4.7073]
    [3.9568 4.9253 4.684 ]], 'nanometer')>,
   'b_factor': <Quantity([0.1725 0.1131], 'nanometer ** 2')>},
  '672': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['680', '681'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[4.069  4.8885 4.5735]
    [4.0819 5.0633 4.6676]], 'nanometer')>,
   'b_factor': <Quantity([0.1946 0.0901], 'nanometer ** 2')>},
  '1321': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['1331', '1332'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[0.9125 4.2321 5.9573]
    [0.9075 4.2307 5.9649]], 'nanometer')>,
   'b_factor': <Quantity([0.213  0.2113], 'nanometer ** 2')>},
  '1322': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['1333', '1334'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[0.872  4.1452 5.8457]
    [0.8799 4.1455 5.8466]], 'nanometer')>,
   'b_factor': <Quantity([0.188  0.1842], 'nanometer ** 2')>},
  '1325': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['1337', '1338'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[0.9935 4.0891 5.7702]
    [0.9994 4.0777 5.7795]], 'nanometer')>,
   'b_factor': <Quantity([0.1785 0.1631], 'nanometer ** 2')>},
  '1326': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['1339', '1340'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[1.0017 4.1857 5.6156]
    [0.9446 3.9733 5.6322]], 'nanometer')>,
   'b_factor': <Quantity([0.1974 0.1569], 'nanometer ** 2')>},
  '1508': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['1522', '1523'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[0.6893 3.9232 5.4976]
    [0.702  3.923  5.4848]], 'nanometer')>,
   'b_factor': <Quantity([0.1272 0.1251], 'nanometer ** 2')>},
  '1509': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['1524', '1525'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[0.6846 4.0198 5.3871]
    [0.6967 4.0131 5.3882]], 'nanometer')>,
   'b_factor': <Quantity([0.1461 0.1419], 'nanometer ** 2')>},
  '1512': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['1528', '1529'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[0.7897 4.1325 5.3962]
    [0.7808 4.1361 5.4235]], 'nanometer')>,
   'b_factor': <Quantity([0.1578 0.1351], 'nanometer ** 2')>},
  '1513': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['1530', '1531'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[0.8076 4.2142 5.5529]
    [0.9586 4.0946 5.4672]], 'nanometer')>,
   'b_factor': <Quantity([0.1718 0.1306], 'nanometer ** 2')>},
  '2160': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['2179', '2180'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[3.5634 2.7405 4.1508]
    [3.5631 2.741  4.15  ]], 'nanometer')>,
   'b_factor': <Quantity([0.1851 0.1816], 'nanometer ** 2')>},
  '2161': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['2181', '2182'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[3.5489 2.6375 4.0455]
    [3.5544 2.6366 4.0465]], 'nanometer')>,
   'b_factor': <Quantity([0.1733 0.1652], 'nanometer ** 2')>},
  '2164': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['2185', '2186'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[3.678  2.6321 3.9619]
    [3.694  2.619  3.9829]], 'nanometer')>,
   'b_factor': <Quantity([0.1846 0.1419], 'nanometer ** 2')>},
  '2165': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['2187', '2188'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[3.6536 2.7408 3.8168]
    [3.6905 2.5091 3.8362]], 'nanometer')>,
   'b_factor': <Quantity([0.2073 0.1173], 'nanometer ** 2')>},
  '2347': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['2370', '2371'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[3.4344 2.3674 3.7288]
    [3.4327 2.3679 3.7296]], 'nanometer')>,
   'b_factor': <Quantity([0.1352 0.1325], 'nanometer ** 2')>},
  '2348': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['2372', '2373'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[3.3995 2.461  3.6228]
    [3.3923 2.4588 3.621 ]], 'nanometer')>,
   'b_factor': <Quantity([0.1436 0.1376], 'nanometer ** 2')>},
  '2351': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['2376', '2377'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[3.4761 2.5935 3.6218]
    [3.4448 2.601  3.642 ]], 'nanometer')>,
   'b_factor': <Quantity([0.1631 0.1302], 'nanometer ** 2')>},
  '2352': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['2378', '2379'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[3.4675 2.7162 3.7469]
    [3.6226 2.6063 3.6713]], 'nanometer')>,
   'b_factor': <Quantity([0.2009 0.1519], 'nanometer ** 2')>}}]

MolSysMT returns the info about alternate locations as a list of dictionaries where the keys are the indices of the atoms with more than an atom_id, occupancy, b_factor and coordinates (stored in a dictionary in the corresponding values).

The output identifies every canonical atom site with alternate records. Let’s load the system as a molsysmt.MolSys object to work with that information:

molecular_system = msm.convert(pdb_filename, to_form='molsysmt.MolSys', get_missing_bonds=False)

The resulting object still keeps the information about the alternate locations:

msm.get(molecular_system, element='atom', selection='atom_index==480', alternate_location=True)
[{'480': {'location_id': array(['A', 'B'], dtype=object),
   'atom_id': ['481', '482'],
   'occupancy': array([0.5, 0.5]),
   'coordinates': <Quantity([[4.17   4.9768 4.186 ]
    [4.1655 4.9841 4.1835]], 'nanometer')>,
   'b_factor': <Quantity([0.2098 0.2083], 'nanometer ** 2')>}}]

A PDB conversion keeps the first listed variant as the active coordinates while retaining every variant in Structures.alternate_location. Calling the resolver with location_id='occupancy' chooses the highest-occupancy variant independently in every selected structure. When occupancies tie at 0.5, location A is preferred when available.

msm.get(molecular_system, element='atom', selection=480, coordinates=True)
Magnitude
[[[4.17 4.9768 4.186]]]
Unitsnanometer

How can we choose a different location for a specific atom? molsysmt.build.solve_atoms_with_alternate_location() can help to do it. Let’s for instance change all atoms to alternate location “B” to show how this function works:

msm.build.solve_atoms_with_alternate_location(molecular_system, location_id='B')
msm.get(molecular_system, element='atom', selection=480, atom_id=True, coordinates=True)
[['482'], <Quantity([[[4.1655 4.9841 4.1835]]], 'nanometer')>]

The function molsysmt.build.solve_atoms_with_alternate_location() accepts the input argument ‘selection’ in case different location ids need to be provided for different atoms:

msm.build.solve_atoms_with_alternate_location(molecular_system, selection=[480,481], location_id=['A','B'])
msm.get(molecular_system, element='atom', selection=[480,481], atom_id=True, coordinates=True)
[['481', '484'],
 <Quantity([[[4.17   4.9768 4.186 ]
   [4.0777 5.0154 4.2938]]], 'nanometer')>]

The input argument ‘location_id’ accepts an extra value: ‘occupancy’. With ‘occupancy’ each atom takes the location with highest occupancy, or the location id equal to ‘A’ in case all occupancy values are equal.

msm.build.solve_atoms_with_alternate_location(molecular_system, selection=[480,481], location_id='occupancy')
msm.get(molecular_system, element='atom', selection=[480,481], atom_id=True, coordinates=True)
[['481', '483'],
 <Quantity([[[4.17   4.9768 4.186 ]
   [4.0741 5.0097 4.2933]]], 'nanometer')>]

Warning

Native PDB file, PDB text, and molsysmt.PDBFileHandler conversions preserve alternate-location variants. They represent each atom site once in topology and keep the variant atom IDs, occupancies, B factors, and coordinates in the structures layer. Forms that do not declare the alternate_location attribute can still lose this metadata; inspect a conversion report when crossing such a boundary.