Conformer Ensembles

The ConformerEnsemble takes everything available in Molecule and extends this to many different conformers of a molecule.

[81]:
# Imports molli
import molli as ml

# Imports numpy
import numpy as np

#Loads Conformer Ensemble
ens = ml.ConformerEnsemble.load_mol2(ml.files.pentane_confs_mol2)
print(ens)
ConformerEnsemble(name='pentane', formula='C5 H12', n_conformers=7)

Conformer ensembles are made up of individual molecules referred to as conformers. These can also be iterated and sliced as if it was a list.

[82]:
#Iterates through each conformer
for i, conformer in enumerate(ens):
    print(conformer)
Conformer(name='pentane', conf_id=0)
Conformer(name='pentane', conf_id=1)
Conformer(name='pentane', conf_id=2)
Conformer(name='pentane', conf_id=3)
Conformer(name='pentane', conf_id=4)
Conformer(name='pentane', conf_id=5)
Conformer(name='pentane', conf_id=6)
[83]:
#Prints the third conformer in the ensemble
print(ens[2])
Conformer(name='pentane', conf_id=2)

Key Properties of Conformer Ensemble

coords represent all coordinates of all conformers, and are shaped by (n_conformers, n_atoms, n_dimensions)

[84]:
#Prints the shape of the ensemble coordinates
np.shape(ens.coords)

