* add angles and distances
* add Inversions
* add torsiona angle contribs
* use new contribs in test
* use new inversion and torsion contribs in dg
* use new distance contribs in dg
* use new angle constraints in dg
* use new constraints in FF tests
* update docstrings
* remove unused import
* include new contribs
* cleanup includes
* make changes requested by @greglandrum
* use std::move instead of release
* fix unsigned int to int comparison
* revert previous mistake
* declaration & init together, sinthetaSq in [0, 1]
* using std::swap
* use that sinThetaSq in [0,1]
* declare & init at same time
* use knowledge about target range
* use std::clamp
* use std::max
* numerically more stable trigonometrics
* numerically more stable trigonometrics
* numerically more stable trigonometrics
* range based for-loop
* actually do the assignement
* Update Code/ForceField/MMFF/Params.h
Co-authored-by: Greg Landrum <greg.landrum@gmail.com>
* implemented suggested changes
* Revert "implemented suggested changes"
This reverts commit f56e8f0ab2.
* auto typing
* remove old comment
* revert to numerically more stable expression
* now correctly formatted
---------
Co-authored-by: Greg Landrum <greg.landrum@gmail.com>
* Fixes atom documentation
* Fixes#1461
This is a complicated one. Basically URANGE_CHECK when
used on unsigned integers has a problem when the size of
the range it’s checking is 0. The standard operations is
to check
URANGE(num, size-1)
Which (for unsigned integers) obviously rolls over.
This fixes all usage cases to be
URANGE(num+1, size)
And fixes the bugs found. (addBond and the mmff tests)
* Fixes#1461 - Updates URANGE_CHECK to be 0<=x<hi
* enforce planarity
* increase the force constant for the impropers
* force constant adapted
* reduced tolerance for planarity and force constants changed for some torsions
* tolerance for planarity increased a bit again
* cerr outputs removed
* planarity tolerance increased
* boost log added in planarity check
- fixed a bug in Code/GraphMol/ForceFieldHelpers/MMFF/AtomTyper.cpp
which caused misassignment of atom types in CYGUAN01 upon shuffling
the order of atoms in the validation SDF files
- added checks for acos and asin function parameters to be within
a (-1, 1) range
to the current value) (C++/Python)
- added absolute/relative AngleConstraints (C++/Python)
- added absolute/relative TorsionConstraints (C++/Python)
- added PositionConstraints (C++/Python)
- exposed fixedPoints from Python
- added relevant C++/Python tests
- removed a number of redundant "this->" in member functions
- moved some getGrad() code into Utils::calcAngleBendGrad and
Utils::calcTorsionGrad to avoid repeating the same code
for constraints
- added #ifdef M_PI (...) #endif in all relevant places
- made length() and sqLength() method consistent
with respect to usage of pow(x, 2) vs x*x in
Code/Geometry/point.h
- removed gzip-related boost.iostreams dependency and
replaced with portable "cmake -E tar xzf" command
in Code/ForceField/MMFF/CMakeLists.txt
incorrect typing might arise when hydrogens were not added after
generating 3D coordinates from SMILES strings; now all 761 test molecules
are correctly typed no matter whether hydrogens are explicit or implicit
- MMFF test suite: I have cut down to the bare essential the
MMFF94/MMFF94s reference log files, but their size could be reduced only
by about 30%. It could have been reduced more converting multiple spaces
into a tab, but the MMFF94 file (the larger one) would still be around
11 MB, and human readability would be greatly impaired. Hence I decided
to keep the spaces and gzip the reference logs, which reduces their
combined size to ~ 3.5 MB, which I think is fine; the test program checks
if the gunzipped files already exist, otherwise it gunzips them upfront.
While cutting, I also sorted the molecules in the same order as in the
SDF/SMILES files, so that it runs about 10 times faster than before.
Now the test runs on MMFF94 only (MMFF94s only concerns different OOP
parameters, there are no algorithmic differences, so as long as one does
not alter the original parameters it can be safely skipped), computing
every 4th molecule, and it runs in 12 seconds on my laptop. Running
all molecules takes ~ 50 seconds, but I think it is rather overkill,
and I would keep it as it is.
- I have added a test suite for MMFF ForceFieldHelpers (like the one
already existing for UFF); I have also complemented the Python wrapper
test suite for ForceFieldHelpers with a few tests for MMFF.
- I have written Python wrappers for the MMFF-related functionality;
while doing that I realized that many of the wrapper code relocations
that I made in my previous pull request were not necessary/appropriate,
so I reverted them. The only difference from the UFF Python API is that,
just like for the C++ API, in addition to the PyForceField object there
is a PyMMFFMolProperties object which is created before constructing the
force field itself; the PyMMFFMolProperties is necessary to set (e.g.,
dielectric constant, dielectric model) or get (e.g., atom type, formal
and partial charge) some MMFF properties which are not present in UFF,
while preserving binary compatibility of the libraries. Probably you
remember that we discussed about setting atom type and charge properties
with SetProp besides populating the MMFFMolProperties object, in order
to allow easy access to Python users. However, I think that the solution
I adopted is preferrable since it is more consistent with the C++ API,
it enables faster access to properties and it allows tailoring the MMFF
environment (i.e., choosing MMFF94/MMFF94s, setting the verbosity level,
including/excluding terms from the MMFF equation, setting dielectric
constant/model) just as from C++.
The MMFF-related Python functions I implemented are:
* MMFFOptimizeMolecule(mol, mmffVariant = "MMFF94", maxIters = 200,
nonBondedThresh = 100.0, confId = -1, ignoreInterfragInteractions
= true)
uses MMFF to optimize a molecule's structure (just like
UFFOptimizeMolecule)
* SanitizeMMFFMol(mol)
sanitizes a molecule according to MMFF requirements
* SetupMMFFForceField(mol, mmffVariant = "MMFF94", mmffVerbosity = 0)
returns a PyMMFFMolProperties object for a molecule; the
PyMMFFMolProperties object is required by MMFFGetMoleculeForceField()
and can be used to get/set MMFF properties
* MMFFGetMoleculeForceField(mol, pyMMFFMolProperties,
nonBondedThresh = 100.0, confId = -1, ignoreInterfragInteractions
= true)
returns a MMFF force field for a molecule (just like
UFFGetMoleculeForceField)
* MMFFHasAllMoleculeParams(mol)
checks if MMFF parameters are available for all of a molecule's atoms
(just like UFFHasAllMoleculeParams)
There are also a few methods connected to the PyMMFFMolProperties class
which mirror those available from C++ for the MMFFMolProperties class:
* GetMMFFAtomType(idx)
Retrieves MMFF atom type for atom with index idx
* GetMMFFFormalCharge(idx)
Retrieves MMFF formal charge for atom with index idx
* GetMMFFPartialCharge(idx)
Retrieves MMFF partial charge for atom with index idx
* SetMMFFDielectricModel(dielModel = 1)
sets the DielModel MMFF property (1: constant; 2: distance-dependent;
defaults to constant)
* SetMMFFDielectricConstant(dielConst = 1.0)
Sets the DielConst MMFF property (defaults to 1.0)
* SetMMFFBondTerm(state = True)
Sets the bond term to be included in the MMFF equation (defaults
to True)
* SetMMFFAngleTerm(state = True)
Sets the angle term to be included in the MMFF equation (defaults
to True)
* SetMMFFStretchBendTerm(state = True)
Sets the stretch-bend term to be included in the MMFF equation (defaults
to True)
* SetMMFFOopTerm(state = True)
Sets the out-of-plane bend term to be included in the MMFF equation
(defaults to True)
* SetMMFFTorsionTerm(state = True)
Sets the torsional term to be included in the MMFF equation (defaults
to True)
* SetMMFFVdWTerm(state = True)
Sets the Van der Waals term to be included in the MMFF equation
(defaults to True)
* SetMMFFEleTerm(state = True)
Sets the electrostatic term to be included in the MMFF equation
(defaults to True)
* SetMMFFVariant(mmffVariant = "MMFF94")
Sets the MMFF variant to be used ("MMFF94" or "MMFF94s"; defaults to
"MMFF94")
* SetMMFFVerbosity(verbosity = 0)
Sets the MMFF verbosity (0: none; 1: low; 2: high; defaults to 0)
Hence, most users will do something like this to optimize a molecule
structure obtained from a SMILES string:
from rdkit import Chem
from rdkit.Chem import AllChem
m = Chem.MolFromSmiles("O=C(C)c1cccnc1", False)
AllChem.SanitizeMMFFMol(m)
m2 = Chem.AddHs(m)
AllChem.EmbedMolecule(m2)
# Opt
AllChem.MMFFOptimizeMolecule(m2)
print >>file('structure_min.sdf','w'), Chem.MolToMolBlock(m2)
Those willing to play a bit more with MMFF properties may do the
following:
from rdkit import Chem
from rdkit.Chem import AllChem
m = Chem.MolFromSmiles("O=C(C)c1cccnc1", False)
AllChem.SanitizeMMFFMol(m)
m2 = Chem.AddHs(m)
AllChem.EmbedMolecule(m2)
pyMP = AllChem.SetupMMFFForceField(m2)
pyMP.SetMMFFVariant("MMFF94s")
pyMP.SetMMFFDielectricModel(2)
pyFF = AllChem.MMFFGetMoleculeForceField(m2, pyMP)
pyFF.Minimize()
print >>file('structure_min.sdf','w'), Chem.MolToMolBlock(m2)
print 'Energy = {0:12.4f}'.format(pyFF.CalcEnergy())
i = 0
for i in range(0, m2.GetNumAtoms()):
print '{0:4d} {1:4d} {2:8.4f} {3:8.4f}'.format(i + 1,
int(pyMP.GetMMFFAtomType(i)),
float(pyMP.GetMMFFFormalCharge(i)),
float(pyMP.GetMMFFPartialCharge(i)))
- OOP backport to UFF. I added the inversion term to the UFF
implementation following the original UFF paper by Rappe'. I have already
modified the figures in a couple of test files to reflect the new energy
values.
- 2-bit neighbor matrix and graph-based angle enumeration now reflect
the MMFF implementation.