File: rdMolAlign.cpp

package info (click to toggle)
rdkit 201403-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 62,288 kB
  • ctags: 15,156
  • sloc: cpp: 125,376; python: 55,674; java: 4,831; ansic: 4,178; xml: 2,499; sql: 1,775; yacc: 1,551; lex: 1,051; makefile: 353; fortran: 183; sh: 148; cs: 93
file content (567 lines) | stat: -rw-r--r-- 25,009 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
// $Id$
//
//  Copyright (C) 2004-2008 Greg Landrum and Rational Discovery LLC
//
//  Copyright (C) 2013 Paolo Tosco
//
//   @@ All Rights Reserved @@
//  This file is part of the RDKit.
//  The contents are covered by the terms of the BSD license
//  which is included in the file license.txt, found at the root
//  of the RDKit source tree.
//
#define PY_ARRAY_UNIQUE_SYMBOL rdmolalign_array_API
#include <boost/python.hpp>
#include <boost/python/numeric.hpp>
#include "numpy/arrayobject.h"
#include <GraphMol/MolAlign/AlignMolecules.h>
#include <GraphMol/MolAlign/O3AAlignMolecules.h>
#include <ForceField/Wrap/PyForceField.h>
#include <GraphMol/ForceFieldHelpers/MMFF/AtomTyper.h>
#include <GraphMol/Descriptors/Crippen.h>
#include <RDBoost/PySequenceHolder.h>
#include <RDBoost/Wrap.h>
#include <GraphMol/ROMol.h>


namespace python = boost::python;

namespace RDKit {
  MatchVectType *_translateAtomMap(python::object atomMap) {
    PySequenceHolder<python::object> aMapSeq(atomMap);
    MatchVectType *aMap;
    aMap = 0;
    unsigned int i, nAtms = aMapSeq.size();
    if (nAtms > 0) {
      aMap = new MatchVectType;
      for (i = 0; i < nAtms; ++i) {
        PySequenceHolder<int> item(aMapSeq[i]);
        if (item.size() != 2) {
          delete aMap;
          aMap = 0;
          throw_value_error("Incorrect format for atomMap");
        }
        aMap->push_back(std::pair<int, int>(item[0], item[1]));
      }
    }
    return aMap;
  }

  RDNumeric::DoubleVector *_translateWeights(python::object weights) {
    PySequenceHolder<double> wts(weights);
    unsigned int nwts = wts.size();
    RDNumeric::DoubleVector *wtsVec;
    wtsVec = 0;
    unsigned int i;
    if (nwts > 0) {
      wtsVec = new RDNumeric::DoubleVector(nwts);
      for ( i = 0; i < nwts; i++) {
        wtsVec->setVal(i, wts[i]);
      }
    }
    return wtsVec;
  }

  std::vector<unsigned int>* _translateIds(python::object ids) {
    PySequenceHolder<unsigned int> idsSeq(ids);
    std::vector<unsigned int>* ivec = 0;
    if (idsSeq.size() > 0) {
      ivec = new std::vector<unsigned int>;
      for(unsigned int i = 0; i < idsSeq.size(); ++i) {
        ivec->push_back(idsSeq[i]);
      }
    }
    return ivec;
  }

  void alignMolConfs(ROMol &mol, python::object atomIds=python::list(),
                     python::object confIds=python::list(), 
                     python::object weights=python::list(),
                     bool reflect=false, unsigned int maxIters=50) {
    
    RDNumeric::DoubleVector *wtsVec = _translateWeights(weights);
    std::vector<unsigned int> *aIds = _translateIds(atomIds);
    std::vector<unsigned int> *cIds = _translateIds(confIds);
    MolAlign::alignMolConformers(mol, aIds, cIds, wtsVec, reflect, maxIters);
    if (wtsVec) {
      delete wtsVec;
    }
    if (aIds) {
      delete aIds;
    }
    if (cIds) {
      delete cIds;
    }
  }
    
