openforcefield/openff-toolkit

`compute_partial_charges_am1bcc` seems Unable to assign charges for some ligands

hannahbrucemacdonald opened this issue · 12 comments

Describe the bug
I am seeing Exception: Unable to assign charges when trying to parameterise some small molecules. This seems similar to #346, however the molecules fail for both oequacpac.OEAM1BCCELF10Charges and OEAM1BCCCharges

To Reproduce
python minimal_example.py
Of this set, there are 4 molecules that are failing (problematic_ids)

min_example.zip

Output

Traceback (most recent call last):
File "minimial_example.py", line 10, in
molecules[i].compute_partial_charges_am1bcc()
File "/home/brucemah/miniconda3/envs/omm_source/lib/python3.6/site-packages/openforcefield/topology/molecule.py", line 2014, in compute_partial_charges_am1bcc
self
File "/home/brucemah/miniconda3/envs/omm_source/lib/python3.6/site-packages/openforcefield/utils/toolkits.py", line 3030, in call
return method(*args, **kwargs)
File "/home/brucemah/miniconda3/envs/omm_source/lib/python3.6/site-packages/openforcefield/utils/toolkits.py", line 1278, in compute_partial_charges_am1bcc
raise Exception('Unable to assign charges')
Exception: Unable to assign charges

Computing environment (please complete the following information):

  • Operating system
  • Output of running conda list

Name Version Build Channel