print(ens.coords)
[[[-2.8045e+00  3.9964e+00 -1.4128e+00]
  [-2.7484e+00  3.3174e+00 -5.3600e-02]
  [-3.6840e+00  4.6446e+00 -1.4767e+00]
  [-2.8678e+00  3.2576e+00 -2.2181e+00]
  [-1.9154e+00  4.6126e+00 -1.5806e+00]
  [-1.5288e+00  2.4040e+00  6.6300e-02]
  [-3.6655e+00  2.7359e+00  9.5900e-02]
  [-2.7185e+00  4.0835e+00  7.2990e-01]
  [-2.2860e-01  3.1846e+00 -1.2460e-01]
  [-1.5920e+00  1.6061e+00 -6.8380e-01]
  [-1.5265e+00  1.9213e+00  1.0512e+00]
  [-8.9200e-02  4.2949e+00  9.0460e-01]
  [-2.0010e-01  3.6206e+00 -1.1300e+00]
  [ 6.2870e-01  2.5066e+00 -4.0400e-02]
  [-9.1500e-01  5.0091e+00  8.2550e-01]
  [ 8.4720e-01  4.8399e+00  7.4960e-01]
  [-8.1700e-02  3.8889e+00  1.9212e+00]]

 [[-2.7298e+00  4.4129e+00  1.0005e+00]
  [-2.7484e+00  3.3174e+00 -5.3600e-02]
  [-3.6107e+00  5.0541e+00  8.9640e-01]
  [-1.8387e+00  5.0405e+00  8.9840e-01]
  [-2.7368e+00  3.9871e+00  2.0090e+00]
  [-1.5288e+00  2.4040e+00  6.6300e-02]
  [-2.7735e+00  3.7765e+00 -1.0488e+00]
  [-3.6668e+00  2.7289e+00  5.5600e-02]
  [-2.2860e-01  3.1846e+00 -1.2460e-01]
  [-1.5920e+00  1.6061e+00 -6.8380e-01]
  [-1.5265e+00  1.9213e+00  1.0512e+00]
  [-1.6270e-01  3.8294e+00 -1.4999e+00]
  [ 6.2990e-01  2.5144e+00 -3.0000e-04]
  [-1.4590e-01  3.9636e+00  6.4210e-01]
  [-2.1040e-01  3.0736e+00 -2.2903e+00]
  [ 7.7480e-01  4.3823e+00 -1.6147e+00]
  [-9.9070e-01  4.5309e+00 -1.6443e+00]]

 [[-4.0432e+00  2.5483e+00  1.5460e-01]
  [-2.7484e+00  3.3174e+00 -5.3600e-02]
  [-4.9021e+00  3.2207e+00  6.4500e-02]
  [-4.0725e+00  2.0912e+00  1.1488e+00]
  [-4.1528e+00  1.7552e+00 -5.9200e-01]
  [-1.5288e+00  2.4040e+00  6.6300e-02]
  [-2.6852e+00  4.1224e+00  6.8780e-01]
  [-2.7652e+00  3.7884e+00 -1.0434e+00]
  [-2.2860e-01  3.1846e+00 -1.2460e-01]
  [-1.5920e+00  1.6061e+00 -6.8380e-01]
  [-1.5265e+00  1.9213e+00  1.0512e+00]
  [ 9.8660e-01  2.2767e+00 -2.1500e-02]
  [-1.5320e-01  3.9735e+00  6.3270e-01]
  [-2.2900e-01  3.6748e+00 -1.1051e+00]
  [ 1.0344e+00  1.7952e+00  9.6040e-01]
  [ 1.9050e+00  2.8554e+00 -1.6120e-01]
  [ 9.5810e-01  1.4947e+00 -7.8710e-01]]

 [[-4.0432e+00  2.5483e+00  1.5460e-01]
  [-2.7484e+00  3.3174e+00 -5.3600e-02]
  [-4.9021e+00  3.2207e+00  6.4500e-02]
  [-4.0725e+00  2.0912e+00  1.1488e+00]
  [-4.1528e+00  1.7552e+00 -5.9200e-01]
  [-1.5288e+00  2.4040e+00  6.6300e-02]
  [-2.6852e+00  4.1224e+00  6.8780e-01]
  [-2.7652e+00  3.7884e+00 -1.0434e+00]
  [-2.2860e-01  3.1846e+00 -1.2460e-01]
  [-1.5920e+00  1.6061e+00 -6.8380e-01]
  [-1.5265e+00  1.9213e+00  1.0512e+00]
  [-8.9200e-02  4.2949e+00  9.0460e-01]
  [-2.0010e-01  3.6206e+00 -1.1300e+00]
  [ 6.2870e-01  2.5066e+00 -4.0400e-02]
  [-9.1500e-01  5.0091e+00  8.2550e-01]
  [ 8.4720e-01  4.8399e+00  7.4960e-01]
  [-8.1700e-02  3.8889e+00  1.9212e+00]]

 [[-4.0432e+00  2.5483e+00  1.5460e-01]
  [-2.7484e+00  3.3174e+00 -5.3600e-02]
  [-4.9021e+00  3.2207e+00  6.4500e-02]
  [-4.0725e+00  2.0912e+00  1.1488e+00]
  [-4.1528e+00  1.7552e+00 -5.9200e-01]
  [-1.5288e+00  2.4040e+00  6.6300e-02]
  [-2.6852e+00  4.1224e+00  6.8780e-01]
  [-2.7652e+00  3.7884e+00 -1.0434e+00]
  [-2.2860e-01  3.1846e+00 -1.2460e-01]
  [-1.5920e+00  1.6061e+00 -6.8380e-01]
  [-1.5265e+00  1.9213e+00  1.0512e+00]
  [-1.6270e-01  3.8294e+00 -1.4999e+00]
  [ 6.2990e-01  2.5144e+00 -3.0000e-04]
  [-1.4590e-01  3.9636e+00  6.4210e-01]
  [-2.1040e-01  3.0736e+00 -2.2903e+00]
  [ 7.7480e-01  4.3823e+00 -1.6147e+00]
  [-9.9070e-01  4.5309e+00 -1.6443e+00]]

 [[-2.8045e+00  3.9964e+00 -1.4128e+00]
  [-2.7484e+00  3.3174e+00 -5.3600e-02]
  [-3.6840e+00  4.6446e+00 -1.4767e+00]
  [-2.8678e+00  3.2576e+00 -2.2181e+00]
  [-1.9154e+00  4.6126e+00 -1.5806e+00]
  [-1.5288e+00  2.4040e+00  6.6300e-02]
  [-3.6655e+00  2.7359e+00  9.5900e-02]
  [-2.7185e+00  4.0835e+00  7.2990e-01]
  [-2.2860e-01  3.1846e+00 -1.2460e-01]
  [-1.5920e+00  1.6061e+00 -6.8380e-01]
  [-1.5265e+00  1.9213e+00  1.0512e+00]
  [ 9.8660e-01  2.2767e+00 -2.1500e-02]
  [-1.5320e-01  3.9735e+00  6.3270e-01]
  [-2.2900e-01  3.6748e+00 -1.1051e+00]
  [ 1.0344e+00  1.7952e+00  9.6040e-01]
  [ 1.9050e+00  2.8554e+00 -1.6120e-01]
  [ 9.5810e-01  1.4947e+00 -7.8710e-01]]

 [[-2.7298e+00  4.4129e+00  1.0005e+00]
  [-2.7484e+00  3.3174e+00 -5.3600e-02]
  [-3.6107e+00  5.0541e+00  8.9640e-01]
  [-1.8387e+00  5.0405e+00  8.9840e-01]
  [-2.7368e+00  3.9871e+00  2.0090e+00]
  [-1.5288e+00  2.4040e+00  6.6300e-02]
  [-2.7735e+00  3.7765e+00 -1.0488e+00]
  [-3.6668e+00  2.7289e+00  5.5600e-02]
  [-2.2860e-01  3.1846e+00 -1.2460e-01]
  [-1.5920e+00  1.6061e+00 -6.8380e-01]
  [-1.5265e+00  1.9213e+00  1.0512e+00]
  [ 9.8660e-01  2.2767e+00 -2.1500e-02]
  [-1.5320e-01  3.9735e+00  6.3270e-01]
  [-2.2900e-01  3.6748e+00 -1.1051e+00]
  [ 1.0344e+00  1.7952e+00  9.6040e-01]
  [ 1.9050e+00  2.8554e+00 -1.6120e-01]
  [ 9.5810e-01  1.4947e+00 -7.8710e-01]]]