  PyObject *generateRmsdTransPyTuple(double rmsd, RDGeom::Transform3D &trans) {
    npy_intp dims[2];
    dims[0] = 4;
    dims[1] = 4;
    PyArrayObject *res = (PyArrayObject *)PyArray_SimpleNew(2, dims, NPY_DOUBLE);
    double *resData=reinterpret_cast<double *>(res->data);
    unsigned int i, j, itab;
    const double *tdata = trans.getData();
    for (i = 0; i < trans.numRows(); ++i) {
      itab = i * 4;
      for (j = 0; j < trans.numRows(); ++j) {
        resData[itab + j] = tdata[itab + j];
      }
    }
    PyObject *resTup = PyTuple_New(2);
    PyObject *rmsdItem = PyFloat_FromDouble(rmsd);
    PyTuple_SetItem(resTup, 0, rmsdItem);
    PyTuple_SetItem(resTup, 1, PyArray_Return(res));
    return resTup;
  }

  PyObject* getMolAlignTransform(const ROMol &prbMol, const ROMol &refMol,
                                      int prbCid=-1, int refCid=-1, 
                                      python::object atomMap=python::list(),
                                      python::object weights=python::list(),
                                      bool reflect=false, unsigned int maxIters=50) {
    MatchVectType *aMap = _translateAtomMap(atomMap);
    unsigned int nAtms;
    if (aMap) {
      nAtms = aMap->size();
    } else {
      nAtms = prbMol.getNumAtoms();
    }
    RDNumeric::DoubleVector *wtsVec = _translateWeights(weights);
    if (wtsVec) {
      if (wtsVec->size() != nAtms) {
        throw_value_error("Incorrect number of weights specified");
      }
    }
    RDGeom::Transform3D trans;
    double rmsd = MolAlign::getAlignmentTransform(prbMol, refMol, trans, prbCid, refCid, aMap, 
                                                  wtsVec, reflect, maxIters);
    if (aMap) {
      delete aMap;
    } 
    if (wtsVec) {
      delete wtsVec;
    }

    return generateRmsdTransPyTuple(rmsd, trans);
  }

  double AlignMolecule(ROMol &prbMol, const ROMol &refMol,
                       int prbCid=-1, int refCid=-1, 
                       python::object atomMap=python::list(),
                       python::object weights=python::list(),
                       bool reflect=false, unsigned int maxIters=50) {
    MatchVectType *aMap = _translateAtomMap(atomMap);
    unsigned int nAtms;
    if (aMap) {
      nAtms = aMap->size();
    } else {
      nAtms = prbMol.getNumAtoms();
    }
    RDNumeric::DoubleVector *wtsVec = _translateWeights(weights);
    if (wtsVec) {
      if (wtsVec->size() != nAtms) {
        throw_value_error("Incorrect number of weights specified");
      }
    }
    double rmsd = MolAlign::alignMol(prbMol, refMol, prbCid, refCid, aMap, 
                                     wtsVec, reflect, maxIters);
    if (aMap) {
      delete aMap;
    } 
    if (wtsVec) {
      delete wtsVec;
    }
    return rmsd;
  }
  