_libgcc_mutex 0.1 conda_forge conda-forge
_openmp_mutex 4.5 0_gnu conda-forge
alabaster 0.7.12 py_0 conda-forge
ambermini 16.16.0 7 omnia
ambertools 19.0 0 ambermd
anaconda-client 1.7.1 py_0 conda-forge
arrow 0.15.5 py36_0 conda-forge
attrs 19.3.0 py_0 conda-forge
babel 2.8.0 py_0 conda-forge
backcall 0.1.0 py_0 conda-forge
beautifulsoup4 4.8.2 py36_0 conda-forge
binaryornot 0.4.4 py_1 conda-forge
bleach 3.1.0 py_0 conda-forge
blosc 1.17.0 he1b5a44_1 conda-forge
bokeh 1.4.0 py36_0 conda-forge
boost 1.70.0 py36h9de70de_1 conda-forge
boost-cpp 1.70.0 h8e57a91_2 conda-forge
bson 0.5.8 py_0 conda-forge
bzip2 1.0.8 h516909a_2 conda-forge
ca-certificates 2019.11.28 hecc5488_0 conda-forge
cairo 1.16.0 hfb77d84_1002 conda-forge
certifi 2019.11.28 py36_0 conda-forge
cffi 1.13.2 py36h8022711_0 conda-forge
cftime 1.0.4.2 py36hc1659b7_0 conda-forge
chardet 3.0.4 py36_1003 conda-forge
click 7.0 py_0 conda-forge
cloudpickle 1.2.2 py_1 conda-forge
clyent 1.2.2 py_1 conda-forge
codecov 2.0.15 pypi_0 pypi
colorama 0.4.0 pypi_0 pypi
conda 4.5.11 py36_1000 conda-forge
conda-build 3.15.1 py36_0 conda-forge
conda-env 2.6.0 1 conda-forge
cookiecutter 1.6.0 py36_1000 conda-forge
coverage 4.5.1 pypi_0 pypi
cryptography 2.8 py36h72c5cf5_1 conda-forge
curl 7.65.3 hf8cf82a_0 conda-forge
cycler 0.10.0 pypi_0 pypi
cython 0.29.14 py36he1b5a44_0 conda-forge
cytoolz 0.10.1 py36h516909a_0 conda-forge
dask 2.9.1 py_0 conda-forge
dask-core 2.9.1 py_0 conda-forge
dask-jobqueue 0.6.3 py_0 conda-forge
dbus 1.13.6 he372182_0 conda-forge
decorator 4.4.1 py_0 conda-forge
defusedxml 0.6.0 py_0 conda-forge
distributed 2.9.1 py_0 conda-forge
docrep 0.2.7 py_0 conda-forge
docutils 0.15.2 py36_0 conda-forge
entrypoints 0.3 py36_1000 conda-forge
expat 2.2.5 he1b5a44_1004 conda-forge
fftw 3.3.8 nompi_h7f3a6c3_1110 conda-forge
fftw3f 3.3.4 2 omnia
filelock 3.0.10 py_0 conda-forge
fontconfig 2.13.1 h86ecdb6_1001 conda-forge
freetype 2.10.0 he983fc9_1 conda-forge
fsspec 0.6.2 py_0 conda-forge
future 0.18.2 py36_0 conda-forge
gettext 0.19.8.1 hc5be6a0_1002 conda-forge
gitdb2 2.0.5 pypi_0 pypi
gitpython 2.1.11 pypi_0 pypi
glib 2.58.3 py36h6f030ca_1002 conda-forge
glob2 0.7 py_0 conda-forge
grand 0.2.0 pypi_0 pypi
gst-plugins-base 1.14.5 h0935bb2_0 conda-forge
gstreamer 1.14.5 h36ae1b5_0 conda-forge
hdf4 4.2.13 hf30be14_1003 conda-forge
hdf5 1.10.5 nompi_h3c11f04_1104 conda-forge
heapdict 1.0.1 py_0 conda-forge
icu 64.2 he1b5a44_1 conda-forge
idna 2.8 py36_1000 conda-forge
imagesize 1.2.0 py_0 conda-forge
importlib_metadata 1.4.0 py36_0 conda-forge
ipykernel 5.1.3 py36h5ca1d4c_0 conda-forge
ipython 7.11.1 py36h5ca1d4c_0 conda-forge
ipython_genutils 0.2.0 py_1 conda-forge
jedi 0.15.2 py36_0 conda-forge
jinja2 2.10 py_1 conda-forge
jinja2-time 0.2.0 py_2 conda-forge
jpeg 9c h14c3975_1001 conda-forge
jsonschema 3.2.0 py36_0 conda-forge
jupyter_client 5.3.4 py36_0 conda-forge
jupyter_core 4.6.1 py36_0 conda-forge
kiwisolver 1.1.0 py36hc9558a2_0 conda-forge
krb5 1.16.4 h2fd8d38_0 conda-forge
libblas 3.8.0 14_openblas conda-forge
libcblas 3.8.0 14_openblas conda-forge
libclang 9.0.1 default_hde54327_0 conda-forge
libcurl 7.65.3 hda55be3_0 conda-forge
libedit 3.1.20170329 hf8c457e_1001 conda-forge
libffi 3.2.1 he1b5a44_1006 conda-forge
libgcc 7.2.0 h69d50b8_2 conda-forge
libgcc-ng 9.2.0 h24d8f2e_2 conda-forge
libgfortran-ng 7.3.0 hdf63c60_4 conda-forge
libgomp 9.2.0 h24d8f2e_2 conda-forge
libiconv 1.15 h516909a_1005 conda-forge
liblapack 3.8.0 14_openblas conda-forge
libllvm9 9.0.1 hc9558a2_0 conda-forge
libnetcdf 4.7.3 nompi_h94020b1_100 conda-forge
libopenblas 0.3.7 h5ec1e0e_6 conda-forge
libpng 1.6.37 hed695b0_0 conda-forge
libsodium 1.0.17 h516909a_0 conda-forge
libssh2 1.8.2 h22169c7_2 conda-forge
libstdcxx-ng 9.2.0 hdf63c60_2 conda-forge
libtiff 4.1.0 hc3755c2_3 conda-forge
libuuid 2.32.1 h14c3975_1000 conda-forge
libxcb 1.13 h14c3975_1002 conda-forge
libxkbcommon 0.9.1 hebb1f50_0 conda-forge
libxml2 2.9.10 hee79883_0 conda-forge
libxslt 1.1.33 h31b3aaa_0 conda-forge
llvmlite 0.25.0 pypi_0 pypi
locket 0.2.0 py_2 conda-forge
lxml 4.4.2 py36h7ec2d77_0 conda-forge
lz4-c 1.8.3 he1b5a44_1001 conda-forge
lzo 2.10 h14c3975_1000 conda-forge
markupsafe 1.1.1 py36h516909a_0 conda-forge
matplotlib 3.1.2 py36_1 conda-forge
matplotlib-base 3.1.2 py36h250f245_1 conda-forge
mdtraj 1.9.1 py36h447964c_2 conda-forge
mistune 0.8.4 py36h516909a_1000 conda-forge
mmpbsa-py 16.0 pypi_0 pypi
mock 3.0.5 py36_0 conda-forge
more-itertools 8.0.2 py_0 conda-forge
msgpack-python 0.6.2 py36hc9558a2_0 conda-forge
nbconvert 5.6.1 py36_0 conda-forge
nbformat 5.0.3 py_0 conda-forge
ncurses 6.1 hf484d3e_1002 conda-forge
netcdf-fortran 4.5.2 nompi_h09cde99_103 conda-forge
netcdf4 1.5.3 nompi_py36hd35fb8e_102 conda-forge
networkx 2.2 pypi_0 pypi
nose 1.3.7 py36_1002 conda-forge
nose-timer 0.7.3 py_0 conda-forge
notebook 6.0.1 py36_0 conda-forge
nspr 4.24 he1b5a44_0 conda-forge
nss 3.47 he751ad9_0 conda-forge
numba 0.40.0 pypi_0 pypi
numexpr 2.7.1 py36hb3f55d8_0 conda-forge
numpy 1.17.3 py36h95a1406_0 conda-forge
olefile 0.46 py_0 conda-forge
openeye-toolkits 2019.4.2 py36_0 openeye
openforcefield 0.6.0 py36_0 omnia
openforcefields 1.0.0 py36_0 omnia
openmm 7.4.0 py36_cuda101_rc_1 omnia
openmmforcefields 0.6.0+18.ga37fdcc pypi_0 pypi
openmmtools 0+unknown pypi_0 pypi
openmoltools 0.9.0.dev0 pypi_0 pypi
openssl 1.1.1d h516909a_0 conda-forge
packaging 20.0 py_0 conda-forge
packmol-memgen 1.0.5rc0 pypi_0 pypi
pandas 0.25.3 py36hb3f55d8_0 conda-forge
pandoc 2.9.1.1 0 conda-forge
pandocfilters 1.4.2 py_1 conda-forge
parmed 3.1.0 pypi_0 pypi
parso 0.5.2 py_0 conda-forge
partd 1.1.0 py_0 conda-forge
patchelf 0.10 he1b5a44_0 conda-forge
pcre 8.43 he1b5a44_0 conda-forge
pdb4amber 1.7.dev0 pypi_0 pypi
perses 0.4.0 pypi_0 pypi
pexpect 4.7.0 py36_0 conda-forge
pickleshare 0.7.5 py36_1000 conda-forge
pillow 7.0.0 py36hefe7db6_0 conda-forge
pip 18.1 py36_1000 conda-forge
pixman 0.38.0 h516909a_1003 conda-forge
pkginfo 1.5.0.1 py_0 conda-forge
pluggy 0.12.0 py_0 conda-forge
poyo 0.5.0 py_0 conda-forge
progressbar2 3.47.0 py_0 conda-forge
prometheus_client 0.7.1 py_0 conda-forge
prompt_toolkit 3.0.2 py_0 conda-forge
psutil 5.6.7 py36h516909a_0 conda-forge
pthread-stubs 0.4 h14c3975_1001 conda-forge
ptyprocess 0.6.0 py_1001 conda-forge
py 1.8.1 py_0 conda-forge
pycairo 1.18.2 py36h438ddbb_0 conda-forge
pycosat 0.6.3 py36h516909a_1002 conda-forge
pycparser 2.19 py36_1 conda-forge
pygments 2.5.2 py_0 conda-forge
pymbar 3.0.5 py36hc1659b7_0 conda-forge
pymsmt 19.0 pypi_0 pypi
pyopenssl 19.1.0 py36_0 conda-forge
pyparsing 2.2.2 pypi_0 pypi
pyqt 5.12.3 py36hcca6a23_1 conda-forge
pyqt5-sip 4.19.18 pypi_0 pypi
pyqtwebengine 5.12.1 pypi_0 pypi
pyrsistent 0.15.7 py36h516909a_0 conda-forge
pysocks 1.7.1 py36_0 conda-forge
pytables 3.6.1 py36h9f153d1_0 conda-forge
pytest 5.3.2 py36_0 conda-forge
pytest-runner 5.2 py_0 conda-forge
python 3.6.7 h357f687_1006 conda-forge
python-dateutil 2.7.3 pypi_0 pypi
python-utils 2.3.0 py_1 conda-forge
pytraj 2.0.5 pypi_0 pypi
pytz 2018.5 pypi_0 pypi
pyyaml 5.1.2 py36h516909a_0 conda-forge
pyzmq 18.1.1 py36h1768529_0 conda-forge
qt 5.12.5 hd8c4c69_1 conda-forge
rdkit 2019.09.3 py36hb31dc5d_0 conda-forge
readline 8.0 hf8c457e_0 conda-forge
requests 2.22.0 py36_1 conda-forge
ruamel_yaml 0.15.71 py36h14c3975_1000 conda-forge
sander 16.0 pypi_0 pypi
scipy 1.4.1 py36h921218d_0 conda-forge
seaborn 0.9.0 pypi_0 pypi
send2trash 1.5.0 py_0 conda-forge
setuptools 44.0.0 py36_0 conda-forge
six 1.13.0 py36_0 conda-forge
smirnoff99frosst 1.1.0 py36_1 omnia
smmap2 2.0.5 pypi_0 pypi
snowballstemmer 2.0.0 py_0 conda-forge
sortedcontainers 2.1.0 py_0 conda-forge
soupsieve 1.9.4 py36_0 conda-forge
sphinx 1.8.1 py36_1000 conda-forge
sphinx_rtd_theme 0.4.2 py_0 conda-forge
sphinxcontrib-websupport 1.1.2 py_0 conda-forge
sqlite 3.30.1 hcee41ef_0 conda-forge
tblib 1.6.0 py_0 conda-forge
termcolor 1.1.0 py_2 conda-forge
terminado 0.8.3 py36_0 conda-forge
testpath 0.4.4 py_0 conda-forge
tinydb 3.15.2 py_0 conda-forge
tk 8.6.10 hed695b0_0 conda-forge
toml 0.10.0 py_0 conda-forge
toolz 0.10.0 py_0 conda-forge
tornado 6.0.3 py36h516909a_0 conda-forge
tqdm 4.41.1 py_0 conda-forge
traitlets 4.3.3 py36_0 conda-forge
urllib3 1.25.7 py36_0 conda-forge
wcwidth 0.1.8 py_0 conda-forge
webencodings 0.5.1 py_1 conda-forge
wheel 0.33.6 py36_0 conda-forge
whichcraft 0.6.1 py_0 conda-forge
xmltodict 0.12.0 py_0 conda-forge
xorg-kbproto 1.0.7 h14c3975_1002 conda-forge
xorg-libice 1.0.10 h516909a_0 conda-forge
xorg-libsm 1.2.3 h84519dc_1000 conda-forge
xorg-libx11 1.6.9 h516909a_0 conda-forge
xorg-libxau 1.0.9 h14c3975_0 conda-forge
xorg-libxdmcp 1.1.3 h516909a_0 conda-forge
xorg-libxext 1.3.4 h516909a_0 conda-forge
xorg-libxrender 0.9.10 h516909a_1002 conda-forge
xorg-libxt 1.2.0 h516909a_0 conda-forge
xorg-renderproto 0.11.1 h14c3975_1002 conda-forge
xorg-xextproto 7.3.0 h14c3975_1002 conda-forge
xorg-xproto 7.0.31 h14c3975_1007 conda-forge
xz 5.2.4 h14c3975_1001 conda-forge
yaml 0.1.7 h14c3975_1001 conda-forge
yank 0.23.8 pypi_0 pypi
yapf 0.28.0 py_0 conda-forge
zeromq 4.3.2 he1b5a44_2 conda-forge
zict 1.0.0 py_0 conda-forge
zipp 0.6.0 py_0 conda-forge
zlib 1.2.11 h516909a_1006 conda-forge
zstd 1.4.4 h3b9ef0a_1 conda-forge