atomic_charges represent the atomic charges of individual atoms for all conformers and are shaped (n_conformers, n_atoms)

[85]:
#Finds the shape of the ensemble coordinates
charge_shape = np.shape(ens.atomic_charges)
print(f'Shape of Atomic Charge Array\n{charge_shape}\n')

#Finds the Atomic Charges set for each conformer
ens.atomic_charges
Shape of Atomic Charge Array
(7, 17)

[85]:
array([[-0.0653, -0.0559,  0.023 ,  0.023 ,  0.023 , -0.0536,  0.0263,
         0.0263, -0.0559,  0.0265,  0.0265, -0.0653,  0.0263,  0.0263,
         0.023 ,  0.023 ,  0.023 ],
       [-0.0653, -0.0559,  0.023 ,  0.023 ,  0.023 , -0.0536,  0.0263,
         0.0263, -0.0559,  0.0265,  0.0265, -0.0653,  0.0263,  0.0263,
         0.023 ,  0.023 ,  0.023 ],
       [-0.0653, -0.0559,  0.023 ,  0.023 ,  0.023 , -0.0536,  0.0263,
         0.0263, -0.0559,  0.0265,  0.0265, -0.0653,  0.0263,  0.0263,
         0.023 ,  0.023 ,  0.023 ],
       [-0.0653, -0.0559,  0.023 ,  0.023 ,  0.023 , -0.0536,  0.0263,
         0.0263, -0.0559,  0.0265,  0.0265, -0.0653,  0.0263,  0.0263,
         0.023 ,  0.023 ,  0.023 ],
       [-0.0653, -0.0559,  0.023 ,  0.023 ,  0.023 , -0.0536,  0.0263,
         0.0263, -0.0559,  0.0265,  0.0265, -0.0653,  0.0263,  0.0263,
         0.023 ,  0.023 ,  0.023 ],
       [-0.0653, -0.0559,  0.023 ,  0.023 ,  0.023 , -0.0536,  0.0263,
         0.0263, -0.0559,  0.0265,  0.0265, -0.0653,  0.0263,  0.0263,
         0.023 ,  0.023 ,  0.023 ],
       [-0.0653, -0.0559,  0.023 ,  0.023 ,  0.023 , -0.0536,  0.0263,
         0.0263, -0.0559,  0.0265,  0.0265, -0.0653,  0.0263,  0.0263,
         0.023 ,  0.023 ,  0.023 ]])

weights represent defined weights of individual conformers, which can be useful for boltzmann weighting or other operations, and it is shaped (n_conformers,)