  namespace MolAlign {
    class PyO3A {
    public:
      PyO3A(O3A *o) : o3a(o) {};
      ~PyO3A() {};
      double align() {
        return o3a.get()->align();
      };
      PyObject *trans() {
        RDGeom::Transform3D trans;
        double rmsd = o3a.get()->trans(trans);
        return RDKit::generateRmsdTransPyTuple(rmsd, trans);
      };
      double score() {
        return o3a.get()->score();
      };
      boost::python::list matches() {
        boost::python::list matchList;
        const RDKit::MatchVectType *o3aMatchVect = o3a->matches();
        
        for (unsigned int i = 0; i < o3aMatchVect->size(); ++i) {
          boost::python::list match;
          match.append((*o3aMatchVect)[i].first);
          match.append((*o3aMatchVect)[i].second);
          matchList.append(match);
        }
        
        return matchList;
      };
      boost::python::list weights() {
        boost::python::list weightList;
        const RDNumeric::DoubleVector *o3aWeights = o3a->weights();
        
        for (unsigned int i = 0; i < o3aWeights->size(); ++i) {
          weightList.append((*o3aWeights)[i]);
        }
        
        return weightList;
      };
      boost::shared_ptr<O3A> o3a;
    };
    PyO3A *getMMFFO3A(ROMol &prbMol, ROMol &refMol,
                  python::object prbProps,
                  python::object refProps,
                  int prbCid = -1, int refCid = -1, bool reflect = false,
                  unsigned int maxIters = 50, unsigned int options = 0,
                  python::list constraintMap = python::list(),
                  python::list constraintWeights = python::list())
    {
      MatchVectType *cMap = (python::len(constraintMap)
        ? _translateAtomMap(constraintMap) : NULL);
      RDNumeric::DoubleVector *cWts = NULL;
      if (cMap) {
        cWts = _translateWeights(constraintWeights);
        if (cWts) {
          if ((*cMap).size() != (*cWts).size()) {
            throw_value_error("The number of weights should match the number of constraints");
          }
        }
        for (unsigned int i = 0; i < (*cMap).size(); ++i) {
          if (((*cMap)[i].first < 0) || ((*cMap)[i].first >= prbMol.getNumAtoms())
            || ((*cMap)[i].second < 0) || ((*cMap)[i].second >= refMol.getNumAtoms())) {
            throw_value_error("Constrained atom idx out of range");
          }
          if ((prbMol[(*cMap)[i].first]->getAtomicNum() == 1)
            || (refMol[(*cMap)[i].second]->getAtomicNum() == 1)) {
            throw_value_error("Constrained atoms must be heavy atoms");
          }
        }
      }
      ForceFields::PyMMFFMolProperties *prbPyMMFFMolProperties=NULL;
      MMFF::MMFFMolProperties *prbMolProps=NULL;
      ForceFields::PyMMFFMolProperties *refPyMMFFMolProperties=NULL;
      MMFF::MMFFMolProperties *refMolProps=NULL;
      
      if(prbProps != python::object()){
        prbPyMMFFMolProperties=python::extract<ForceFields::PyMMFFMolProperties *>(prbProps);
        prbMolProps=prbPyMMFFMolProperties->mmffMolProperties.get();
      } else {
        prbMolProps = new MMFF::MMFFMolProperties(prbMol);
        if(!prbMolProps->isValid()){
          throw_value_error("missing MMFF94 parameters for probe molecule");
        }
      }
      if(refProps != python::object()){
        refPyMMFFMolProperties=python::extract<ForceFields::PyMMFFMolProperties *>(refProps);
        refMolProps=refPyMMFFMolProperties->mmffMolProperties.get();
      } else {
        refMolProps = new MMFF::MMFFMolProperties(refMol);
        if(!refMolProps->isValid()){
          throw_value_error("missing MMFF94 parameters for reference molecule");
        }
      }
      O3A *o3a = new MolAlign::O3A(prbMol, refMol, prbMolProps,refMolProps,
                                   MolAlign::O3A::MMFF94, prbCid, refCid,
                                   reflect, maxIters, options, cMap, cWts);
      PyO3A *pyO3A = new PyO3A(o3a);

      if(!prbPyMMFFMolProperties) delete prbMolProps;
      if(!refPyMMFFMolProperties) delete refMolProps;
      if (cMap) {
        delete cMap;
      } 
      if (cWts) {
        delete cWts;
      }
      
      return pyO3A;
    }
    PyO3A *getCrippenO3A(ROMol &prbMol, ROMol &refMol,
                  python::list prbCrippenContribs,
                  python::list refCrippenContribs,
                  int prbCid = -1, int refCid = -1, bool reflect = false,
                  unsigned int maxIters = 50, unsigned int options = 0,
                  python::list constraintMap = python::list(),
                  python::list constraintWeights = python::list())
    {
      MatchVectType *cMap = (python::len(constraintMap)
        ? _translateAtomMap(constraintMap) : NULL);
      RDNumeric::DoubleVector *cWts = NULL;
      if (cMap) {
        cWts = _translateWeights(constraintWeights);
        if (cWts) {
          if ((*cMap).size() != (*cWts).size()) {
            throw_value_error("The number of weights should match the number of constraints");
          }
        }
        for (unsigned int i = 0; i < (*cMap).size(); ++i) {
          if (((*cMap)[i].first < 0) || ((*cMap)[i].first >= prbMol.getNumAtoms())
            || ((*cMap)[i].second < 0) || ((*cMap)[i].second >= refMol.getNumAtoms())) {
            throw_value_error("Constrained atom idx out of range");
          }
          if ((prbMol[(*cMap)[i].first]->getAtomicNum() == 1)
            || (refMol[(*cMap)[i].second]->getAtomicNum() == 1)) {
            throw_value_error("Constrained atoms must be heavy atoms");
          }
        }
      }
      unsigned int prbNAtoms = prbMol.getNumAtoms();
      std::vector<double> prbLogpContribs(prbNAtoms);
      unsigned int refNAtoms = refMol.getNumAtoms();
      std::vector<double> refLogpContribs(refNAtoms);
      
      if ((prbCrippenContribs != python::list())
        && (python::len(prbCrippenContribs) == prbNAtoms)) {
        for (unsigned int i = 0; i < prbNAtoms; ++i) {
          python::tuple logpMRTuple = python::extract<python::tuple>(prbCrippenContribs[i]);
          prbLogpContribs[i] = python::extract<double>(logpMRTuple[0]);
        }
      }
      else {
        std::vector<double> prbMRContribs(prbNAtoms);
        std::vector<unsigned int> prbAtomTypes(prbNAtoms);
        std::vector<std::string> prbAtomTypeLabels(prbNAtoms);
        Descriptors::getCrippenAtomContribs(prbMol, prbLogpContribs,
          prbMRContribs, true, &prbAtomTypes, &prbAtomTypeLabels);
      }
      if ((refCrippenContribs != python::list())
        && (python::len(refCrippenContribs) == refNAtoms)) {
        for (unsigned int i = 0; i < refNAtoms; ++i) {
          python::tuple logpMRTuple = python::extract<python::tuple>(refCrippenContribs[i]);
          refLogpContribs[i] = python::extract<double>(logpMRTuple[0]);
        }
      }
      else {
        std::vector<double> refMRContribs(refNAtoms);
        std::vector<unsigned int> refAtomTypes(refNAtoms);
        std::vector<std::string> refAtomTypeLabels(refNAtoms);
        Descriptors::getCrippenAtomContribs(refMol, refLogpContribs,
          refMRContribs, true, &refAtomTypes, &refAtomTypeLabels);
      }
      O3A *o3a = new MolAlign::O3A(prbMol, refMol, &prbLogpContribs, &refLogpContribs,
                                   MolAlign::O3A::CRIPPEN, prbCid, refCid,
                                   reflect, maxIters, options, cMap, cWts);
      PyO3A *pyO3A = new PyO3A(o3a);
      if (cMap) {
        delete cMap;
      } 
      if (cWts) {
        delete cWts;
      }
      
      return pyO3A;
    }
  }
}