Additional context

@hannahbrucemacdonald Thanks for the report and example. I'm able to reproduce this error on my machine. I'll keep you updated as I work on this.

@j-wags : Let's mark this as "high priority" since it's blocking us from finishing the free energy benchmarks of openff-1.0.0.

@j-wags thanks for looking into this!

I think @jchodera figured it out - the problem goes away if molecule.generate_conformers()

He's going to open a PR into https://github.com/openmm/openmm-forcefields that should fix this.

The bigger issue here is that some molecules in that tarball succeed when calling just

molecule.compute_partial_charges_am1bcc()

but others fail, and succeed only if calling

molecule.generate_conformers(n_conformers=10)
molecule.compute_partial_charges_am1bcc()

I'd love to figure out what's going on with this, and which one of these idioms is the "right" way to assign charges.

Hm, I had figured this had something to do with the molecules' connectivity, and not the geometry. That's a bit surprising, and I'll keep looking into this issue.

@hannahbrucemacdonald Normally SMIRNOFF calculates partial charges based on several conformers it generates behind-the-scenes for a molecule. This makes the output of the charge calculation deterministic (since OE omega will generate the same conformer for the same graph representation of a molecule). Is your workflow intending to look specifically at the charges applied to the input conformers in the attached file, or is it OK to regenerate the conformers (our normal workflow)?