[86]:
#Gives the weights of individual conformers
ens.weights
[86]:
array([1., 1., 1., 1., 1., 1., 1.])

Key Methods in ConformerEnsemble

Conformer ensembles have a few key methods that allow transformations of all coordinates at once.

translate moves all atoms in all conformers by the vector specified

[87]:
#This translates all coordinates over by 2 units in the x direction
ens.translate([30,0,0])

#This prints the first 5 coordinates of each conformer
print(ens.coords[:, 0:5, :])
[[[27.1955  3.9964 -1.4128]
  [27.2516  3.3174 -0.0536]
  [26.316   4.6446 -1.4767]
  [27.1322  3.2576 -2.2181]
  [28.0846  4.6126 -1.5806]]

 [[27.2702  4.4129  1.0005]
  [27.2516  3.3174 -0.0536]
  [26.3893  5.0541  0.8964]
  [28.1613  5.0405  0.8984]
  [27.2632  3.9871  2.009 ]]

 [[25.9568  2.5483  0.1546]
  [27.2516  3.3174 -0.0536]
  [25.0979  3.2207  0.0645]
  [25.9275  2.0912  1.1488]
  [25.8472  1.7552 -0.592 ]]

 [[25.9568  2.5483  0.1546]
  [27.2516  3.3174 -0.0536]
  [25.0979  3.2207  0.0645]
  [25.9275  2.0912  1.1488]
  [25.8472  1.7552 -0.592 ]]

 [[25.9568  2.5483  0.1546]
  [27.2516  3.3174 -0.0536]
  [25.0979  3.2207  0.0645]
  [25.9275  2.0912  1.1488]
  [25.8472  1.7552 -0.592 ]]

 [[27.1955  3.9964 -1.4128]
  [27.2516  3.3174 -0.0536]
  [26.316   4.6446 -1.4767]
  [27.1322  3.2576 -2.2181]
  [28.0846  4.6126 -1.5806]]

 [[27.2702  4.4129  1.0005]
  [27.2516  3.3174 -0.0536]
  [26.3893  5.0541  0.8964]
  [28.1613  5.0405  0.8984]
  [27.2632  3.9871  2.009 ]]]

center_at_atom translates coordinates of the ensemble placing a specified atom at the origin

[88]:
#This gets the first atom of the conformer ensemble shared with all conformers
atom = ens.get_atom(0)

#This centers the conformer ensemble
ens.center_at_atom(atom)

#This prints the first 5 coordinates of each conformer
print(ens.coords[:, 0:5, :])
[[[ 0.      0.      0.    ]
  [ 0.0561 -0.679   1.3592]
  [-0.8795  0.6482 -0.0639]
  [-0.0633 -0.7388 -0.8053]
  [ 0.8891  0.6162 -0.1678]]

 [[ 0.      0.      0.    ]
  [-0.0186 -1.0955 -1.0541]
  [-0.8809  0.6412 -0.1041]
  [ 0.8911  0.6276 -0.1021]
  [-0.007  -0.4258  1.0085]]

 [[ 0.      0.      0.    ]
  [ 1.2948  0.7691 -0.2082]
  [-0.8589  0.6724 -0.0901]
  [-0.0293 -0.4571  0.9942]
  [-0.1096 -0.7931 -0.7466]]

 [[ 0.      0.      0.    ]
  [ 1.2948  0.7691 -0.2082]
  [-0.8589  0.6724 -0.0901]
  [-0.0293 -0.4571  0.9942]
  [-0.1096 -0.7931 -0.7466]]

 [[ 0.      0.      0.    ]
  [ 1.2948  0.7691 -0.2082]
  [-0.8589  0.6724 -0.0901]
  [-0.0293 -0.4571  0.9942]
  [-0.1096 -0.7931 -0.7466]]

 [[ 0.      0.      0.    ]
  [ 0.0561 -0.679   1.3592]
  [-0.8795  0.6482 -0.0639]
  [-0.0633 -0.7388 -0.8053]
  [ 0.8891  0.6162 -0.1678]]

 [[ 0.      0.      0.    ]
  [-0.0186 -1.0955 -1.0541]
  [-0.8809  0.6412 -0.1041]
  [ 0.8911  0.6276 -0.1021]
  [-0.007  -0.4258  1.0085]]]

