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]]] |
|---|---|
| Units | nanometer |
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.