Regardless, I'm going to debug the quacpac failure. I'd just like to figure out if generating new conformers can get you running again in the short-term.

I think I was under the impression that Molecule.compute_partial_charges_am1bcc() implemented our AM1-BCC ELF10 best practices, but it appears that is not the case!

The key issue was that openmm-forcefields just called compute_partial_charges_am1bcc() for GAFF, but uses the openforcefield.typing.engines.smirnoff.ForceField.create_openmm_system for SMIRNOFF (which generates conformers first).

It sounds like compute_partial_charges_am1bcc() may just use the existing conformer(s) defined in the molecule, so if a bad geometry is defined, that appears to cause quacpac to fail.

We should figure out what we actually want compute_partial_charges_am1bcc() to do---whether it is our best-practices charging method, or just a "charge this conformer" method.

I think I was under the impression that Molecule.compute_partial_charges_am1bcc() implemented our AM1-BCC ELF10 best practices, but it appears that is not the case!

Ah, that makes sense. You're right -- It's not clear from the name whether compute_partial_charges_am1bcc() implements the whole workflow under the hood or not.

When I initially put it together, I had the same question. I decided that it's best to keep the toolkit wrappers as modular as possible, so generate_conformers and compute_partial_charges became separate functions. I'd be OK to put in a "whole shebang" method for charge calculation, with a name that reflects its function -- and it could even mirror the ChargeIncrementModel SMIRNOFF spec section -- so maybe generate_charges(partial_charge_method, n_conformers)?