rotate rotates coordinates by a set rotation matrix

[89]:
#This rotates the coordinates 90 degrees around the x axis (i.e. X stays the same, Y and Z invert)
rot_matrix = np.array([[1,0,0],[0,0,-1],[0,1,0]])

#This rotates the coordinates 90 degrees around the x axis
ens.rotate(rot_matrix)

#This prints the first 5 coordinates of each conformer
print(ens.coords[:, 0:5, :])
[[[ 0.      0.      0.    ]
  [ 0.0561  1.3592  0.679 ]
  [-0.8795 -0.0639 -0.6482]
  [-0.0633 -0.8053  0.7388]
  [ 0.8891 -0.1678 -0.6162]]

 [[ 0.      0.      0.    ]
  [-0.0186 -1.0541  1.0955]
  [-0.8809 -0.1041 -0.6412]
  [ 0.8911 -0.1021 -0.6276]
  [-0.007   1.0085  0.4258]]

 [[ 0.      0.      0.    ]
  [ 1.2948 -0.2082 -0.7691]
  [-0.8589 -0.0901 -0.6724]
  [-0.0293  0.9942  0.4571]
  [-0.1096 -0.7466  0.7931]]

 [[ 0.      0.      0.    ]
  [ 1.2948 -0.2082 -0.7691]
  [-0.8589 -0.0901 -0.6724]
  [-0.0293  0.9942  0.4571]
  [-0.1096 -0.7466  0.7931]]

 [[ 0.      0.      0.    ]
  [ 1.2948 -0.2082 -0.7691]
  [-0.8589 -0.0901 -0.6724]
  [-0.0293  0.9942  0.4571]
  [-0.1096 -0.7466  0.7931]]

 [[ 0.      0.      0.    ]
  [ 0.0561  1.3592  0.679 ]
  [-0.8795 -0.0639 -0.6482]
  [-0.0633 -0.8053  0.7388]
  [ 0.8891 -0.1678 -0.6162]]

 [[ 0.      0.      0.    ]
  [-0.0186 -1.0541  1.0955]
  [-0.8809 -0.1041 -0.6412]
  [ 0.8911 -0.1021 -0.6276]
  [-0.007   1.0085  0.4258]]]

Potential Unexpected Behavior

It’s important to note that the structure of the ConformerEnsemble class operates with a baseline connectivity, atoms, bonds, and attributes.

The only thing changing between individual conformers are the coordinates of the atoms, and as such, no attempt should be made to alter these properties unless you would like to affect all conformers within the ensemble.

This may lead to unexpected behavior if not used correctly when operating with individual conformers. One example of this is attempting to set an attribute on an individual Conformer, which inadvertently sets attributes for the full ensemble.

[90]:
#This prints the attributes in the Conformer Ensemble
print(f'Original ensemble attributes: {ens.attrib}')

#This uses Conformer 3 of the ensemble
conf3 = ens[2]

#This ATTEMPTS to set a new attribute to only Conformer 3
conf3.attrib["New"] = 1

#The full ensemble now has this property
print(f'Reprinting the ensemble attributes: {ens.attrib}')
Original ensemble attributes: {}
Reprinting the ensemble attributes: {'New': 1}

As a result, each conformer now has this property

[91]:
#This prints the conformers and their associated attributes
for conf in ens:
    print(f'Conformer = {conf}, Attributes = {conf.attrib}')
Conformer = Conformer(name='pentane', conf_id=0), Attributes = {'New': 1}
Conformer = Conformer(name='pentane', conf_id=1), Attributes = {'New': 1}
Conformer = Conformer(name='pentane', conf_id=2), Attributes = {'New': 1}
Conformer = Conformer(name='pentane', conf_id=3), Attributes = {'New': 1}
Conformer = Conformer(name='pentane', conf_id=4), Attributes = {'New': 1}
Conformer = Conformer(name='pentane', conf_id=5), Attributes = {'New': 1}
Conformer = Conformer(name='pentane', conf_id=6), Attributes = {'New': 1}