BOOST_PYTHON_MODULE(rdMolAlign) {
  import_array();
  python::scope().attr("__doc__") =
    "Module containing functions to align a molecule to a second molecule";
    
  std::string docString = "Compute the transformation required to align a molecule\n\
     \n\
      The 3D transformation required to align the specied conformation in the probe molecule\n\
      to a specified conformation in the reference molecule is computed so that the root mean\n\
      squared distance between a specified set of atoms is minimized\n\
     \n\
     ARGUMENTS\n\
      - prbMol    molecule that is to be aligned\n\
      - refMol    molecule used as the reference for the alignment\n\
      - prbCid    ID of the conformation in the probe to be used \n\
                       for the alignment (defaults to first conformation)\n\
      - refCid    ID of the conformation in the ref molecule to which \n\
                       the alignment is computed (defaults to first conformation)\n\
      - atomMap   a vector of pairs of atom IDs (probe AtomId, ref AtomId)\n\
                       used to compute the alignments. If this mapping is \n\
                       not specified an attempt is made to generate on by\n\
                       substructure matching\n\
      - weights   Optionally specify weights for each of the atom pairs\n\
      - reflect   if true reflect the conformation of the probe molecule\n\
      - maxIters  maximum number of iterations used in mimizing the RMSD\n\
       \n\
      RETURNS\n\
      a tuple of (RMSD value, transform matrix) \n\
    \n";
  python::def("GetAlignmentTransform", RDKit::getMolAlignTransform,
              (python::arg("prbMol"), python::arg("refMol"),
               python::arg("prbCid")=-1,python::arg("refCid")=-1,
               python::arg("atomMap")=python::list(), python::arg("weights")=python::list(),
               python::arg("reflect")=false, python::arg("maxIters")=50),
              docString.c_str());

  docString = "Optimally (minimum RMSD) align a molecule to another molecule\n\
     \n\
      The 3D transformation required to align the specied conformation in the probe molecule\n\
      to a specified conformation in the reference molecule is computed so that the root mean\n\
      squared distance between a specified set of atoms is minimized. \n\
      This transform is then applied to the specified conformation in the probe molecule\n\
     \n\
     ARGUMENTS\n\
      - prbMol    molecule that is to be aligned\n\
      - refMol    molecule used as the reference for the alignment\n\
      - prbCid    ID of the conformation in the probe to be used \n\
                       for the alignment (defaults to first conformation)\n\
      - refCid    ID of the conformation in the ref molecule to which \n\
                       the alignment is computed (defaults to first conformation)\n\
      - atomMap   a vector of pairs of atom IDs (probe AtomId, ref AtomId)\n\
                       used to compute the alignments. If this mapping is \n\
                       not specified an attempt is made to generate on by\n\
                       substructure matching\n\
      - weights   Optionally specify weights for each of the atom pairs\n\
      - reflect   if true reflect the conformation of the probe molecule\n\
      - maxIters  maximum number of iterations used in mimizing the RMSD\n\
       \n\
      RETURNS\n\
      RMSD value\n\
    \n";
  python::def("AlignMol", RDKit::AlignMolecule,
              (python::arg("prbMol"), python::arg("refMol"),
               python::arg("prbCid")=-1,python::arg("refCid")=-1,
               python::arg("atomMap")=python::list(), python::arg("weights")=python::list(),
               python::arg("reflect")=false, python::arg("maxIters")=50),
              docString.c_str());

  docString = "Alignment conformations in a molecule to each other\n\
     \n\
      The first conformation in the molecule is used as the reference\n\
     \n\
     ARGUMENTS\n\
      - mol       molecule of interest\n\
      - atomIds   List of atom ids to use a points for alingment - defaults to all atoms\n\
      - confIds   Ids of conformations to align - defaults to all conformers \n\
      - weights   Optionally specify weights for each of the atom pairs\n\
      - reflect   if true reflect the conformation of the probe molecule\n\
      - maxIters  maximum number of iterations used in mimizing the RMSD\n\
       \n\
      RETURNS\n\
      RMSD value\n\
    \n";
  python::def("AlignMolConformers", RDKit::alignMolConfs,
              (python::arg("mol"), python::arg("atomIds")=python::list(), 
               python::arg("confIds")=python::list(),
               python::arg("weights")=python::list(),
               python::arg("reflect")=false, python::arg("maxIters")=50),
              docString.c_str());

  docString = "Perform a random transformation on a molecule\n\
     \n\
     ARGUMENTS\n\
      - mol    molecule that is to be transformed\n\
      - cid    ID of the conformation in the mol to be transformed\n\
               (defaults to first conformation)\n\
      - seed   seed used to initialize the random generator\n\
               (defaults to -1, that is no seeding)\n\
       \n\
    \n";
  python::def("RandomTransform", RDKit::MolAlign::randomTransform,
              (python::arg("mol"), python::arg("cid")=-1,
               python::arg("seed")=-1),docString.c_str());

  python::class_<RDKit::MolAlign::PyO3A>("O3A","Open3DALIGN object",python::no_init)
    .def("Align",&RDKit::MolAlign::PyO3A::align, (python::arg("self")),
	 "aligns probe molecule onto reference molecule")
    .def("Trans",&RDKit::MolAlign::PyO3A::trans, (python::arg("self")),
	 "returns the transformation which aligns probe molecule onto reference molecule")
    .def("Score",&RDKit::MolAlign::PyO3A::score, (python::arg("self")),
	 "returns the O3AScore of the alignment")
    .def("Matches",&RDKit::MolAlign::PyO3A::matches, (python::arg("self")),
	 "returns the AtomMap as found by Open3DALIGN")
    .def("Weights",&RDKit::MolAlign::PyO3A::weights, (python::arg("self")),
	 "returns the weight vector as found by Open3DALIGN")
    ;
  docString = "Get an O3A object with atomMap and weights vectors to overlay\n\
      the probe molecule onto the reference molecule based on\n\
      MMFF atom types and charges\n\
     \n\
     ARGUMENTS\n\
      - prbMol                   molecule that is to be aligned\n\
      - refMol                   molecule used as the reference for the alignment\n\
      - prbPyMMFFMolProperties   PyMMFFMolProperties object for the probe molecule as returned\n\
                                 by SetupMMFFForceField()\n\
      - refPyMMFFMolProperties   PyMMFFMolProperties object for the reference molecule as returned\n\
                                 by SetupMMFFForceField()\n\
      - prbCid                   ID of the conformation in the probe to be used \n\
                                 for the alignment (defaults to first conformation)\n\
      - refCid                   ID of the conformation in the ref molecule to which \n\
                                 the alignment is computed (defaults to first conformation)\n\
      - reflect                  if true reflect the conformation of the probe molecule\n\
                                 (defaults to false)\n\
      - maxIters                 maximum number of iterations used in mimizing the RMSD\n\
                                 (defaults to 50)\n\
      - options                  least 2 significant bits encode accuracy\n\
                                 (0: maximum, 3: minimum; defaults to 0)\n\
                                 bit 3 triggers local optimization of the alignment\n\
                                 (no computation of the cost matrix; defaults: off)\n\
      - constraintMap            a vector of pairs of atom IDs (probe AtomId, ref AtomId)\n\
                                 which shall be used for the alignment (defaults to [])\n\
      - constraintWeights        optionally specify weights for each of the constraints\n\
                                 (weights default to 100.0)\n\
       \n\
      RETURNS\n\
      RMSD value\n\
    \n";
  python::def("GetO3A", RDKit::MolAlign::getMMFFO3A,
              (python::arg("prbMol"), python::arg("refMol"),
               python::arg("prbPyMMFFMolProperties") = python::object(),
               python::arg("refPyMMFFMolProperties") = python::object(),
               python::arg("prbCid") = -1, python::arg("refCid") = -1,
               python::arg("reflect") = false, python::arg("maxIters") = 50,
               python::arg("options") = 0,
               python::arg("constraintMap") = python::list(),
               python::arg("constraintWeights") = python::list()),
               python::return_value_policy<python::manage_new_object>(),
               docString.c_str());
  docString = "Get an O3A object with atomMap and weights vectors to overlay\n\
      the probe molecule onto the reference molecule based on\n\
      Crippen logP atom contributions\n\
     \n\
     ARGUMENTS\n\
      - prbMol                   molecule that is to be aligned\n\
      - refMol                   molecule used as the reference for the alignment\n\
      - prbCrippenContribs       Crippen atom contributions for the probe molecule\n\
                                 as a list of (logp, mr) tuples, as returned\n\
                                 by _CalcCrippenContribs()\n\
      - refCrippenContribs       Crippen atom contributions for the reference molecule\n\
                                 as a list of (logp, mr) tuples, as returned\n\
                                 by _CalcCrippenContribs()\n\
      - prbCid                   ID of the conformation in the probe to be used \n\
                                 for the alignment (defaults to first conformation)\n\
      - refCid                   ID of the conformation in the ref molecule to which \n\
                                 the alignment is computed (defaults to first conformation)\n\
      - reflect                  if true reflect the conformation of the probe molecule\n\
                                 (defaults to false)\n\
      - maxIters                 maximum number of iterations used in mimizing the RMSD\n\
                                 (defaults to 50)\n\
      - options                  least 2 significant bits encode accuracy\n\
                                 (0: maximum, 3: minimum; defaults to 0)\n\
                                 bit 3 triggers local optimization of the alignment\n\
                                 (no computation of the cost matrix; defaults: off)\n\
      - constraintMap            a vector of pairs of atom IDs (probe AtomId, ref AtomId)\n\
                                 which shall be used for the alignment (defaults to [])\n\
      - constraintWeights        optionally specify weights for each of the constraints\n\
                                 (weights default to 100.0)\n\
       \n\
      RETURNS\n\
      RMSD value\n\
    \n";
  python::def("GetCrippenO3A", RDKit::MolAlign::getCrippenO3A,
              (python::arg("prbMol"), python::arg("refMol"),
               python::arg("prbCrippenContribs") = python::list(),
               python::arg("refCrippenContribs") = python::list(),
               python::arg("prbCid") = -1, python::arg("refCid") = -1,
               python::arg("reflect") = false, python::arg("maxIters") = 50,
               python::arg("options") = 0,
               python::arg("constraintMap") = python::list(),
               python::arg("constraintWeights") = python::list()),
               python::return_value_policy<python::manage_new_object>(),
               docString.c_str());
}