Ok, I've confirmed that it's a problem with OpenEye Quacpac rejecting the conformers due to a short interatomic distance. I think we can consider this issue to be resolved if you agree, @hannahbrucemacdonald.

I'm opening a new issue to make OpenEyeToolkitWrapper failures more verbose now that I know how to wrangle OE output streams -- having the distance error print out initially would be much friendlier to users than the current opaque "Unable to generate charges" message.

from openforcefield.topology import Molecule
from openeye import oechem, oeomega, oequacpac
molecules = Molecule.from_file('/Users/jwagner/Downloads/PTP1B_ligands_shifted.sdf')

problematic_ids = [3,7,14,21]

for idx in range(len(molecules)):
    mol = molecules[idx]
    print(idx, mol.total_charge, mol.to_smiles())
    oemol = mol.to_openeye()
    copymol = oechem.OEMol(oemol)

    errfs = oechem.oeosstream()
    oechem.OEThrow.SetOutputStream(errfs)
    oechem.OEThrow.Clear()
    quacpac_status = oequacpac.OEAssignCharges(oemol, oequacpac.OEAM1BCCELF10Charges())
    oechem.OEThrow.SetOutputStream(oechem.oeerr)#restoring to original state
    print(errfs.str().decode("UTF-8"))

0 -2 [H]c1c(c(c(c(c1[H])[H])C(=O)N([H])c2c(c(c(c(c2[H])C3=C(C(=C(S3)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H])[H])[H])[H])[H]

1 -2 [H]c1c(c(c(c(c1[H])N([H])S(=O)(=O)c2c(c(c(c(c2[H])[H])F)[H])[H])[H])C3=C(C(=C(S3)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H]

2 -2 [H]c1c(c(c(c(c1[H])N([H])C([H])([H])C2(C(C(C(C(C2([H])[H])([H])[H])([H])[H])([H])[H])([H])[H])[H])[H])C3=C(C(=C(S3)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H]

3 -2 [H]c1c(c(c(c(c1[H])N([H])C2(C(C(N(C(C2([H])[H])([H])[H])C(=O)OC([H])([H])[H])([H])[H])([H])[H])[H])[H])C3=C(C(=C(S3)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H]
Warning: Distance between atom 29 and 38 is below 0.7A
Please, correct the input
Warning: OESEmpPartialCharges: Stopping the charging process for molecule 23480
Warning: OESEmpBCCPartialCharges: Stopping the charging process at the second stage for mol 23480

4 -2 [H]c1c(c(c(c(c1[H])N([H])C(=O)N([H])C([H])(C([H])([H])[H])C([H])([H])[H])[H])C2=C(C(=C(S2)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H]

5 -2 [H]c1c(c(c(c(c1[H])[H])C([H])([H])S(=O)(=O)N2C(C(C(C(C2([H])[H])([H])[H])([H])N([H])c3c(c(c(c(c3[H])C4=C(C(=C(S4)C(=O)[O-])OC([H])([H])C(=O)[O-])Cl)[H])[H])[H])([H])[H])([H])[H])[H])[H]

6 -2 [H]c1c(c(c(c(c1[H])[H])C([H])([H])N([H])c2c(c(c(c(c2[H])C3=C(C(=C(S3)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H])[H])[H])[H])[H]

7 -2 [H]c1c(c(c(c(c1[H])N([H])C2(C(C(N(C(C2([H])[H])([H])[H])S(=O)(=O)C([H])([H])[H])([H])[H])([H])[H])[H])[H])C3=C(C(=C(S3)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H]
Warning: Distance between atom 29 and 38 is below 0.7A
Please, correct the input
Warning: OESEmpPartialCharges: Stopping the charging process for molecule 23482
Warning: OESEmpBCCPartialCharges: Stopping the charging process at the second stage for mol 23482

8 -2 [H]c1c(c(c(c(c1[H])[H])S(=O)(=O)N2C(C(C(C(C2([H])[H])([H])[H])([H])N([H])c3c(c(c(c(c3[H])C4=C(C(=C(S4)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H])[H])[H])([H])[H])([H])[H])[H])[H]

9 -2 [H]c1c(c(c(c(c1[H])[H])C([H])([H])Oc2c(c(c(c(c2[H])C3=C(C(=C(S3)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H])[H])[H])[H])[H]

10 -2 [H]c1c(c(c(c(c1[H])N([H])C2(C(C(C(C(C2([H])[H])([H])[H])([H])[H])([H])[H])([H])[H])[H])[H])C3=C(C(=C(S3)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H]

11 -2 [H]c1c(c(c(c(c1[H])N([H])C(=O)C([H])([H])C([H])([H])[H])[H])C2=C(C(=C(S2)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H]

12 -2 [H]c1c(c(c(c(c1[H])N([H])C2(C(C(C(C2([H])[H])([H])[H])([H])[H])([H])[H])[H])[H])C3=C(C(=C(S3)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H]

13 -2 [H]c1c(c(c(c(c1[H])[H])C([H])([H])S(=O)(=O)N2C(C(C(C(C2([H])[H])([H])[H])([H])N([H])c3c(c(c(c(c3[H])C4=C(C(=C(S4)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H])[H])[H])([H])[H])([H])[H])[H])[H]

14 -2 [H]c1c(c(c(c(c1[H])N([H])C2(C(C(OC(C2([H])[H])([H])[H])([H])[H])([H])[H])[H])[H])C3=C(C(=C(S3)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H]
Warning: Distance between atom 16 and 33 is below 0.7A
Please, correct the input
Warning: OESEmpPartialCharges: Stopping the charging process for molecule 23477
Warning: OESEmpBCCPartialCharges: Stopping the charging process at the second stage for mol 23477

15 -2 [H]c1c(c(c(c(c1[H])N([H])C(=O)OC([H])([H])[H])[H])C2=C(C(=C(S2)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H]

16 -2 [H]c1c(c(c(c(c1[H])[H])C([H])([H])S(=O)(=O)N2C(C(C(C(C2([H])[H])([H])[H])([H])N([H])c3c(c(c(c(c3[H])C4=C(C(=C(S4)C(=O)[O-])OC([H])([H])C(=O)[O-])C([H])([H])[H])[H])[H])[H])([H])[H])([H])[H])[H])[H]

17 -2 [H]c1c(c(c(c(c1[H])OC([H])([H])[H])[H])C2=C(C(=C(S2)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H]

18 -2 [H]c1c(c(c(c(c1[H])N([H])[H])[H])C2=C(C(=C(S2)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H]

19 -2 [H]c1c(c(c(c(c1[H])N([H])C2(C(C(C(C(C(C2([H])[H])([H])[H])([H])[H])([H])[H])([H])[H])([H])[H])[H])[H])C3=C(C(=C(S3)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H]

20 -2 [H]c1c(c(c(c(c1[H])[H])C([H])([H])S(=O)(=O)N2C(C(C(C(C2([H])[H])([H])[H])([H])N([H])c3c(c(c(c(c3[H])C4=C(C(=C(S4)C(=O)[O-])OC([H])([H])C(=O)[O-])[H])[H])[H])[H])([H])[H])([H])[H])[H])[H]

21 -2 [H]c1c(c(c(c(c1[H])N([H])C2(C(C(N(C(C2([H])[H])([H])[H])C(=O)C([H])([H])[H])([H])[H])([H])[H])[H])[H])C3=C(C(=C(S3)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H]
Warning: Distance between atom 28 and 37 is below 0.7A
Please, correct the input
Warning: OESEmpPartialCharges: Stopping the charging process for molecule 23479
Warning: OESEmpBCCPartialCharges: Stopping the charging process at the second stage for mol 23479

22 -2 [H]c1c(c(c(c(c1[H])N([H])C2(C(C(C(C(C2([H])[H])(C([H])([H])[H])C([H])([H])[H])([H])[H])(C([H])([H])[H])C([H])([H])[H])([H])[H])[H])[H])C3=C(C(=C(S3)C(=O)[O-])OC([H])([H])C(=O)[O-])Br)[H]

@j-wags : It would also be great to discuss what the API should do. I don't think it's good design to have molecule.compute_partial_charges_am1bcc() either

  • use a conformer if it's defined
  • use some of multiple conformers (how many?) if they're defined
  • fail if it doesn't like a conformer
  • fail if no conformers are defined

and to require that, to achieve the established best practice for assigning charges, we have to overwrite the conformers in the molecule with

molecule.generate_conformers(n_conformers=10)
molecule.compute_partial_charges_am1bcc()

This is just bad design, because it (1) doesn't produce a well-determined result, (2) violates the principle of minimum surprise in causing (or using) unintended side-effects, and (3) is verbose and confusing compared to a single API point that just does the best practice.

@j-wags: We've resolved the urgency in openmm/openmmforcefields#95, but it would still be useful to revisit what API design we really want here.

One other consideration here: we want to be sure we are steering people toward clear, unambiguous behavior to make it very easy to use current best practices, while still keeping optional flexibility so we can experiment as needed (with a bit more effort to select non-default behavior).

Per discussion between @jchodera and myself on Monday --

Big picture is that we'll do best practices, as defined by:

  • compute_partial_charges_am1bcc, assign_partial_charges, and assign_fractional_bond_orders will disregard existing conformers unless an override is provided.
  • create_openmm_system will disregard existing partial charges and fractional bond orders unless an override is provided
  • We define best practice for the am1bccelf10 method as "generate a lot of conformers, potentially by using a low RMS cutoff, since we need to start with at least 50 to get multiple conformers due to the lowest-2% step in the ELF10 process" (I talked to Chris Bayly and, for molecules with fewer than 50 conformers, the fraction considered is >2%)

This issue will be closed when the following changes are made:

  • forcefield.create_openmm_system

    • new kwarg: fractional_bond_orders_from_molecules: iterable of Molecule
  • molecule.generate_conformers

    • new kwarg: rms_cutoff --> float or Quantity (distance dimension). If float, assume Angstrom
  • Molecule/OpenEyeToolkitWrapper/AmberToolsToolkitWrapper.compute_partial_charges_am1bcc

    • Raise DeprecationWarning -- Tell users to use new assign_partial_charges(partial_charge_method="am1bcc") in the future
    • behavior change: disregard existing confs by default
    • new kwarg: use_conformers = conformer or [iterable_of_conformers]
    • Update releasenotes: mention behavior change
  • new methods: Molecule/OpenEyeToolkitWrapper/AmberToolsToolkitWrapper.assign_partial_charges

    • behavior: disregard existing confs by default. Make new conformers with a small RMS cutoff so ELF10 has several to choose from
    • kwarg: partial_charge_method --> str. Name of a partial charge method that is validated by underlying toolkit. ValueError raised if the method can't be handled.
    • kwarg: use_conformers --> conformer or [iterable_of_conformers]
    • kwarg: strict_n_conformers --> bool, saying whether to raise a ValueError if the the number of input conformers is incorrect (versus a warning). This provides a pathway for an error if someone produces a logically inconsistent OFFXML with, for example <ChargeIncrementModel method="graph-based-method" num_conformers="5">