00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "py_commands.h"
00021 #include "AtomSel.h"
00022 #include "VMDApp.h"
00023 #include "MoleculeList.h"
00024 #include "SymbolTable.h"
00025 #include "Measure.h"
00026 #include "SpatialSearch.h"
00027
00028
00029 #if PY_VERSION_HEX < ((2<<24)|(5<<16))
00030 typedef int Py_ssize_t;
00031 #endif
00032
00033 typedef struct {
00034 PyObject_HEAD
00035 AtomSel *atomSel;
00036 VMDApp *app;
00037 } PyAtomSelObject;
00038
00039
00040 static int atomsel_Check(PyObject *obj);
00041
00042
00043 DrawMolecule *get_molecule(PyAtomSelObject *a) {
00044 int molid = a->atomSel->molid();
00045 DrawMolecule *mol = a->app->moleculeList->mol_from_id(molid);
00046 if (!mol) {
00047 PyErr_SetString(PyExc_ValueError, "invalid molid");
00048 }
00049 return mol;
00050 }
00051
00052 static char *atomsel_doc = (char *)
00053 "atomsel( selection, molid = top, frame = now) -> new selection object\n"
00054 ;
00055
00056 static void
00057 atomsel_dealloc( PyAtomSelObject *obj ) {
00058 delete obj->atomSel;
00059 obj->ob_type->tp_free((PyObject *)obj);
00060 }
00061
00062 static PyObject *
00063 atomsel_repr( PyAtomSelObject *obj ) {
00064 AtomSel *sel = obj->atomSel;
00065 char *s = new char[strlen(sel->cmdStr) + 100];
00066 sprintf(s, "atomsel('%s', molid=%d, frame=%d)",
00067 sel->cmdStr, sel->molid(), sel->which_frame);
00068 PyObject *result = PyString_FromString(s);
00069 delete [] s;
00070 return result;
00071 }
00072
00073 static PyObject *
00074 atomsel_str( PyAtomSelObject *obj ) {
00075 return PyString_FromString(obj->atomSel->cmdStr);
00076 }
00077
00078
00079 static PyObject *
00080 atomsel_new( PyTypeObject *type, PyObject *args, PyObject *kwds ) {
00081
00082 const char *sel = "all";
00083 int molid = -1;
00084 int frame = -1;
00085
00086 static char *kwlist[] = { (char *)"selection", (char *)"molid",
00087 (char *)"frame", NULL };
00088
00089 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sii", kwlist,
00090 &sel, &molid, &frame))
00091 return NULL;
00092
00093 VMDApp *app = get_vmdapp();
00094 if (molid < 0) molid = app->molecule_top();
00095 DrawMolecule *mol = app->moleculeList->mol_from_id(molid);
00096 if (!mol) {
00097 PyErr_SetString(PyExc_ValueError, "invalid molid");
00098 return NULL;
00099 }
00100
00101 AtomSel *atomSel = new AtomSel(app->atomSelParser, mol->id());
00102 atomSel->which_frame = frame;
00103 int parse_result;
00104
00105 Py_BEGIN_ALLOW_THREADS
00106 parse_result = atomSel->change(sel, mol);
00107 Py_END_ALLOW_THREADS
00108
00109 if (parse_result == AtomSel::NO_PARSE) {
00110 PyErr_SetString(PyExc_ValueError, "cannot parse atom selection text");
00111 delete atomSel;
00112 return NULL;
00113 }
00114
00115 PyObject *obj = type->tp_alloc(type, 0);
00116 if (obj == NULL) {
00117 delete atomSel;
00118 return NULL;
00119 }
00120 ((PyAtomSelObject *)obj)->app = app;
00121 ((PyAtomSelObject *)obj)->atomSel = atomSel;
00122 return obj;
00123 }
00124
00125 static char *get_doc = (char *)
00126 "get( attrib ) -> corresponding attrib values for selected atoms."
00127 ;
00128
00129 static PyObject *atomsel_get(PyAtomSelObject *a, PyObject *keyobj) {
00130
00131
00132
00133 VMDApp *app = a->app;
00134 AtomSel *atomSel = a->atomSel;
00135 const int num_atoms = atomSel->num_atoms;
00136 DrawMolecule *mol;
00137 if (!(mol = get_molecule(a))) return NULL;
00138
00139 const char *attr = PyString_AsString(keyobj);
00140 if (!attr) return NULL;
00141
00142
00143
00144
00145 SymbolTable *table = app->atomSelParser;
00146 int attrib_index = table->find_attribute(attr);
00147 if (attrib_index == -1) {
00148 PyErr_SetString(PyExc_ValueError, "unknown atom attribute");
00149 return NULL;
00150 }
00151 SymbolTableElement *elem = table->fctns.data(attrib_index);
00152 if (elem->is_a != SymbolTableElement::KEYWORD &&
00153 elem->is_a != SymbolTableElement::SINGLEWORD) {
00154 PyErr_SetString(PyExc_ValueError, "attribute is not a keyword or singleword");
00155 return NULL;
00156 }
00157
00158
00159
00160
00161
00162 int *flgs = atomSel->on;
00163 atomsel_ctxt context(table, mol, atomSel->which_frame, attr);
00164 PyObject *newlist = PyList_New(atomSel->selected);
00165
00166 if (elem->is_a == SymbolTableElement::SINGLEWORD) {
00167 int *tmp = new int[num_atoms];
00168 memcpy(tmp, atomSel->on, num_atoms*sizeof(int));
00169 elem->keyword_single(&context, num_atoms, tmp);
00170 int j=0;
00171 for (int i=0; i<num_atoms; i++) {
00172 if (flgs[i]) {
00173 PyObject *val = tmp[i] ? Py_True : Py_False;
00174 Py_INCREF(val);
00175 PyList_SET_ITEM(newlist, j++, val);
00176 }
00177 }
00178 delete [] tmp;
00179 } else {
00180 switch(table->fctns.data(attrib_index)->returns_a) {
00181 case (SymbolTableElement::IS_STRING):
00182 {
00183 const char **tmp= new const char *[num_atoms];
00184 elem->keyword_string(&context, num_atoms, tmp, flgs);
00185 int j=0;
00186 for (int i=0; i<num_atoms; i++) {
00187 if (flgs[i]) {
00188 PyList_SET_ITEM(newlist, j++, PyString_FromString(tmp[i]));
00189 }
00190 }
00191 delete [] tmp;
00192 }
00193 break;
00194 case (SymbolTableElement::IS_INT):
00195 {
00196 int *tmp = new int[num_atoms];
00197 elem->keyword_int(&context, num_atoms, tmp, flgs);
00198 int j=0;
00199 for (int i=0; i<num_atoms; i++) {
00200 if (flgs[i]) {
00201 PyList_SET_ITEM(newlist, j++, PyInt_FromLong(tmp[i]));
00202 }
00203 }
00204 delete [] tmp;
00205 }
00206 break;
00207 case (SymbolTableElement::IS_FLOAT):
00208 {
00209 double *tmp = new double[num_atoms];
00210 elem->keyword_double(&context, num_atoms, tmp, flgs);
00211 int j=0;
00212 for (int i=0; i<num_atoms; i++) {
00213 if (flgs[i])
00214 PyList_SET_ITEM(newlist, j++, PyFloat_FromDouble(tmp[i]));
00215 }
00216 delete [] tmp;
00217 }
00218 break;
00219 }
00220 }
00221 return newlist;
00222 }
00223
00224 static char *set_doc = (char *)
00225 "set( attrib, val ) -> set attrib values for selected atoms.\n"
00226 " val must be either a single value, or a sequence of values, one for\n"
00227 " each atom in selection.\n" ;
00228
00229 static PyObject *atomsel_set(PyAtomSelObject *a, PyObject *args) {
00230
00231
00232
00233 VMDApp *app = a->app;
00234 AtomSel *atomSel = a->atomSel;
00235 const int num_atoms = atomSel->num_atoms;
00236 DrawMolecule *mol;
00237 if (!(mol = get_molecule(a))) return NULL;
00238
00239 const char *attr;
00240 PyObject *val;
00241 if (!PyArg_ParseTuple(args, "sO", &attr, &val)) return NULL;
00242
00243
00244
00245
00246 SymbolTable *table = app->atomSelParser;
00247 int attrib_index = table->find_attribute(attr);
00248 if (attrib_index == -1) {
00249 PyErr_SetString(PyExc_ValueError, "unknown atom attribute");
00250 return NULL;
00251 }
00252 SymbolTableElement *elem = table->fctns.data(attrib_index);
00253 if (elem->is_a != SymbolTableElement::KEYWORD &&
00254 elem->is_a != SymbolTableElement::SINGLEWORD) {
00255 PyErr_SetString(PyExc_ValueError, "attribute is not a keyword or singleword");
00256 return NULL;
00257 }
00258 if (!table->is_changeable(attrib_index)) {
00259 PyErr_SetString(PyExc_ValueError, "attribute is not modifiable");
00260 return NULL;
00261 }
00262
00263
00264
00265
00266
00267
00268 const int num_selected = atomSel->selected;
00269 PyObject *fastval = NULL;
00270 int nvals = 1;
00271 if (PySequence_Check(val) && !PyString_Check(val)) {
00272 nvals = PySequence_Size(val);
00273 if (nvals != 1 && nvals != num_selected) {
00274 PyErr_SetString(PyExc_ValueError, "wrong number of items");
00275 return NULL;
00276 }
00277 fastval = PySequence_Fast(val, "Invalid values");
00278 if (!fastval) return NULL;
00279 }
00280
00281 int *flgs = atomSel->on;
00282
00283
00284
00285
00286
00287
00288 atomsel_ctxt context(table, mol, atomSel->which_frame, NULL);
00289 if (elem->returns_a == SymbolTableElement::IS_INT) {
00290 int *list = new int[num_atoms];
00291 if (fastval) {
00292 int j=0;
00293 for (int i=0; i<num_atoms; i++) {
00294 if (!flgs[i]) continue;
00295 int ival = (int)PyInt_AsLong(PySequence_Fast_GET_ITEM(fastval, j++));
00296
00297 list[i] = ival;
00298 }
00299 } else {
00300 int ival = (int)PyInt_AsLong(val);
00301
00302 for (int i=0; i<num_atoms; i++) {
00303 if (flgs[i]) list[i] = ival;
00304 }
00305 }
00306 elem->set_keyword_int(&context, num_atoms, list, flgs);
00307 delete [] list;
00308
00309 } else if (elem->returns_a == SymbolTableElement::IS_FLOAT) {
00310 double *list = new double[num_atoms];
00311 if (fastval) {
00312 int j=0;
00313 for (int i=0; i<num_atoms; i++) {
00314 if (!flgs[i]) continue;
00315 float dval = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(fastval, j++));
00316 list[i] = dval;
00317 }
00318 } else {
00319 float dval = (float)PyFloat_AsDouble(val);
00320 for (int i=0; i<num_atoms; i++) {
00321 if (flgs[i]) list[i] = dval;
00322 }
00323 }
00324 elem->set_keyword_double(&context, num_atoms, list, flgs);
00325 delete [] list;
00326
00327
00328 } else if (elem->returns_a == SymbolTableElement::IS_STRING) {
00329 const char **list = new const char *[num_atoms];
00330 if (fastval) {
00331 int j=0;
00332 for (int i=0; i<num_atoms; i++) {
00333 if (!flgs[i]) continue;
00334 const char *sval = PyString_AsString(PySequence_Fast_GET_ITEM(fastval, j++));
00335 list[i] = sval;
00336 }
00337 } else {
00338 const char *sval = PyString_AsString(val);
00339 for (int i=0; i<num_atoms; i++) {
00340 if (flgs[i]) list[i] = sval;
00341 }
00342 }
00343 elem->set_keyword_string(&context, num_atoms, list, flgs);
00344 delete [] list;
00345 }
00346
00347
00348 if (!strcmp(attr, "name") ||
00349 !strcmp(attr, "type") ||
00350 !strcmp(attr, "resname") ||
00351 !strcmp(attr, "chain") ||
00352 !strcmp(attr, "segid") ||
00353 !strcmp(attr, "segname"))
00354 app->moleculeList->add_color_names(mol->id());
00355
00356 mol->force_recalc(DrawMolItem::SEL_REGEN | DrawMolItem::COL_REGEN);
00357
00358 Py_XDECREF(fastval);
00359 Py_INCREF(Py_None);
00360 return Py_None;
00361 }
00362
00363 static PyObject *getframe(PyAtomSelObject *a, void *) {
00364
00365 AtomSel *atomSel = a->atomSel;
00366 DrawMolecule *mol;
00367 if (!(mol = get_molecule(a))) return NULL;
00368
00369 return PyInt_FromLong(atomSel->which_frame);
00370 }
00371
00372 static int setframe(PyAtomSelObject *a, PyObject *frameobj, void *) {
00373
00374 AtomSel *atomSel = a->atomSel;
00375 DrawMolecule *mol;
00376 if (!(mol = get_molecule(a))) return -1;
00377
00378 int frame = PyInt_AsLong(frameobj);
00379 if (frame < 0 && PyErr_Occurred()) return -1;
00380 if (frame != AtomSel::TS_LAST && frame != AtomSel::TS_NOW &&
00381 (frame < 0 || frame >= mol->numframes())) {
00382 PyErr_SetString(PyExc_ValueError, "Invalid frame");
00383 return -1;
00384 }
00385 atomSel->which_frame = frame;
00386 return 0;
00387 }
00388
00389 static char *frame_doc = (char *)
00390 "frame -- the animation frame referenced by the selection.\n"
00391 " Special values: -1 = current frame, -2 = last frame.\n"
00392 "NOTE: changing the frame does not update the selection;\n"
00393 " use update() to do that.\n";
00394
00395 static char *update_doc = (char *)
00396 "update() -> Recompute atoms in selection; if the selection is\n"
00397 "distance-based (e.g., if it uses 'within'), changes to the frame\n"
00398 "will not be reflected in the selected atoms until this method\n"
00399 "is called.\n" ;
00400
00401 static PyObject *py_update(PyAtomSelObject *a) {
00402
00403 AtomSel *atomSel = a->atomSel;
00404 DrawMolecule *mol;
00405 if (!(mol = get_molecule(a))) return NULL;
00406
00407 Py_BEGIN_ALLOW_THREADS
00408 atomSel->change(NULL, mol);
00409 Py_END_ALLOW_THREADS
00410
00411 Py_INCREF(Py_None);
00412 return Py_None;
00413 }
00414
00415 static char *write_doc = (char *)
00416 "write(filetype, filename) -> None\n"
00417 "Write the atoms in the selection to a file of the given type.";
00418
00419 static PyObject *py_write(PyAtomSelObject *a, PyObject *args, PyObject *kwds) {
00420
00421 AtomSel *atomSel = a->atomSel;
00422 DrawMolecule *mol;
00423 if (!(mol = get_molecule(a))) return NULL;
00424 const char *filetype, *filename;
00425 static char *kwlist[] = { (char *)"filetype", (char *)"filename", NULL };
00426 if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
00427 &filetype, &filename))
00428 return NULL;
00429
00430 int frame=-1;
00431 switch (atomSel -> which_frame) {
00432 case AtomSel::TS_NOW: frame = mol->frame(); break;
00433 case AtomSel::TS_LAST: frame = mol->numframes()-1; break;
00434 default: frame = atomSel->which_frame; break;
00435 }
00436 if (frame < 0) frame = 0;
00437 else if (frame >= mol->numframes()) frame = mol->numframes()-1;
00438
00439 FileSpec spec;
00440 spec.first = frame;
00441 spec.last = frame;
00442 spec.stride = 1;
00443 spec.waitfor = -1;
00444 spec.selection = atomSel->on;
00445 int rc;
00446 VMDApp *app = get_vmdapp();
00447 Py_BEGIN_ALLOW_THREADS
00448 rc = app->molecule_savetrajectory(mol->id(), filename, filetype, &spec);
00449 Py_END_ALLOW_THREADS
00450
00451 if (rc < 0) {
00452 PyErr_SetString(PyExc_ValueError, "Unable to write selection to file.");
00453 return NULL;
00454 }
00455 Py_INCREF(Py_None);
00456 return Py_None;
00457 }
00458
00459 static PyObject *getbonds(PyAtomSelObject *a, void *) {
00460
00461 AtomSel *atomSel = a->atomSel;
00462 DrawMolecule *mol;
00463 if (!(mol = get_molecule(a))) return NULL;
00464
00465 PyObject *newlist = PyList_New(atomSel->selected);
00466
00467 int k=0;
00468 for (int i=0; i< atomSel->num_atoms; i++) {
00469 if (!atomSel->on[i]) continue;
00470 const MolAtom *atom = mol->atom(i);
00471 PyObject *bondlist = PyList_New(atom->bonds);
00472 for (int j=0; j<atom->bonds; j++) {
00473 PyList_SET_ITEM(bondlist, j, PyInt_FromLong(atom->bondTo[j]));
00474 }
00475 PyList_SET_ITEM(newlist, k++, bondlist);
00476 }
00477 return newlist;
00478 }
00479
00480 static int setbonds(PyAtomSelObject *a, PyObject *obj, void *) {
00481
00482 AtomSel *atomSel = a->atomSel;
00483 DrawMolecule *mol;
00484 if (!(mol = get_molecule(a))) return -1;
00485
00486 if (!PySequence_Check(obj) || PySequence_Size(obj) != atomSel->selected) {
00487 PyErr_SetString(PyExc_ValueError,
00488 (char *)"setbonds: atomlist and bondlist must have the same size");
00489 return -1;
00490 }
00491 PyObject *fastbonds = PySequence_Fast(obj, "Argument to setbonds must be sequence");
00492 if (!fastbonds) return -1;
00493
00494 int ibond = 0;
00495 mol->force_recalc(DrawMolItem::MOL_REGEN);
00496 for (int i=0; i<atomSel->num_atoms; i++) {
00497 if (!atomSel->on[i]) continue;
00498 MolAtom *atom = mol->atom(i);
00499
00500 PyObject *atomids = PySequence_Fast_GET_ITEM(fastbonds, ibond++);
00501 if (!PyList_Check(atomids)) continue;
00502 int numbonds = PyList_Size(atomids);
00503 int k=0;
00504 for (int j=0; j<numbonds; j++) {
00505 int bond = PyInt_AsLong(PyList_GET_ITEM(atomids, j));
00506 if (bond >= 0 && bond < mol->nAtoms) {
00507 atom->bondTo[k++] = bond;
00508 }
00509 }
00510 atom->bonds = k;
00511 }
00512 Py_DECREF(fastbonds);
00513 return 0;
00514 }
00515
00516 static char *bonds_doc = (char *)
00517 "bonds - for each atom in selection, a list of the indices\n"
00518 " of the bonded atoms.\n";
00519
00520 static PyObject *getmolid(PyAtomSelObject *a, void *) {
00521 AtomSel *atomSel = a->atomSel;
00522 return PyInt_FromLong(atomSel->molid());
00523 }
00524
00525 static char *molid_doc = (char *)
00526 "molid - The id of the molecule this selection is associated with.\n";
00527
00528
00529 static PyGetSetDef atomsel_getset[] = {
00530 { (char *)"frame", (getter)getframe, (setter)setframe, frame_doc, NULL },
00531 { (char *)"bonds", (getter)getbonds, (setter)setbonds, bonds_doc, NULL },
00532 { (char *)"molid", (getter)getmolid, (setter)NULL, molid_doc, NULL },
00533 { NULL },
00534 };
00535
00536
00537
00538 static PyObject *macro(PyObject *self, PyObject *args, PyObject *keywds) {
00539 char *name = NULL, *selection = NULL;
00540 static char *kwlist[] = {
00541 (char *)"name", (char *)"selection", NULL
00542 };
00543 if (!PyArg_ParseTupleAndKeywords(args, keywds, (char *)"|ss:atomsel.macro", kwlist, &name, &selection))
00544 return NULL;
00545
00546 if (selection && !name) {
00547 PyErr_SetString(PyExc_ValueError, (char *)"Must specify name for macro");
00548 return NULL;
00549 }
00550 SymbolTable *table = get_vmdapp()->atomSelParser;
00551 if (!name && !selection) {
00552
00553 PyObject *macrolist = PyList_New(0);
00554 for (int i=0; i<table->num_custom_singleword(); i++) {
00555 const char *s = table->custom_singleword_name(i);
00556 if (s && strlen(s))
00557 PyList_Append(macrolist, PyString_FromString(s));
00558 }
00559 return macrolist;
00560 }
00561 if (name && !selection) {
00562
00563 const char *s = table->get_custom_singleword(name);
00564 if (!s) {
00565 PyErr_SetString(PyExc_ValueError, (char *)"No macro for given name");
00566 return NULL;
00567 }
00568 return PyString_FromString(s);
00569 }
00570
00571 if (!table->add_custom_singleword(name, selection)) {
00572 PyErr_SetString(PyExc_ValueError, (char *)"Unable to create new macro");
00573 return NULL;
00574 }
00575 Py_INCREF(Py_None);
00576 return Py_None;
00577 }
00578
00579
00580 static PyObject *delmacro(PyObject *self, PyObject *args) {
00581 char *name;
00582 if (!PyArg_ParseTuple(args, (char *)"s:atomsel.delmacro", &name))
00583 return NULL;
00584 if (!get_vmdapp()->atomSelParser->remove_custom_singleword(name)) {
00585 PyErr_SetString(PyExc_ValueError, (char *)"Unable to remove macro");
00586 return NULL;
00587 }
00588 Py_INCREF(Py_None);
00589 return Py_None;
00590 }
00591
00592
00593 static int py_get_vector(PyObject *matobj, int n, float *vec) {
00594
00595 if (!PySequence_Check(matobj) || PySequence_Size(matobj) != n) {
00596 PyErr_SetString(PyExc_ValueError, "vector has incorrect size");
00597 return 0;
00598 }
00599 PyObject *fastval = PySequence_Fast(matobj, "Invalid sequence");
00600 if (!fastval) return 0;
00601
00602 for (int i=0; i<n; i++) {
00603 vec[i] = PyFloat_AsDouble(PySequence_Fast_GET_ITEM(fastval, i));
00604 if (PyErr_Occurred()) {
00605 Py_DECREF(fastval);
00606 return 0;
00607 }
00608 }
00609 Py_DECREF(fastval);
00610 return 1;
00611 }
00612
00613 static PyObject *inverse(PyObject *self, PyObject *matobj) {
00614 Matrix4 mat;
00615 if (!py_get_vector(matobj, 16, mat.mat)) return NULL;
00616 if (mat.inverse()) {
00617 PyErr_SetString(PyExc_ValueError, "Matrix is singular.");
00618 return NULL;
00619 }
00620 PyObject *result = PyTuple_New(16);
00621 for (int i=0; i<16; i++) {
00622 PyTuple_SET_ITEM(result, i, PyFloat_FromDouble(mat.mat[i]));
00623 }
00624 return result;
00625 }
00626
00627 #define SYMBOL_TABLE_FUNC(funcname, elemtype) \
00628 static PyObject *funcname(PyObject *self) { \
00629 VMDApp *app = get_vmdapp(); \
00630 PyObject *result = PyList_New(0); \
00631 SymbolTable *table = app->atomSelParser; \
00632 int i, n = table->fctns.num(); \
00633 for (i=0; i<n; i++) \
00634 if (table->fctns.data(i)->is_a == elemtype) \
00635 PyList_Append(result, PyString_FromString(table->fctns.name(i))); \
00636 return result; \
00637 }
00638
00639 SYMBOL_TABLE_FUNC(keywords, SymbolTableElement::KEYWORD)
00640 SYMBOL_TABLE_FUNC(booleans, SymbolTableElement::SINGLEWORD)
00641 SYMBOL_TABLE_FUNC(functions, SymbolTableElement::FUNCTION)
00642 SYMBOL_TABLE_FUNC(stringfunctions, SymbolTableElement::STRINGFCTN)
00643
00644 #undef SYMBOL_TABLE_FUNC
00645
00646
00647
00648 static float *parse_weight(AtomSel *sel, PyObject *wtobj) {
00649 float *weight = new float[sel->selected];
00650 if (!wtobj || wtobj == Py_None) {
00651 for (int i=0; i<sel->selected; i++) weight[i] = 1.0f;
00652 return weight;
00653 }
00654
00655 PyObject *seq = PySequence_Fast(wtobj, (char *)"weight must be a sequence.");
00656 if (!seq) return NULL;
00657 if (PySequence_Size(seq) != sel->selected) {
00658 Py_DECREF(seq);
00659 PyErr_SetString(PyExc_ValueError, "weight must be same size as selection.");
00660 delete [] weight;
00661 return NULL;
00662 }
00663 for (int i=0; i<sel->selected; i++) {
00664 double tmp = PyFloat_AsDouble(PySequence_Fast_GET_ITEM(seq, i));
00665 if (PyErr_Occurred()) {
00666 PyErr_SetString(PyExc_ValueError, "non-floating point value found in weight.");
00667 Py_DECREF(seq);
00668 delete [] weight;
00669 return NULL;
00670 }
00671 weight[i] = (float)tmp;
00672 }
00673 Py_DECREF(seq);
00674 return weight;
00675 }
00676
00677 static char *minmax_doc = (char *)
00678 "minmax() -> (min, max)\n"
00679 "Return minimum and maximum coordinates for selected atoms.";
00680
00681 static PyObject *minmax(PyAtomSelObject *a, PyObject *withradii) {
00682
00683 AtomSel *atomSel = a->atomSel;
00684 DrawMolecule *mol;
00685 if (!(mol = get_molecule(a))) return NULL;
00686 const float *radii = NULL;
00687 if (withradii && PyObject_IsTrue(withradii)) {
00688 radii = mol->extra.data("radius");
00689 }
00690
00691 const float *pos = atomSel->coordinates(a->app->moleculeList);
00692 float min[3], max[3];
00693 int rc = measure_minmax(atomSel->num_atoms, atomSel->on, pos, radii, min, max);
00694 if (rc < 0) {
00695 PyErr_SetString(PyExc_ValueError, measure_error(rc));
00696 return NULL;
00697 }
00698 return Py_BuildValue("[f,f,f],[f,f,f]",
00699 min[0], min[1], min[2], max[0], max[1], max[2]);
00700 }
00701
00702 static char *center_doc = (char *)
00703 "center(weight=None) -> (x, y, z)\n"
00704 "Return a tuple corresponding to the center of the selection,\n"
00705 "optionally weighted by weight.";
00706
00707 static PyObject *center(PyAtomSelObject *a, PyObject *args, PyObject *kwds) {
00708
00709 AtomSel *atomSel = a->atomSel;
00710 DrawMolecule *mol;
00711 if (!(mol = get_molecule(a))) return NULL;
00712 PyObject *weightobj = NULL;
00713 static char *kwlist[] = { "weight", NULL };
00714
00715 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &weightobj))
00716 return NULL;
00717 float *weight = parse_weight(atomSel, weightobj);
00718 if (!weight) return NULL;
00719
00720 float cen[3];
00721
00722 int ret_val = measure_center(atomSel,
00723 atomSel->coordinates(a->app->moleculeList),
00724 weight, cen);
00725 delete [] weight;
00726 if (ret_val < 0) {
00727 PyErr_SetString(PyExc_ValueError, measure_error(ret_val));
00728 return NULL;
00729 }
00730 return Py_BuildValue("(f,f,f)", cen[0], cen[1], cen[2]);
00731 }
00732
00733 static float *parse_two_selections_return_weight(PyAtomSelObject *a,
00734 PyObject *args, AtomSel **othersel) {
00735
00736 PyObject *other;
00737 PyObject *weightobj = NULL;
00738
00739 AtomSel *atomSel = a->atomSel;
00740 DrawMolecule *mol;
00741 if (!(mol = get_molecule(a))) return NULL;
00742
00743 if (!PyArg_ParseTuple(args, "O|O", &other, &weightobj))
00744 return NULL;
00745 float *weight = parse_weight(atomSel, weightobj);
00746 if (!weight) return NULL;
00747 if (!atomsel_Check(other)) return NULL;
00748 AtomSel *sel2 = ((PyAtomSelObject *)other)->atomSel;
00749 if (atomSel->selected != sel2->selected) {
00750 PyErr_SetString(PyExc_ValueError, "Selections must have same number of atoms");
00751 return NULL;
00752 }
00753 *othersel = sel2;
00754 return weight;
00755 }
00756
00757 static char *rmsd_doc = (char *)
00758 "rmsd(sel, weight=None) -> rms distance between selections.\n"
00759 " Selections must have the same number of atoms.\n"
00760 " Weight must be None or list of same size as selections.";
00761
00762 static PyObject *py_rmsd(PyAtomSelObject *a, PyObject *args) {
00763
00764 AtomSel *sel2;
00765 float *weight = parse_two_selections_return_weight(a, args, &sel2);
00766 if (!weight) return NULL;
00767
00768 float rmsd;
00769 int rc = measure_rmsd(a->atomSel, sel2, a->atomSel->selected,
00770 a->atomSel->coordinates(a->app->moleculeList),
00771 sel2->coordinates(a->app->moleculeList),
00772 weight, &rmsd);
00773 delete [] weight;
00774 if (rc < 0) {
00775 PyErr_SetString(PyExc_ValueError, measure_error(rc));
00776 return NULL;
00777 }
00778 return PyFloat_FromDouble(rmsd);
00779 }
00780
00781 static char *fit_doc = (char *)
00782 "fit(sel, weight=None) -> transformation matrix\n"
00783 " Compute and return the transformation matrix for the RMS alignment\n"
00784 " of the selection to sel. The format of the matrix is a 16-element\n"
00785 " tuple suitable for passing to the move() method.\n"
00786 " Weight must be None or list of same size as selections.";
00787
00788 static PyObject *py_fit(PyAtomSelObject *a, PyObject *args) {
00789
00790 AtomSel *sel2;
00791 float *weight = parse_two_selections_return_weight(a, args, &sel2);
00792 if (!weight) return NULL;
00793
00794 Matrix4 mat;
00795 int rc = measure_fit(a->atomSel, sel2,
00796 a->atomSel->coordinates(a->app->moleculeList),
00797 sel2->coordinates(a->app->moleculeList),
00798 weight, NULL, &mat);
00799 delete [] weight;
00800 if (rc < 0) {
00801 PyErr_SetString(PyExc_ValueError, measure_error(rc));
00802 return NULL;
00803 }
00804 PyObject *result = PyTuple_New(16);
00805 for (int i=0; i<16; i++) {
00806 PyTuple_SET_ITEM(result, i, PyFloat_FromDouble(mat.mat[i]));
00807 }
00808 return result;
00809 }
00810
00811 static char *moveby_doc = (char *)
00812 "moveby( vec ) -> shift the selection by the three-element vector vec.";
00813
00814 static PyObject *py_moveby(PyAtomSelObject *a, PyObject *vecobj) {
00815
00816 AtomSel *atomSel = a->atomSel;
00817 DrawMolecule *mol;
00818 if (!(mol = get_molecule(a))) return NULL;
00819
00820 float offset[3];
00821 if (!py_get_vector(vecobj, 3, offset)) return NULL;
00822 float *pos = atomSel->coordinates(a->app->moleculeList);
00823 if (!pos) {
00824 PyErr_SetString(PyExc_ValueError, "No coordinates");
00825 return NULL;
00826 }
00827 for (int i=0; i<atomSel->num_atoms; i++) {
00828 if (atomSel->on[i]) {
00829 vec_add(pos, pos, offset);
00830 }
00831 pos += 3;
00832 }
00833 mol->force_recalc(DrawMolItem::MOL_REGEN);
00834 Py_INCREF(Py_None);
00835 return Py_None;
00836 }
00837
00838 static char *move_doc = (char *)
00839 "move( matrix ) -> apply coordinate transformation to selection.\n"
00840 " matrix should be of the form returned by fit().";
00841
00842 static PyObject *py_move(PyAtomSelObject *a, PyObject *matobj) {
00843
00844 AtomSel *atomSel = a->atomSel;
00845 DrawMolecule *mol;
00846 if (!(mol = get_molecule(a))) return NULL;
00847
00848 Matrix4 mat;
00849 if (!py_get_vector(matobj, 16, mat.mat)) return NULL;
00850
00851 int err;
00852 if ((err = measure_move(
00853 atomSel,
00854 atomSel->coordinates(a->app->moleculeList),
00855 mat)) != MEASURE_NOERR) {
00856 PyErr_SetString(PyExc_ValueError, measure_error(err));
00857 return NULL;
00858 }
00859 mol->force_recalc(DrawMolItem::MOL_REGEN);
00860 Py_INCREF(Py_None);
00861 return Py_None;
00862 }
00863
00864 static char *contacts_doc = (char *)
00865 "contacts(sel, cutoff) -> contact pairs\n"
00866 "Return two lists, whose corresponding elements contain atom indices\n"
00867 "in selection that are within cutoff of sel, but not directly bonded.\n";
00868
00869
00870 static PyObject *contacts(PyAtomSelObject *a, PyObject *args) {
00871
00872 AtomSel *sel1 = a->atomSel;
00873 DrawMolecule *mol;
00874 if (!(mol = get_molecule(a))) return NULL;
00875
00876 PyObject *obj2;
00877 float cutoff;
00878 if (!PyArg_ParseTuple(args, "Of", &obj2, &cutoff))
00879 return NULL;
00880 if (!atomsel_Check(obj2)) return NULL;
00881 if (!(get_molecule((PyAtomSelObject *)obj2))) return NULL;
00882 AtomSel *sel2 = ((PyAtomSelObject *)obj2)->atomSel;
00883
00884 const float *ts1 = sel1->coordinates(a->app->moleculeList);
00885 const float *ts2 = sel2->coordinates(a->app->moleculeList);
00886 if (!ts1 || !ts2) {
00887 PyErr_SetString(PyExc_ValueError, "No coordinates in selection");
00888 return NULL;
00889 }
00890
00891 GridSearchPair *pairlist = vmd_gridsearch3(
00892 ts1, sel1->num_atoms, sel1->on,
00893 ts2, sel2->num_atoms, sel2->on,
00894 cutoff, -1, (sel1->num_atoms + sel2->num_atoms) * 27);
00895
00896 GridSearchPair *p, *tmp;
00897 PyObject *list1 = PyList_New(0);
00898 PyObject *list2 = PyList_New(0);
00899 for (p=pairlist; p != NULL; p=tmp) {
00900
00901 MolAtom *a1 = mol->atom(p->ind1);
00902 if (sel1->molid() != sel2->molid() || !a1->bonded(p->ind2)) {
00903 PyList_Append(list1, PyInt_FromLong(p->ind1));
00904 PyList_Append(list2, PyInt_FromLong(p->ind2));
00905 }
00906 tmp = p->next;
00907 free(p);
00908 }
00909 PyObject *result = PyList_New(2);
00910 PyList_SET_ITEM(result, 0, list1);
00911 PyList_SET_ITEM(result, 1, list2);
00912 return result;
00913 }
00914
00915 static char *sasa_doc = (char *)
00916 "sasa(srad, sel, ... ) -> solvent accessible surface area.\n"
00917 "srad gives solvent radius.\n"
00918 "Optional keyword arguments:\n"
00919 " samples=500 -- specifies number of sample points used per atom.\n"
00920 " points=None -- If points is a list, coordinates of surface points\n"
00921 " will be appended to the list.\n"
00922 " restrict=None -- Pass an atom selection as argument to restrict\n"
00923 " to find contributions coming from just atoms in restrict.\n";
00924
00925 static PyObject *sasa(PyAtomSelObject *a, PyObject *args, PyObject *keywds) {
00926 float srad = 0;
00927 int samples = -1;
00928 const int *sampleptr = NULL;
00929 PyObject *pointsobj = NULL;
00930 PyObject *restrictobj = NULL;
00931
00932 AtomSel *sel = a->atomSel;
00933 DrawMolecule *mol;
00934 if (!(mol = get_molecule(a))) return NULL;
00935
00936 static char *kwlist[] = {
00937 "srad", "samples", "points", "restrict", NULL
00938 };
00939 if (!PyArg_ParseTupleAndKeywords(args, keywds,
00940 "f|iOO:atomsel.sasa", kwlist,
00941 &srad, &samples, &pointsobj, &restrictobj))
00942 return NULL;
00943
00944
00945 if (srad < 0) {
00946 PyErr_SetString(PyExc_ValueError, "atomselect.sasa: srad must be non-negative.");
00947 return NULL;
00948 }
00949
00950
00951 const float *radii = mol->extra.data("radius");
00952 const float *coords = sel->coordinates(a->app->moleculeList);
00953
00954
00955 if (samples > 1) sampleptr = &samples;
00956
00957
00958 AtomSel *restrictsel = NULL;
00959 if (restrictobj) {
00960 if (!atomsel_Check(restrictobj)) return NULL;
00961 if (!get_molecule((PyAtomSelObject *)restrictobj)) return NULL;
00962 restrictsel = ((PyAtomSelObject *)restrictobj)->atomSel;
00963 }
00964
00965
00966 ResizeArray<float> sasapts;
00967 ResizeArray<float> *sasaptsptr = pointsobj ? &sasapts : NULL;
00968
00969
00970 float sasa = 0;
00971 int rc = measure_sasa(sel, coords, radii, srad, &sasa,
00972 sasaptsptr, restrictsel, sampleptr);
00973 if (rc) {
00974 PyErr_SetString(PyExc_ValueError, measure_error(rc));
00975 return NULL;
00976 }
00977
00978
00979 if (pointsobj) {
00980 for (int i=0; i<sasapts.num(); i++) {
00981 PyList_Append(pointsobj, PyFloat_FromDouble(sasapts[i]));
00982 }
00983 }
00984
00985
00986 return PyFloat_FromDouble(sasa);
00987 }
00988
00989
00990
00991
00992
00993 static Py_ssize_t
00994 atomselection_length( PyObject *a ) {
00995 return ((PyAtomSelObject *)a)->atomSel->selected;
00996 }
00997
00998
00999 static PyObject *
01000 atomselection_subscript( PyAtomSelObject * a, PyObject * keyobj ) {
01001 long ind = PyInt_AsLong(keyobj);
01002 if (ind < 0 && PyErr_Occurred()) return NULL;
01003 PyObject *result = Py_False;
01004 if (ind >= 0 && ind < a->atomSel->num_atoms &&
01005 a->atomSel->on[ind]) {
01006 result = Py_True;
01007 }
01008 Py_INCREF(result);
01009 return result;
01010 }
01011
01012 static PyMappingMethods atomsel_mapping = {
01013 atomselection_length,
01014 (binaryfunc)atomselection_subscript,
01015 0
01016 };
01017
01018
01019 static PyMethodDef atomselection_methods[] = {
01020 { "get", (PyCFunction)atomsel_get, METH_O, get_doc },
01021 { "set", (PyCFunction)atomsel_set, METH_VARARGS, set_doc },
01022 { "update", (PyCFunction)py_update, METH_NOARGS, update_doc },
01023 { "write", (PyCFunction)py_write, METH_VARARGS|METH_KEYWORDS, write_doc },
01024 { "minmax", (PyCFunction)minmax, METH_VARARGS, minmax_doc },
01025 { "center", (PyCFunction)center, METH_VARARGS|METH_KEYWORDS, center_doc },
01026 { "rmsd", (PyCFunction)py_rmsd, METH_VARARGS, rmsd_doc, },
01027 { "fit", (PyCFunction)py_fit, METH_VARARGS, fit_doc, },
01028 { "move", (PyCFunction)py_move, METH_O, move_doc },
01029 { "moveby", (PyCFunction)py_moveby, METH_O, moveby_doc },
01030 { "contacts", (PyCFunction)contacts, METH_VARARGS, contacts_doc },
01031 { "sasa", (PyCFunction)sasa, METH_VARARGS | METH_KEYWORDS, sasa_doc },
01032 { NULL, NULL }
01033 };
01034
01035
01036
01037 namespace {
01038 typedef struct {
01039 PyObject_HEAD
01040 int index;
01041 PyAtomSelObject * a;
01042 } iterobject;
01043
01044 PyObject *atomsel_iter(PyObject *);
01045
01046 PyObject *iter_next(iterobject *it) {
01047 for ( ; it->index < it->a->atomSel->num_atoms; ++it->index) {
01048 if (it->a->atomSel->on[it->index])
01049 return PyInt_FromLong(it->index++);
01050 }
01051 return NULL;
01052 }
01053
01054 void iter_dealloc(iterobject *it) {
01055 Py_XDECREF(it->a);
01056 }
01057 PyObject *iter_len(iterobject *it) {
01058 return PyInt_FromLong(it->a->atomSel->selected);
01059 }
01060
01061 PyMethodDef iter_methods[] = {
01062 {"__length_hint__", (PyCFunction)iter_len, METH_NOARGS },
01063 {NULL, NULL}
01064 };
01065
01066 PyTypeObject itertype = {
01067 PyObject_HEAD_INIT(&PyType_Type)
01068 0,
01069 "atomsel.iterator",
01070 sizeof(iterobject),
01071 0,
01072
01073 (destructor)iter_dealloc,
01074 0,
01075 0,
01076 0,
01077 0,
01078 0,
01079 0,
01080 0,
01081 0,
01082 0,
01083 0,
01084 0,
01085 PyObject_GenericGetAttr,
01086 0,
01087 0,
01088 Py_TPFLAGS_DEFAULT,
01089 0,
01090 0,
01091 0,
01092 0,
01093 0,
01094 PyObject_SelfIter,
01095 (iternextfunc)iter_next,
01096 iter_methods,
01097 0,
01098 };
01099
01100 PyObject *atomsel_iter(PyObject *self) {
01101 iterobject * iter = PyObject_New(iterobject, &itertype);
01102 if (!iter) return NULL;
01103 Py_INCREF( iter->a = (PyAtomSelObject *)self );
01104 iter->index = 0;
01105 return (PyObject *)iter;
01106 }
01107 }
01108
01109
01110
01111 PyTypeObject atomsel_type = {
01112 PyObject_HEAD_INIT(0)
01113 0,
01114 "atomsel.atomsel",
01115 sizeof(PyAtomSelObject),
01116 0,
01117 (destructor)atomsel_dealloc,
01118 0,
01119 0,
01120 0,
01121 0,
01122 (reprfunc)atomsel_repr,
01123 0,
01124 0,
01125 &atomsel_mapping,
01126 0,
01127 0,
01128 (reprfunc)atomsel_str,
01129 PyObject_GenericGetAttr,
01130 0,
01131 0,
01132 Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_CLASS,
01133 atomsel_doc,
01134 0,
01135 0,
01136 0,
01137 0,
01138 atomsel_iter,
01139 0,
01140 atomselection_methods,
01141 0,
01142 atomsel_getset,
01143 0,
01144 0,
01145 0,
01146 0,
01147 0,
01148 0,
01149 PyType_GenericAlloc,
01150 atomsel_new,
01151 _PyObject_Del,
01152 };
01153
01154 static int atomsel_Check(PyObject *obj) {
01155 if (PyObject_TypeCheck(obj, &atomsel_type)) return 1;
01156 PyErr_SetString(PyExc_TypeError, "expected atomsel");
01157 return 0;
01158 }
01159
01160 AtomSel * atomsel_AsAtomSel( PyObject *obj) {
01161 if (!atomsel_Check(obj)) return NULL;
01162 return ((PyAtomSelObject *)obj)->atomSel;
01163 }
01164
01165
01166 static PyMethodDef atomsel_methods[] = {
01167 {"macro", (PyCFunction)macro, METH_VARARGS | METH_KEYWORDS,
01168 "macro(name=None, selection=None) -- create and query selection macros.\n"
01169 "If both name and selection are None, return list of macro names.\n"
01170 "If selection is None, return definition for name.\n"
01171 "If both name and selection are given, define new macro.\n" },
01172 {"delmacro", (vmdPyMethod)delmacro, METH_VARARGS,
01173 "delmacro(name) -> Delete atom selection macro with given name." },
01174 {"inverse", (PyCFunction)inverse, METH_O,
01175 "inverse(matrix) -> Inverse of matrix returned by atomsel.fit(...)"},
01176 {"keywords", (PyCFunction)keywords, METH_NOARGS,
01177 "keywords() -> List of available atom selection keywords."},
01178 {"booleans", (PyCFunction)booleans, METH_NOARGS,
01179 "booleans() -> List of available atom selection boolean tokens."},
01180 {"functions", (PyCFunction)functions, METH_NOARGS,
01181 "functions() -> List of available atom selection functions."},
01182 {"stringfunctions", (PyCFunction)stringfunctions, METH_NOARGS,
01183 "stringfunctions() -> List of available atom selection string functions."},
01184 { NULL, NULL }
01185 };
01186
01187 static char *module_doc = (char *)
01188 "Methods for creating, updating, querying, and modifying\n"
01189 "selections of atoms.\n"
01190 "\n"
01191 "Example of usage:\n"
01192 ">>> from atomsel import *\n"
01193 ">>> s1 = atomsel('residue 1 to 10 and backbone')\n"
01194 ">>> s1.get('resid')\n"
01195 " <snip> \n"
01196 ">>> s1.set('beta', 5') # set B value to 5 for atoms in s1\n"
01197 ">>> # Mass-weighted RMS alignment:\n"
01198 ">>> mass = s1.get('mass')\n"
01199 ">>> s2 = atomsel('residue 21 to 30 and backbone')\n"
01200 ">>> mat = s1.fit(s2, mass)\n"
01201 ">>> s1.move(mat)\n"
01202 ">>> print s1.rmsd(s2)\n" ;
01203
01204 void
01205 initatomsel(void) {
01206 PyObject *m;
01207
01208 atomsel_type.ob_type = &PyType_Type;
01209 m = Py_InitModule3( "atomsel", atomsel_methods, module_doc );
01210
01211 Py_INCREF((PyObject *)&atomsel_type);
01212 if (PyModule_AddObject(m, "atomsel",
01213 (PyObject *)&atomsel_type) !=0)
01214 return;
01215 if (PyType_Ready(&atomsel_type) < 0)
01216 return;
01217
01218 return;
01219 }
01220