Main Page Namespace List Class Hierarchy Alphabetical List Compound List File List Namespace Members Compound Members File Members Related Pages

TclMeasure.C

Go to the documentation of this file.
00001 /***************************************************************************
00002 *cr 
00003 *cr (C) Copyright 1995-2019 The Board of Trustees of the 
00004 *cr University of Illinois 
00005 *cr All Rights Reserved 
00006 *cr 
00007 ***************************************************************************/
00008 
00009 /***************************************************************************
00010 * RCS INFORMATION:
00011 *
00012 * $RCSfile: TclMeasure.C,v $
00013 * $Author: dgomes $ $Locker: $ $State: Exp $
00014 * $Revision: 1.186 $ $Date: 2024年05月16日 14:56:56 $
00015 *
00016 ***************************************************************************
00017 * DESCRIPTION:
00018 * These are essentially just Tcl wrappers for the measure commands in
00019 * Measure.C.
00020 *
00021 ***************************************************************************/
00022 
00023 #include <stdlib.h>
00024 #include <tcl.h>
00025 #include <ctype.h>
00026 #include <math.h>
00027 #include "TclCommands.h"
00028 #include "AtomSel.h"
00029 #include "Matrix4.h"
00030 #include "SymbolTable.h"
00031 #include "VMDApp.h"
00032 #include "MoleculeList.h"
00033 #include "utilities.h"
00034 #include "config.h"
00035 #include "Measure.h"
00036 #include "MeasureSymmetry.h"
00037 #include "SpatialSearch.h"
00038 #include "Atom.h"
00039 #include "Molecule.h"
00040 
00041 // needed by VolInterior commands
00042 #include "QuickSurf.h"
00043 #include "MDFF.h"
00044 #include "CUDAMDFF.h"
00045 #include "MeasureVolInterior.h"
00046 
00047 
00048 // Get weights from a Tcl obj. Data must hold sel->selected items, or natoms.
00049 // If there is no obj, or if it's "none", return a list of ones.
00050 // If the obj matches an atom selection keyword/field, and the field returns 
00051 // floating point data, return that data, otherwise return error.
00052 // Otherwise, the obj must be a list of floats to use for the weights,
00053 // with either a size == sel->selected, or size == natoms. 
00054 //
00055 // NOTE: this routine cannot be used on selections that change between frames.
00056 //
00057 int tcl_get_weights(Tcl_Interp *interp, VMDApp *app, AtomSel *sel, 
00058 Tcl_Obj *weight_obj, float *data) {
00059 char *weight_string = NULL;
00060 
00061 if (!sel) return MEASURE_ERR_NOSEL;
00062 if (!app->molecule_valid_id(sel->molid())) return MEASURE_ERR_NOMOLECULE;
00063 if (weight_obj)
00064 weight_string = Tcl_GetStringFromObj(weight_obj, NULL);
00065 
00066 if (!weight_string || !strcmp(weight_string, "none")) {
00067 for (int i=0; i<sel->selected; i++) {
00068 data[i] = 1.0;
00069 }
00070 return MEASURE_NOERR;
00071 }
00072 
00073 // if a selection string was given, check the symbol table
00074 SymbolTable *atomSelParser = app->atomSelParser; 
00075 
00076 // weights must return floating point values, so the symbol must not 
00077 // be a singleword, so macro is NULL.
00078 atomsel_ctxt context(atomSelParser, 
00079 app->moleculeList->mol_from_id(sel->molid()), 
00080 sel->which_frame, NULL);
00081 
00082 int fctn = atomSelParser->find_attribute(weight_string);
00083 if (fctn >= 0) {
00084 // the keyword exists, so get the data if it is type float, otherwise fail
00085 if (atomSelParser->fctns.data(fctn)->returns_a != SymbolTableElement::IS_FLOAT) {
00086 Tcl_AppendResult(interp, 
00087 "weight attribute must have floating point values", NULL);
00088 return MEASURE_ERR_BADWEIGHTPARM; // can't understand weight parameter 
00089 }
00090 
00091 double *tmp_data = new double[sel->num_atoms];
00092 atomSelParser->fctns.data(fctn)->keyword_double(&context, 
00093 sel->num_atoms, tmp_data, sel->on);
00094 
00095 for (int i=sel->firstsel, j=0; i<=sel->lastsel; i++) {
00096 if (sel->on[i])
00097 data[j++] = (float)tmp_data[i];
00098 }
00099 
00100 delete [] tmp_data;
00101 return MEASURE_NOERR;
00102 }
00103 
00104 // Determine if weights are a Tcl list with the right number of atoms
00105 int list_num;
00106 Tcl_Obj **list_data;
00107 if (Tcl_ListObjGetElements(interp, weight_obj, &list_num, &list_data) 
00108 != TCL_OK) {
00109 return MEASURE_ERR_BADWEIGHTPARM;
00110 }
00111 if (list_num != sel->selected && list_num != sel->num_atoms) 
00112 return MEASURE_ERR_BADWEIGHTNUM;
00113 
00114 for (int i=0, j=0; i<list_num; i++) {
00115 double tmp_data;
00116 
00117 if (Tcl_GetDoubleFromObj(interp, list_data[i], &tmp_data) != TCL_OK) 
00118 return MEASURE_ERR_NONNUMBERPARM;
00119 
00120 if (list_num == sel->selected) {
00121 data[i] = (float)tmp_data; // one weight list item per selected atom
00122 } else {
00123 if (sel->on[i]) {
00124 data[j++] = (float)tmp_data; // one weight list item for each atom
00125 }
00126 }
00127 }
00128 
00129 return MEASURE_NOERR;
00130 }
00131 
00132 
00133 int atomsel_default_weights(AtomSel *sel, float *weights) {
00134 if (sel->firstsel > 0)
00135 memset(&weights[0], 0, sel->firstsel * sizeof(float)); 
00136 
00137 for (int i=sel->firstsel; i<=sel->lastsel; i++) {
00138 // Use the standard "on" check instead of typecasting the array elements
00139 weights[i] = sel->on[i] ? 1.0f : 0.0f;
00140 }
00141 
00142 if (sel->lastsel < (sel->num_atoms - 1))
00143 memset(&weights[sel->lastsel+1], 0, ((sel->num_atoms - 1) - sel->lastsel) * sizeof(float)); 
00144 
00145 return 0;
00146 }
00147 
00148 
00149 int get_weights_from_tcl_list(Tcl_Interp *interp, VMDApp *app, AtomSel *sel,
00150 Tcl_Obj *weights_obj, float *weights) {
00151 int list_num = 0;
00152 Tcl_Obj **list_elems = NULL;
00153 if (Tcl_ListObjGetElements(interp, weights_obj, &list_num, &list_elems)
00154 != TCL_OK) {
00155 return MEASURE_ERR_BADWEIGHTPARM;
00156 }
00157 if (list_num != sel->num_atoms) {
00158 return MEASURE_ERR_BADWEIGHTNUM;
00159 }
00160 for (int i = 0; i < sel->num_atoms; i++) {
00161 double tmp_data = 0.0;
00162 if (Tcl_GetDoubleFromObj(interp, list_elems[i], &tmp_data) != TCL_OK) {
00163 return TCL_ERROR;
00164 }
00165 weights[i] = static_cast<float>(tmp_data);
00166 }
00167 return 0;
00168 }
00169 
00170 
00171 // This routine eliminates the need to include SymbolTable.h,
00172 // and allows the caller to determine if a per-atom attribute/field
00173 // exists or not.
00174 int get_attribute_index(VMDApp *app, char const *string) {
00175 SymbolTable *atomSelParser = app->atomSelParser;
00176 return atomSelParser->find_attribute(string);
00177 }
00178 
00179 
00180 int get_weights_from_attribute(VMDApp *app, AtomSel *sel,
00181 char const *weights_string, float *weights) {
00182 SymbolTable *atomSelParser = app->atomSelParser;
00183 int fctn = atomSelParser->find_attribute(weights_string);
00184 // first, check to see that the function returns floats.
00185 // if it doesn't, it makes no sense to use it as a weight
00186 if (atomSelParser->fctns.data(fctn)->returns_a !=
00187 SymbolTableElement::IS_FLOAT) {
00188 return MEASURE_ERR_BADWEIGHTPARM; // can't understand weight parameter
00189 }
00190 atomsel_ctxt context(atomSelParser,
00191 app->moleculeList->mol_from_id(sel->molid()),
00192 sel->which_frame, NULL);
00193 double *tmp_data = new double[sel->num_atoms];
00194 atomSelParser->fctns.data(fctn)->keyword_double(&context, sel->num_atoms,
00195 tmp_data, sel->on);
00196 
00197 if (sel->firstsel > 0)
00198 memset(&weights[0], 0, sel->firstsel * sizeof(float)); 
00199 
00200 for (int i=sel->firstsel; i<=sel->lastsel; i++) {
00201 weights[i] = sel->on[i] ? static_cast<float>(tmp_data[i]) : 0.0f;
00202 }
00203 
00204 if (sel->lastsel < (sel->num_atoms - 1))
00205 memset(&weights[sel->lastsel+1], 0, ((sel->num_atoms - 1) - sel->lastsel) * sizeof(float)); 
00206 
00207 delete [] tmp_data;
00208 return 0;
00209 }
00210 
00211 
00212 // get the atom index re-ordering list for use by measure_fit
00213 int tcl_get_orders(Tcl_Interp *interp, int selnum, 
00214 Tcl_Obj *order_obj, int *data) {
00215 int list_num;
00216 Tcl_Obj **list_data;
00217 
00218 if (Tcl_ListObjGetElements(interp, order_obj, &list_num, &list_data)
00219 != TCL_OK) {
00220 return MEASURE_ERR_NOSEL;
00221 }
00222 
00223 // see if this is a Tcl list with the right number of atom indices
00224 if (list_num != selnum) return MEASURE_ERR_NOSEL;
00225 
00226 for (int i=0; i<list_num; i++) {
00227 if (Tcl_GetIntFromObj(interp, list_data[i], &data[i]) != TCL_OK)
00228 return MEASURE_ERR_NONNUMBERPARM;
00229 
00230 // order indices are 0-based
00231 if (data[i] < 0 || data[i] >= selnum)
00232 return MEASURE_ERR_BADORDERINDEX; // order index is out of range
00233 }
00234 
00235 return MEASURE_NOERR;
00236 }
00237 
00238 
00239 //
00240 // Function: vmd_measure_center
00241 // Parameters: <selection> // computes with weight == 1
00242 // Parameters: <selection> weight [none | atom field | list] 
00243 // computes with the weights based on the following:
00244 // none => weights all 1
00245 // atom field => value from atomSelParser (eg
00246 // mass => use weight based on mass
00247 // index => use weight based on atom index (0 to n-1)
00248 // list => use list to get weights for each atom. 
00249 // The list can have length == number of selected atoms,
00250 // or it can have length == the total number of atoms
00251 // 
00252 // Examples: 
00253 // vmd_measure_center atomselect12
00254 // vmd_measure_center atomselect12 weight mass
00255 // vmd_measure_center atomselect12 {12 13} [atomselect top "index 2 3"]
00256 // If no weight is given, no weight term is used (computes center of number)
00257 //
00258 static int vmd_measure_center(VMDApp *app, int argc, Tcl_Obj *const objv[], Tcl_Interp *interp) {
00259 if (argc != 2 && argc != 4 ) {
00260 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<sel> [weight <weights>]");
00261 return TCL_ERROR;
00262 }
00263 if (argc == 4 && strcmp(Tcl_GetStringFromObj(objv[2],NULL), "weight")) {
00264 Tcl_SetResult(interp, (char *) "measure center: parameter can only be 'weight'", TCL_STATIC);
00265 return TCL_ERROR;
00266 }
00267 
00268 // get the selection
00269 AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
00270 if (!sel) {
00271 Tcl_SetResult(interp, (char *) "measure center: no atom selection", TCL_STATIC);
00272 return TCL_ERROR;
00273 }
00274 
00275 // get the weight
00276 float *weight = new float[sel->selected];
00277 int ret_val=0;
00278 if (argc == 2) { // only from atom selection, so weight is 1
00279 ret_val = tcl_get_weights(interp, app, sel, NULL, weight);
00280 } else {
00281 ret_val = tcl_get_weights(interp, app, sel, objv[3], weight);
00282 }
00283 if (ret_val < 0) {
00284 Tcl_AppendResult(interp, "measure center: ", measure_error(ret_val), NULL);
00285 delete [] weight;
00286 return TCL_ERROR;
00287 }
00288 
00289 // compute the center of "mass"
00290 float com[3];
00291 const float *framepos = sel->coordinates(app->moleculeList);
00292 ret_val = measure_center(sel, framepos, weight, com);
00293 delete [] weight;
00294 if (ret_val < 0) {
00295 Tcl_AppendResult(interp, "measure center: ", measure_error(ret_val), NULL);
00296 return TCL_ERROR;
00297 }
00298 
00299 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00300 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(com[0]));
00301 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(com[1]));
00302 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(com[2]));
00303 Tcl_SetObjResult(interp, tcl_result);
00304 
00305 return TCL_OK;
00306 }
00307 
00308 
00309 static int vmd_measure_centerperresidue(VMDApp *app, int argc, Tcl_Obj *const objv[], Tcl_Interp *interp) {
00310 int ret_val = 0;
00311 
00312 // check argument counts for valid combinations
00313 if (argc != 2 && argc != 4 ) {
00314 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<sel> [weight <weights>]");
00315 return TCL_ERROR;
00316 }
00317 
00318 // the only valid optional parameter is "weight"
00319 if (argc == 4 && strcmp(Tcl_GetStringFromObj(objv[2], NULL), "weight")) {
00320 Tcl_SetResult(interp, (char *) "measure centerperresidue: parameter can only be 'weight'", TCL_STATIC);
00321 return TCL_ERROR;
00322 }
00323 
00324 // get the selection
00325 AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1], NULL));
00326 if (!sel) {
00327 Tcl_SetResult(interp, (char *) "measure centerperresidue: no atom selection", TCL_STATIC);
00328 return TCL_ERROR;
00329 }
00330 
00331 // get the weight
00332 float *weight = new float[sel->selected];
00333 if (argc == 2) {
00334 // only from atom selection, so weights are all set to 1
00335 ret_val = tcl_get_weights(interp, app, sel, NULL, weight);
00336 } else {
00337 // from a per-atom field or a list
00338 ret_val = tcl_get_weights(interp, app, sel, objv[3], weight);
00339 }
00340 if (ret_val < 0) {
00341 Tcl_AppendResult(interp, "measure centerperresidue: ", 
00342 measure_error(ret_val), NULL);
00343 delete [] weight;
00344 return TCL_ERROR;
00345 }
00346 
00347 // compute the center of "mass"
00348 float *com = new float[3*sel->selected];
00349 const float *framepos = sel->coordinates(app->moleculeList);
00350 ret_val = measure_center_perresidue(app->moleculeList, sel, 
00351 framepos, weight, com);
00352 delete [] weight;
00353 if (ret_val < 0) {
00354 Tcl_AppendResult(interp, "measure centerperresidue: ", 
00355 measure_error(ret_val), NULL);
00356 return TCL_ERROR;
00357 }
00358 
00359 // generate lists of CoMs from results
00360 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00361 for (int i=0; i<ret_val; i++) {
00362 Tcl_Obj *m = Tcl_NewListObj(0, NULL);
00363 for (int j=0; j<3; j++) {
00364 Tcl_ListObjAppendElement(interp, m, Tcl_NewDoubleObj(com[3*i+j]));
00365 }
00366 Tcl_ListObjAppendElement(interp, tcl_result, m);
00367 }
00368 Tcl_SetObjResult(interp, tcl_result);
00369 delete [] com;
00370 
00371 return TCL_OK;
00372 }
00373 
00374 
00375 // measure sum of weights for selected atoms
00376 static int vmd_measure_sumweights(VMDApp *app, int argc, Tcl_Obj *const objv[], Tcl_Interp *interp) {
00377 if (argc != 4) {
00378 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<sel> weight <weights>");
00379 return TCL_ERROR;
00380 }
00381 if (strcmp(Tcl_GetStringFromObj(objv[2],NULL), "weight")) {
00382 Tcl_SetResult(interp, (char *) "measure sumweights: parameter can only be 'weight'", TCL_STATIC);
00383 return TCL_ERROR;
00384 }
00385 
00386 // get the selection
00387 AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL)
00388 );
00389 if (!sel) {
00390 Tcl_SetResult(interp, (char *) "measure sumweights: no atom selection", TCL_STATIC);
00391 return TCL_ERROR;
00392 }
00393 
00394 // get the weight
00395 float *weight = new float[sel->selected];
00396 int ret_val = 0;
00397 ret_val = tcl_get_weights(interp, app, sel, objv[3], weight);
00398 if (ret_val < 0) {
00399 Tcl_AppendResult(interp, "measure center: ", measure_error(ret_val), NULL);
00400 delete [] weight;
00401 return TCL_ERROR;
00402 }
00403 
00404 // compute the sum of the weights
00405 float weightsum=0;
00406 ret_val = measure_sumweights(sel, sel->selected, weight, &weightsum);
00407 delete [] weight;
00408 if (ret_val < 0) {
00409 Tcl_AppendResult(interp, "measure center: ", measure_error(ret_val), NULL);
00410 return TCL_ERROR;
00411 }
00412 
00413 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00414 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(weightsum));
00415 Tcl_SetObjResult(interp, tcl_result);
00416 
00417 return TCL_OK;
00418 }
00419 
00420 
00421 // Function: vmd_measure_avpos <selection> first <first> last <last> step <step>
00422 // Returns: the average position of the selected atoms over the selected frames
00423 // Example: measure avpos atomselect76 0 20 1
00424 static int vmd_measure_avpos(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00425 int first = 0; // start with first frame by default
00426 int last = -1; // finish with last frame by default
00427 int step = 1; // use all frames by default
00428 
00429 if (argc < 2 || argc > 8) {
00430 Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<sel> [first <first>] [last <last>] [step <step>]");
00431 return TCL_ERROR;
00432 }
00433 AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL)
00434 );
00435 if (!sel) {
00436 Tcl_AppendResult(interp, "measure avpos: no atom selection", NULL);
00437 return TCL_ERROR;
00438 }
00439 
00440 int i;
00441 for (i=2; i<argc; i+=2) {
00442 char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
00443 if (!strupncmp(argvcur, "first", CMDLEN)) {
00444 if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
00445 Tcl_AppendResult(interp, "measure avpos: bad first frame value", NULL);
00446 return TCL_ERROR;
00447 }
00448 } else if (!strupncmp(argvcur, "last", CMDLEN)) {
00449 if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
00450 Tcl_AppendResult(interp, "measure avpos: bad last frame value", NULL);
00451 return TCL_ERROR;
00452 }
00453 } else if (!strupncmp(argvcur, "step", CMDLEN)) {
00454 if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK) {
00455 Tcl_AppendResult(interp, "measure avpos: bad frame step value", NULL);
00456 return TCL_ERROR;
00457 }
00458 } else {
00459 Tcl_AppendResult(interp, "measure rmsdmat_qcp: invalid syntax, no such keyword: ", argvcur, NULL);
00460 return TCL_ERROR;
00461 }
00462 }
00463 
00464 float *avpos = new float[3L*sel->selected];
00465 int ret_val = measure_avpos(sel, app->moleculeList, first, last, step, avpos);
00466 if (ret_val < 0) {
00467 Tcl_AppendResult(interp, "measure avpos: ", measure_error(ret_val), NULL);
00468 delete [] avpos;
00469 return TCL_ERROR;
00470 }
00471 
00472 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00473 for (i=0; i<sel->selected; i++) {
00474 Tcl_Obj *atom = Tcl_NewListObj(0, NULL);
00475 Tcl_ListObjAppendElement(interp, atom, Tcl_NewDoubleObj(avpos[i*3L ]));
00476 Tcl_ListObjAppendElement(interp, atom, Tcl_NewDoubleObj(avpos[i*3L + 1]));
00477 Tcl_ListObjAppendElement(interp, atom, Tcl_NewDoubleObj(avpos[i*3L + 2]));
00478 Tcl_ListObjAppendElement(interp, tcl_result, atom);
00479 }
00480 
00481 Tcl_SetObjResult(interp, tcl_result);
00482 delete [] avpos;
00483 
00484 return TCL_OK;
00485 }
00486 
00487 
00488 // Function: vmd_measure_dipole <selection>
00489 // Returns: the dipole moment for the selected atoms
00490 static int vmd_measure_dipole(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00491 const char *opt;
00492 int unitsdebye=0; // default units are elementary charges/Angstrom
00493 int usecenter=1; // remove net charge at the center of mass (-1), geometrical center (1), don't (0)
00494 
00495 if ((argc < 2) || (argc > 4)) {
00496 Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<sel> [-elementary|-debye] [-geocenter|-masscenter|-origincenter]");
00497 return TCL_ERROR;
00498 }
00499 AtomSel *sel;
00500 sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1], NULL));
00501 if (!sel) {
00502 Tcl_AppendResult(interp, "measure dipole: no atom selection", NULL);
00503 return TCL_ERROR;
00504 }
00505 
00506 int i;
00507 for (i=0; i < (argc-2); ++i) {
00508 opt = Tcl_GetStringFromObj(objv[2+i], NULL);
00509 if (!strcmp(opt, "-debye"))
00510 unitsdebye=1; 
00511 if (!strcmp(opt, "-elementary"))
00512 unitsdebye=0; 
00513 
00514 if (!strcmp(opt, "-geocenter"))
00515 usecenter=1; 
00516 if (!strcmp(opt, "-masscenter"))
00517 usecenter=-1; 
00518 if (!strcmp(opt, "-origincenter"))
00519 usecenter=0; 
00520 }
00521 
00522 float dipole[3];
00523 int ret_val = measure_dipole(sel, app->moleculeList, dipole, unitsdebye, usecenter);
00524 if (ret_val < 0) {
00525 Tcl_AppendResult(interp, "measure dipole: ", measure_error(ret_val), NULL);
00526 return TCL_ERROR;
00527 }
00528 
00529 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00530 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(dipole[0]));
00531 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(dipole[1]));
00532 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(dipole[2]));
00533 Tcl_SetObjResult(interp, tcl_result);
00534 
00535 return TCL_OK;
00536 }
00537 
00538 
00539 
00540 // Function: vmd_measure_dihed {4 atoms as {<atomid> ?<molid>?}} ?molid <default molid>?
00541 // ?frame [f|all|last]? | ?first <first>? ?last <last>?
00542 // Returns: the dihedral angle for the specified atoms
00543 static int vmd_measure_dihed(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00544 int first=-1, last=-1, frame=-1;
00545 if (argc < 2) {
00546 Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "{{<atomid1> [<molid1>]} {<atomid2> [<molid2>]} {<atomid3> [<molid3>]} {<atomid4> [<molid4>]}} [molid <default molid>] [frame <frame|all|last> | first <first> last <last>]");
00547 return TCL_ERROR;
00548 }
00549 
00550 int molid[4], atmid[4], defmolid = -1;
00551 bool allframes = false;
00552 
00553 // Get the geometry type dihed/imprp
00554 char *geomname = Tcl_GetStringFromObj(objv[0],NULL);
00555 
00556 // Read the atom list
00557 int numatms;
00558 Tcl_Obj **data;
00559 if (Tcl_ListObjGetElements(interp, objv[1], &numatms, &data) != TCL_OK) {
00560 Tcl_AppendResult(interp, " measure ", geomname, ": bad syntax", NULL);
00561 Tcl_AppendResult(interp, " Usage: measure ", geomname, " {{<atomid1> [<molid1>]} {<atomid2> [<molid2>]} {<atomid3> [<molid3>]} {<atomid4> [<molid4>]}} [molid <default molid>] [frame <frame|all|last> | first <first> last <last>]", NULL);
00562 return TCL_ERROR;
00563 }
00564 
00565 if (numatms != 4) {
00566 Tcl_AppendResult(interp, " measure dihed: must specify exactly four atoms in a list", NULL);
00567 return TCL_ERROR;
00568 }
00569 
00570 if (argc > 3) {
00571 int i;
00572 for (i=2; i<argc; i+=2) {
00573 char *argvcur = Tcl_GetStringFromObj(objv[i], NULL);
00574 if (!strupncmp(argvcur, "molid", CMDLEN)) {
00575 if (Tcl_GetIntFromObj(interp, objv[i+1], &defmolid) != TCL_OK) {
00576 Tcl_AppendResult(interp, " measure ", geomname, ": bad molid", NULL);
00577 return TCL_ERROR;
00578 }
00579 } else if (!strupncmp(argvcur, "first", CMDLEN)) {
00580 if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
00581 Tcl_AppendResult(interp, " measure ", geomname, ": bad first frame value", NULL);
00582 return TCL_ERROR;
00583 }
00584 } else if (!strupncmp(argvcur, "last", CMDLEN)) {
00585 if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
00586 Tcl_AppendResult(interp, " measure ", geomname, ": bad last frame value", NULL);
00587 return TCL_ERROR;
00588 }
00589 } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
00590 if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "all")) {
00591 allframes = true;
00592 } else if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "last")) {
00593 frame=-2;
00594 } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
00595 Tcl_AppendResult(interp, " measure ", geomname, ": bad frame value", NULL);
00596 return TCL_ERROR;
00597 }
00598 } else {
00599 Tcl_AppendResult(interp, "measure ", geomname, ": invalid syntax, no such keyword: ", argvcur, NULL);
00600 return TCL_ERROR;
00601 }
00602 }
00603 }
00604 
00605 if ((allframes || frame>=0) && (first>=0 || last>=0)) {
00606 Tcl_AppendResult(interp, "measure ", geomname, ": Ambiguous syntax: You cannot specify a frame AND a frame range (using first or last).", NULL);
00607 Tcl_AppendResult(interp, "\nUsage:\nmeasure ", geomname, " {<atomid1> [<molid1>]} {<atomid2> [<molid2>]} {<atomid3> [<molid3>]} {<atomid4> [<molid4>]}} [molid <default molid>] [frame <frame|all|last> | first <first> last <last>]", NULL);
00608 return TCL_ERROR; 
00609 }
00610 
00611 if (allframes) first=0;
00612 
00613 // If no molecule was specified use top as default
00614 if (defmolid<0) defmolid = app->molecule_top();
00615 
00616 // Assign atom IDs and molecule IDs
00617 int i,numelem;
00618 Tcl_Obj **atmmol;
00619 for (i=0; i<numatms; i++) {
00620 if (Tcl_ListObjGetElements(interp, data[i], &numelem, &atmmol) != TCL_OK) {
00621 return TCL_ERROR;
00622 }
00623 
00624 if (!numelem) {
00625 Tcl_AppendResult(interp, " measure ", geomname, ": empty atom index", NULL);
00626 return TCL_ERROR;
00627 }
00628 
00629 if (Tcl_GetIntFromObj(interp, atmmol[0], atmid+i) != TCL_OK) {
00630 Tcl_AppendResult(interp, " measure ", geomname, ": bad atom index", NULL);
00631 return TCL_ERROR;
00632 }
00633 
00634 if (numelem==2) {
00635 if (Tcl_GetIntFromObj(interp, atmmol[1], molid+i) != TCL_OK) {
00636 Tcl_AppendResult(interp, " measure ", geomname, ": bad molid", NULL);
00637 return TCL_ERROR;
00638 }
00639 } else molid[i] = defmolid;
00640 }
00641 
00642 // Compute the value
00643 ResizeArray<float> gValues(1024);
00644 int ret_val;
00645 ret_val = measure_geom(app->moleculeList, molid, atmid, &gValues, 
00646 frame, first, last, defmolid, MEASURE_DIHED);
00647 if (ret_val < 0) {
00648 Tcl_AppendResult(interp, measure_error(ret_val), NULL);
00649 return TCL_ERROR;
00650 }
00651 
00652 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00653 int numvalues = gValues.num();
00654 for (int count = 0; count < numvalues; count++) {
00655 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(gValues[count]));
00656 }
00657 Tcl_SetObjResult(interp, tcl_result);
00658 
00659 return TCL_OK;
00660 }
00661 
00662 
00663 // Function: vmd_measure_angle {3 atoms as {<atomid> ?<molid>?}} ?molid <default molid>? 
00664 // ?frame [f|all|last]? | ?first <first>? ?last <last>?
00665 // Returns: the bond angle for the specified atoms
00666 static int vmd_measure_angle(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00667 int first=-1, last=-1, frame=-1;
00668 
00669 if(argc<2) {
00670 Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "{{<atomid1> [<molid1>]} {<atomid2> [<molid2>]} {<atomid3> [<molid3>]}} [molid <default molid>] [frame <frame|all|last> | first <first> last <last>]");
00671 return TCL_ERROR;
00672 }
00673 
00674 int molid[3], atmid[3], defmolid = -1;
00675 bool allframes = false;
00676 
00677 // Read the atom list
00678 int numatms;
00679 Tcl_Obj **data;
00680 if (Tcl_ListObjGetElements(interp, objv[1], &numatms, &data) != TCL_OK) {
00681 Tcl_AppendResult(interp, " measure bond: bad syntax", NULL);
00682 Tcl_AppendResult(interp, " Usage: measure angle {{<atomid1> [<molid1>]} {<atomid2> [<molid2>]} {<atomid3> [<molid3>]}} [molid <default molid>] [frame <frame|all|last> | first <first> last <last>]", NULL);
00683 return TCL_ERROR;
00684 }
00685 
00686 if (numatms != 3) {
00687 Tcl_AppendResult(interp, " measure angle: must specify exactly three atoms in a list", NULL);
00688 return TCL_ERROR;
00689 }
00690 
00691 if (argc > 3) {
00692 for (int i=2; i<argc; i+=2) {
00693 char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
00694 if (!strupncmp(argvcur, "molid", CMDLEN)) {
00695 if (Tcl_GetIntFromObj(interp, objv[i+1], &defmolid) != TCL_OK) {
00696 Tcl_AppendResult(interp, " measure angle: bad molid", NULL);
00697 return TCL_ERROR;
00698 }
00699 } else if (!strupncmp(argvcur, "first", CMDLEN)) {
00700 if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
00701 Tcl_AppendResult(interp, " measure angle: bad first frame value", NULL);
00702 return TCL_ERROR;
00703 }
00704 } else if (!strupncmp(argvcur, "last", CMDLEN)) {
00705 if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
00706 Tcl_AppendResult(interp, " measure angle: bad last frame value", NULL);
00707 return TCL_ERROR;
00708 }
00709 } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
00710 if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "all")) {
00711 allframes = true;
00712 } else if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "last")) {
00713 frame=-2;
00714 } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
00715 Tcl_AppendResult(interp, " measure angle: bad frame value", NULL);
00716 return TCL_ERROR;
00717 }
00718 } else {
00719 Tcl_AppendResult(interp, " measure angle: invalid syntax, no such keyword: ", argvcur, NULL);
00720 return TCL_ERROR;
00721 }
00722 }
00723 }
00724 
00725 if ((allframes || frame>=0) && (first>=0 || last>=0)) {
00726 Tcl_AppendResult(interp, "measure angle: Ambiguous syntax: You cannot specify a frame AND a frame range (using first or last).", NULL);
00727 Tcl_AppendResult(interp, "\nUsage:\nmeasure angle {<atomid1> [<molid1>]} {<atomid2> [<molid2>]} {<atomid3> [<molid3>]}} [molid <default molid>] [frame <frame|all|last> | first <first> last <last>]", NULL);
00728 return TCL_ERROR; 
00729 }
00730 
00731 if (allframes) first=0;
00732 
00733 // If no molecule was specified use top as default
00734 if (defmolid<0) defmolid = app->molecule_top();
00735 
00736 // Assign atom IDs and molecule IDs
00737 int i,numelem;
00738 Tcl_Obj **atmmol;
00739 for (i=0; i<numatms; i++) {
00740 if (Tcl_ListObjGetElements(interp, data[i], &numelem, &atmmol) != TCL_OK) {
00741 return TCL_ERROR;
00742 }
00743 
00744 if (!numelem) {
00745 Tcl_AppendResult(interp, " measure angle: empty atom index", NULL);
00746 return TCL_ERROR;
00747 }
00748 
00749 if (Tcl_GetIntFromObj(interp, atmmol[0], atmid+i) != TCL_OK) {
00750 Tcl_AppendResult(interp, " measure angle: bad atom index", NULL);
00751 return TCL_ERROR;
00752 }
00753 
00754 if (numelem==2) {
00755 if (Tcl_GetIntFromObj(interp, atmmol[1], molid+i) != TCL_OK) {
00756 Tcl_AppendResult(interp, " measure angle: bad molid", NULL);
00757 return TCL_ERROR;
00758 }
00759 } else molid[i] = defmolid;
00760 }
00761 
00762 // Compute the value
00763 ResizeArray<float> gValues(1024);
00764 int ret_val;
00765 ret_val = measure_geom(app->moleculeList, molid, atmid, &gValues, 
00766 frame, first, last, defmolid, MEASURE_ANGLE);
00767 if (ret_val<0) {
00768 printf("ERROR\n %s\n", measure_error(ret_val));
00769 Tcl_AppendResult(interp, measure_error(ret_val), NULL);
00770 return TCL_ERROR;
00771 }
00772 
00773 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00774 int numvalues = gValues.num();
00775 for (int count = 0; count < numvalues; count++) {
00776 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(gValues[count]));
00777 }
00778 Tcl_SetObjResult(interp, tcl_result);
00779 
00780 return TCL_OK;
00781 }
00782 
00783 
00784 // vmd_measure_bond {2 atoms as {<atomid> ?<molid>?}} ?molid <molid>? ?frame [f|all|last]? | ?first <first>? ?last <last>?
00785 // Returns the bond length for the specified atoms
00786 static int vmd_measure_bond(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00787 int first=-1, last=-1, frame=-1;
00788 
00789 if (argc<2) {
00790 Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "{{<atomid1> [<molid1>]} {<atomid2> [<molid2>]}} [molid <default molid>] [frame <frame|all|last> | first <first> last <last>]]");
00791 return TCL_ERROR;
00792 }
00793 
00794 int molid[2], atmid[2], defmolid = -1;
00795 bool allframes = false;
00796 
00797 // Read the atom list
00798 int numatms=0;
00799 Tcl_Obj **data;
00800 if (Tcl_ListObjGetElements(interp, objv[1], &numatms, &data) != TCL_OK) {
00801 Tcl_AppendResult(interp, " measure bond: bad syntax", NULL);
00802 Tcl_AppendResult(interp, " Usage: measure bond {{<atomid1> [<molid1>]} {<atomid2> [<molid2>]}} [molid <default>] [frame <frame|all|last> | first <first> last <last>]]", NULL);
00803 return TCL_ERROR;
00804 }
00805 
00806 if (numatms != 2) {
00807 Tcl_AppendResult(interp, " measure bond: must specify exactly two atoms in a list", NULL);
00808 return TCL_ERROR;
00809 }
00810 
00811 if (argc > 3) {
00812 for (int i=2; i<argc; i+=2) {
00813 char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
00814 if (!strupncmp(argvcur, "molid", CMDLEN)) {
00815 if (Tcl_GetIntFromObj(interp, objv[i+1], &defmolid) != TCL_OK) {
00816 Tcl_AppendResult(interp, " measure bond: bad molid", NULL);
00817 return TCL_ERROR;
00818 }
00819 } else if (!strupncmp(argvcur, "first", CMDLEN)) {
00820 if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
00821 Tcl_AppendResult(interp, " measure bond: bad first frame value", NULL);
00822 return TCL_ERROR;
00823 }
00824 } else if (!strupncmp(argvcur, "last", CMDLEN)) {
00825 if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
00826 Tcl_AppendResult(interp, " measure bond: bad last frame value", NULL);
00827 return TCL_ERROR;
00828 }
00829 } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
00830 if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "all")) {
00831 allframes = true;
00832 } else if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "last")) {
00833 frame=-2;
00834 } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
00835 Tcl_AppendResult(interp, " measure bond: bad frame value", NULL);
00836 return TCL_ERROR;
00837 }
00838 } else {
00839 Tcl_AppendResult(interp, " measure bond: invalid syntax, no such keyword: ", argvcur, NULL);
00840 return TCL_ERROR;
00841 }
00842 }
00843 }
00844 
00845 if ((allframes || frame>=0) && (first>=0 || last>=0)) {
00846 Tcl_AppendResult(interp, "measure bond: Ambiguous syntax: You cannot specify a frame AND a frame range (using first or last).", NULL);
00847 Tcl_AppendResult(interp, "\nUsage:\nmeasure bond {{<atomid1> [<molid1>]} {<atomid2> [<molid2>]}} [molid <default>] [frame <frame|all|last> | first <first> last <last>]", NULL);
00848 return TCL_ERROR; 
00849 }
00850 
00851 if (allframes) first=0;
00852 
00853 // If no molecule was specified use top as default
00854 if (defmolid<0) defmolid = app->molecule_top();
00855 
00856 // Assign atom IDs and molecule IDs
00857 int i, numelem;
00858 Tcl_Obj **atmmol;
00859 for (i=0; i<numatms; i++) {
00860 if (Tcl_ListObjGetElements(interp, data[i], &numelem, &atmmol) != TCL_OK) {
00861 return TCL_ERROR;
00862 }
00863 
00864 if (!numelem) {
00865 Tcl_AppendResult(interp, " measure bond: empty atom index", NULL);
00866 return TCL_ERROR;
00867 }
00868 
00869 if (Tcl_GetIntFromObj(interp, atmmol[0], atmid+i) != TCL_OK) {
00870 Tcl_AppendResult(interp, " measure bond: bad atom index", NULL);
00871 return TCL_ERROR;
00872 }
00873 
00874 if (numelem==2) {
00875 if (Tcl_GetIntFromObj(interp, atmmol[1], molid+i) != TCL_OK) {
00876 Tcl_AppendResult(interp, " measure bond: bad molid", NULL);
00877 return TCL_ERROR;
00878 }
00879 } else molid[i] = defmolid;
00880 }
00881 
00882 // Compute the value
00883 ResizeArray<float> gValues(1024);
00884 int ret_val;
00885 ret_val = measure_geom(app->moleculeList, molid, atmid, &gValues, 
00886 frame, first, last, defmolid, MEASURE_BOND);
00887 if (ret_val < 0) {
00888 printf("ERROR\n %s\n", measure_error(ret_val));
00889 Tcl_AppendResult(interp, measure_error(ret_val), NULL);
00890 return TCL_ERROR;
00891 }
00892 
00893 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00894 int numvalues = gValues.num();
00895 for (int count = 0; count < numvalues; count++) {
00896 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(gValues[count]));
00897 }
00898 Tcl_SetObjResult(interp, tcl_result);
00899 
00900 return TCL_OK;
00901 }
00902 
00903 
00904 // vmd_measure_rmsfperresidue <selection> first <first> last <last> step <step>
00905 // Returns: position variance of the selected atoms over the selected frames, 
00906 // returning one value per residue in the selection.
00907 // Example: measure rmsfperresidue atomselect76 0 20 1
00908 static int vmd_measure_rmsfperresidue(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00909 int first = 0; // start with first frame by default
00910 int last = -1; // finish with last frame by default
00911 int step = 1; // use all frames by default
00912 
00913 if (argc < 2 || argc > 8) {
00914 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<sel> [first <first>] [last <last>] [step <step>]");
00915 return TCL_ERROR;
00916 }
00917 AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL)
00918 );
00919 if (!sel) {
00920 Tcl_AppendResult(interp, "measure rmsfperresidue: no atom selection", NULL);
00921 return TCL_ERROR;
00922 }
00923 
00924 int i;
00925 for (i=2; i<argc; i+=2) {
00926 char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
00927 if (!strupncmp(argvcur, "first", CMDLEN)) {
00928 if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
00929 Tcl_AppendResult(interp, "measure rmsfperresidue: bad first frame value", NULL);
00930 return TCL_ERROR;
00931 }
00932 } else if (!strupncmp(argvcur, "last", CMDLEN)) {
00933 if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
00934 Tcl_AppendResult(interp, "measure rmsfperresidue: bad last frame value", NULL);
00935 return TCL_ERROR;
00936 }
00937 } else if (!strupncmp(argvcur, "step", CMDLEN)) {
00938 if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK) {
00939 Tcl_AppendResult(interp, "measure rmsfperresidue: bad frame step value", NULL);
00940 return TCL_ERROR;
00941 }
00942 } else {
00943 Tcl_AppendResult(interp, "measure rmsfperresidue: invalid syntax, no such keyword: ", argvcur, NULL);
00944 return TCL_ERROR;
00945 }
00946 }
00947 
00948 float *rmsf = new float[sel->selected];
00949 int ret_val = measure_rmsf_perresidue(sel, app->moleculeList, first, last, step, rmsf);
00950 if (ret_val < 0) {
00951 Tcl_AppendResult(interp, "measure rmsfperresidue: ", measure_error(ret_val), NULL);
00952 delete [] rmsf;
00953 return TCL_ERROR;
00954 }
00955 
00956 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00957 for (i=0; i<ret_val; i++) {
00958 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(rmsf[i]));
00959 }
00960 Tcl_SetObjResult(interp, tcl_result);
00961 delete [] rmsf;
00962 
00963 return TCL_OK;
00964 }
00965 
00966 
00967 // Function: vmd_measure_rmsf <selection> first <first> last <last> step <step>
00968 // Returns: position variance of the selected atoms over the selected frames
00969 // Example: measure rmsf atomselect76 0 20 1
00970 static int vmd_measure_rmsf(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00971 int first = 0; // start with first frame by default
00972 int last = -1; // finish with last frame by default
00973 int step = 1; // use all frames by default
00974 
00975 if (argc < 2 || argc > 8) {
00976 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<sel> [first <first>] [last <last>] [step <step>]");
00977 return TCL_ERROR;
00978 }
00979 AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL)
00980 );
00981 if (!sel) {
00982 Tcl_AppendResult(interp, "measure rmsf: no atom selection", NULL);
00983 return TCL_ERROR;
00984 }
00985 
00986 int i;
00987 for (i=2; i<argc; i+=2) {
00988 char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
00989 if (!strupncmp(argvcur, "first", CMDLEN)) {
00990 if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
00991 Tcl_AppendResult(interp, "measure rmsf: bad first frame value", NULL);
00992 return TCL_ERROR;
00993 }
00994 } else if (!strupncmp(argvcur, "last", CMDLEN)) {
00995 if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
00996 Tcl_AppendResult(interp, "measure rmsf: bad last frame value", NULL);
00997 return TCL_ERROR;
00998 }
00999 } else if (!strupncmp(argvcur, "step", CMDLEN)) {
01000 if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK) {
01001 Tcl_AppendResult(interp, "measure rmsf: bad frame step value", NULL);
01002 return TCL_ERROR;
01003 }
01004 } else {
01005 Tcl_AppendResult(interp, "measure rmsf: invalid syntax, no such keyword: ", argvcur, NULL);
01006 return TCL_ERROR;
01007 }
01008 }
01009 
01010 float *rmsf = new float[sel->selected];
01011 int ret_val = measure_rmsf(sel, app->moleculeList, first, last, step, rmsf);
01012 if (ret_val < 0) {
01013 Tcl_AppendResult(interp, "measure rmsf: ", measure_error(ret_val), NULL);
01014 delete [] rmsf;
01015 return TCL_ERROR;
01016 }
01017 
01018 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
01019 for (i=0; i<sel->selected; i++) {
01020 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(rmsf[i]));
01021 }
01022 Tcl_SetObjResult(interp, tcl_result);
01023 
01024 delete [] rmsf;
01025 return TCL_OK;
01026 }
01027 
01028 
01029 // measure radius of gyration for selected atoms
01030 static int vmd_measure_rgyr(VMDApp *app, int argc, Tcl_Obj *const objv[], Tcl_Interp *interp) {
01031 if (argc != 2 && argc != 4) {
01032 Tcl_WrongNumArgs(interp, 2, objv-1,
01033 (char *)"<selection> [weight <weights>]");
01034 return TCL_ERROR;
01035 }
01036 if (argc == 4 && strcmp(Tcl_GetStringFromObj(objv[2],NULL), "weight")) {
01037 Tcl_SetResult(interp, (char *) "measure rgyr: parameter can only be 'weight'", TCL_STATIC);
01038 return TCL_ERROR;
01039 }
01040 
01041 // get the selection
01042 AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
01043 if (!sel) {
01044 Tcl_SetResult(interp, (char *) "measure rgyr: no atom selection", TCL_STATIC);
01045 return TCL_ERROR;
01046 }
01047 
01048 // get the weight
01049 float *weight = new float[sel->selected];
01050 int ret_val = 0;
01051 if (argc == 2) { // only from atom selection, so weight is 1
01052 ret_val = tcl_get_weights(interp, app, sel, NULL, weight);
01053 } else {
01054 ret_val = tcl_get_weights(interp, app, sel, objv[3], weight);
01055 }
01056 if (ret_val < 0) {
01057 Tcl_AppendResult(interp, "measure rgyr: ", measure_error(ret_val), NULL);
01058 delete [] weight;
01059 return TCL_ERROR;
01060 }
01061 
01062 float rgyr;
01063 ret_val = measure_rgyr(sel, app->moleculeList, weight, &rgyr);
01064 delete [] weight;
01065 if (ret_val < 0) {
01066 Tcl_AppendResult(interp, "measure rgyr: ", measure_error(ret_val), NULL);
01067 return TCL_ERROR;
01068 }
01069 Tcl_SetObjResult(interp, Tcl_NewDoubleObj(rgyr));
01070 return TCL_OK;
01071 }
01072 
01073 
01074 // Function: vmd_measure_minmax <selection>
01075 // Returns: the cartesian range of a selection (min/max){x,y,z}
01076 // Example: vmd_measure_minmax atomselect76
01077 // {-5 0 0} {15 10 11.2}
01078 static int vmd_measure_minmax(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01079 const float *radii = NULL;
01080 if (argc != 2 && argc != 3) {
01081 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<selection> [-withradii]");
01082 return TCL_ERROR;
01083 }
01084 if (argc == 3 && strcmp(Tcl_GetStringFromObj(objv[2],NULL), "-withradii")) {
01085 Tcl_SetResult(interp, (char *) "measure minmax: parameter can only be '-withradii'", TCL_STATIC);
01086 return TCL_ERROR;
01087 }
01088 
01089 AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
01090 if (!sel) {
01091 Tcl_AppendResult(interp, "measure minmax: no atom selection", NULL);
01092 return TCL_ERROR;
01093 }
01094 
01095 float min_coord[3], max_coord[3];
01096 const float *framepos = sel->coordinates(app->moleculeList);
01097 if (!framepos) return TCL_ERROR;
01098 
01099 // get atom radii if requested
01100 if (argc == 3) {
01101 Molecule *mol = app->moleculeList->mol_from_id(sel->molid());
01102 radii = mol->extraflt.data("radius");
01103 } 
01104 
01105 int ret_val = measure_minmax(sel->num_atoms, sel->on, framepos, radii, 
01106 min_coord, max_coord);
01107 if (ret_val < 0) {
01108 Tcl_AppendResult(interp, "measure minmax: ", measure_error(ret_val), NULL);
01109 return TCL_ERROR;
01110 }
01111 
01112 Tcl_Obj *list1 = Tcl_NewListObj(0, NULL);
01113 Tcl_Obj *list2 = Tcl_NewListObj(0, NULL);
01114 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
01115 
01116 Tcl_ListObjAppendElement(interp, list1, Tcl_NewDoubleObj(min_coord[0]));
01117 Tcl_ListObjAppendElement(interp, list1, Tcl_NewDoubleObj(min_coord[1]));
01118 Tcl_ListObjAppendElement(interp, list1, Tcl_NewDoubleObj(min_coord[2]));
01119 
01120 Tcl_ListObjAppendElement(interp, list2, Tcl_NewDoubleObj(max_coord[0]));
01121 Tcl_ListObjAppendElement(interp, list2, Tcl_NewDoubleObj(max_coord[1]));
01122 Tcl_ListObjAppendElement(interp, list2, Tcl_NewDoubleObj(max_coord[2]));
01123 
01124 Tcl_ListObjAppendElement(interp, tcl_result, list1);
01125 Tcl_ListObjAppendElement(interp, tcl_result, list2);
01126 Tcl_SetObjResult(interp, tcl_result);
01127 
01128 return TCL_OK;
01129 }
01130 
01131 
01132 static int vmd_measure_rmsdperresidue(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01133 if (argc !=3 && argc != 5) {
01134 Tcl_WrongNumArgs(interp, 2, objv-1, 
01135 (char *)"<sel1> <sel2> [weight <weights>]");
01136 return TCL_ERROR;
01137 }
01138 
01139 // get the selections
01140 AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
01141 AtomSel *sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2],NULL));
01142 if (!sel1 || !sel2) {
01143 Tcl_AppendResult(interp, "measure rmsd: no atom selection", NULL);
01144 return TCL_ERROR;
01145 }
01146 
01147 if (sel1->selected != sel2->selected) {
01148 Tcl_AppendResult(interp, "measure rmsd: selections must have the same number of atoms", NULL);
01149 return TCL_ERROR;
01150 }
01151 if (!sel1->selected) {
01152 Tcl_AppendResult(interp, "measure rmsd: no atoms selected", NULL);
01153 return TCL_ERROR;
01154 }
01155 float *weight = new float[sel1->selected];
01156 int ret_val = 0;
01157 if (argc == 3) {
01158 ret_val = tcl_get_weights(interp, app, sel1, NULL, weight);
01159 } else {
01160 ret_val = tcl_get_weights(interp, app, sel1, objv[4], weight);
01161 }
01162 if (ret_val < 0) {
01163 Tcl_AppendResult(interp, "measure rmsd: ", measure_error(ret_val), NULL);
01164 delete [] weight;
01165 return TCL_ERROR;
01166 }
01167 
01168 // compute the rmsd
01169 float *rmsd = new float[sel1->selected];
01170 int rc = measure_rmsd_perresidue(sel1, sel2, app->moleculeList, 
01171 sel1->selected, weight, rmsd);
01172 if (rc < 0) {
01173 Tcl_AppendResult(interp, "measure rmsd: ", measure_error(rc), NULL);
01174 delete [] weight;
01175 delete [] rmsd;
01176 return TCL_ERROR;
01177 }
01178 delete [] weight;
01179 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
01180 int i;
01181 for (i=0; i<rc; i++) {
01182 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(rmsd[i]));
01183 }
01184 delete [] rmsd;
01185 Tcl_SetObjResult(interp, tcl_result);
01186 
01187 return TCL_OK;
01188 }
01189 
01190 // Function: vmd_measure_rmsd <selection1> <selection2> 
01191 // {weight [none|atom value|string array]}
01192 //
01193 // Returns the RMSD between the two selection, taking the weight (if
01194 // any) into account. If number of elements in the weight != num_atoms
01195 // in sel1 then (num in weight = num selected in sel1 = num selected in
01196 // sel2) else (num in weight = total num in sel1 = total num in sel2).
01197 // The weights are taken from the FIRST selection, if needed
01198 // 
01199 // Examples:
01200 // set sel1 [atomselect 0 all]
01201 // set sel2 [atomselect 1 all]
01202 // measure rmsd $sel1 $sel2
01203 // measure rmsd $sel1 $sel2 weight mass
01204 // set sel3 [atomselect 0 "index 3 4 5"]
01205 // set sel4 [atomselect 1 "index 8 5 9"] # gets turned to 5 8 9
01206 // measure rmsd $sel3 $sel4 weight occupancy
01207 //
01208 static int vmd_measure_rmsd(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp)
01209 {
01210 if (argc !=3 && argc != 5) {
01211 Tcl_WrongNumArgs(interp, 2, objv-1, 
01212 (char *)"<sel1> <sel2> [weight <weights>]");
01213 return TCL_ERROR;
01214 }
01215 
01216 // get the selections
01217 AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
01218 AtomSel *sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2],NULL));
01219 if (!sel1 || !sel2) {
01220 Tcl_AppendResult(interp, "measure rmsd: no atom selection", NULL);
01221 return TCL_ERROR;
01222 }
01223 
01224 if (sel1->selected != sel2->selected) {
01225 Tcl_AppendResult(interp, "measure rmsd: selections must have the same number of atoms", NULL);
01226 return TCL_ERROR;
01227 }
01228 if (!sel1->selected) {
01229 Tcl_AppendResult(interp, "measure rmsd: no atoms selected", NULL);
01230 return TCL_ERROR;
01231 }
01232 
01233 float *weight = new float[sel1->selected];
01234 int ret_val = 0;
01235 if (argc == 3) {
01236 ret_val = tcl_get_weights(interp, app, sel1, NULL, weight);
01237 } else {
01238 ret_val = tcl_get_weights(interp, app, sel1, objv[4], weight);
01239 }
01240 if (ret_val < 0) {
01241 Tcl_AppendResult(interp, "measure rmsd: ", measure_error(ret_val), NULL);
01242 delete [] weight;
01243 return TCL_ERROR;
01244 }
01245 
01246 // compute the rmsd
01247 float rmsd = 0;
01248 const float *x = sel1->coordinates(app->moleculeList);
01249 const float *y = sel2->coordinates(app->moleculeList);
01250 if (!x || !y) {
01251 delete [] weight;
01252 return TCL_ERROR;
01253 }
01254 
01255 ret_val = measure_rmsd(sel1, sel2, sel1->selected, x, y, weight, &rmsd);
01256 delete [] weight;
01257 if (ret_val < 0) {
01258 Tcl_AppendResult(interp, "measure rmsd: ", measure_error(ret_val), NULL);
01259 return TCL_ERROR;
01260 }
01261 Tcl_SetObjResult(interp, Tcl_NewDoubleObj(rmsd));
01262 
01263 return TCL_OK;
01264 }
01265 
01266 
01268 // measure rmsd_qcp $sel1 $sel2 [weight <weights>]
01269 static int vmd_measure_rmsd_qcp(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01270 if (argc !=3 && argc != 5) {
01271 Tcl_WrongNumArgs(interp, 2, objv-1, 
01272 (char *)"<sel1> <sel2> [weight <weights>]");
01273 return TCL_ERROR;
01274 }
01275 // get the selections
01276 AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
01277 AtomSel *sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2],NULL));
01278 if (!sel1 || !sel2) {
01279 Tcl_AppendResult(interp, "measure rmsd: no atom selection", NULL);
01280 return TCL_ERROR;
01281 }
01282 
01283 if (sel1->selected != sel2->selected) {
01284 Tcl_AppendResult(interp, "measure rmsd: selections must have the same number of atoms", NULL);
01285 return TCL_ERROR;
01286 }
01287 if (!sel1->selected) {
01288 Tcl_AppendResult(interp, "measure rmsd: no atoms selected", NULL);
01289 return TCL_ERROR;
01290 }
01291 float *weight = new float[sel1->selected];
01292 {
01293 int ret_val;
01294 if (argc == 3) {
01295 ret_val = tcl_get_weights(interp, app, sel1, NULL, weight);
01296 } else {
01297 ret_val = tcl_get_weights(interp, app, sel1, objv[4], weight);
01298 }
01299 if (ret_val < 0) {
01300 Tcl_AppendResult(interp, "measure rmsd: ", measure_error(ret_val), NULL);
01301 delete [] weight;
01302 return TCL_ERROR;
01303 }
01304 }
01305 
01306 // compute the rmsd
01307 {
01308 float rmsd = 0;
01309 const float *x = sel1->coordinates(app->moleculeList);
01310 const float *y = sel2->coordinates(app->moleculeList);
01311 if (!x || !y) {
01312 delete [] weight;
01313 return TCL_ERROR;
01314 }
01315 int ret_val = measure_rmsd_qcp(app, sel1, sel2, sel1->selected, x, y, weight, &rmsd);
01316 delete [] weight;
01317 if (ret_val < 0) {
01318 Tcl_AppendResult(interp, "measure rmsd: ", measure_error(ret_val), NULL);
01319 return TCL_ERROR;
01320 }
01321 Tcl_SetObjResult(interp, Tcl_NewDoubleObj(rmsd));
01322 }
01323 return TCL_OK;
01324 }
01325 
01326 
01328 // measure rmsdmat_qcp $sel1 [weight <weights>] start s end e step s
01329 static int vmd_measure_rmsdmat_qcp(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01330 int first = 0; // start with first frame by default
01331 int last = -1; // finish with last frame by default
01332 int step = 1; // use all frames by default
01333 
01334 if (argc < 2 || argc > 9) {
01335 Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<sel> [weight <weights>] [first <first>] [last <last>] [step <step>]");
01336 return TCL_ERROR;
01337 }
01338 AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL)
01339 );
01340 if (!sel) {
01341 Tcl_AppendResult(interp, "measure rmsdmat_qcp: no atom selection", NULL);
01342 return TCL_ERROR;
01343 }
01344 
01345 int i;
01346 for (i=2; i<argc; i+=2) {
01347 char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
01348 if (!strupncmp(argvcur, "first", CMDLEN)) {
01349 if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
01350 Tcl_AppendResult(interp, "measure rmsdmat_qcp: bad first frame value", NULL);
01351 return TCL_ERROR;
01352 }
01353 } else if (!strupncmp(argvcur, "last", CMDLEN)) {
01354 if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
01355 Tcl_AppendResult(interp, "measure rmsdmat_qcp: bad last frame value", NULL);
01356 return TCL_ERROR;
01357 }
01358 } else if (!strupncmp(argvcur, "step", CMDLEN)) {
01359 if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK) {
01360 Tcl_AppendResult(interp, "measure rmsdmat_qcp: bad frame step value", NULL);
01361 return TCL_ERROR;
01362 }
01363 } else {
01364 Tcl_AppendResult(interp, "measure rmsdmat_qcp: invalid syntax, no such keyword: ", argvcur, NULL);
01365 return TCL_ERROR;
01366 }
01367 }
01368 
01369 float *weight = NULL;
01370 if (0) {
01371 weight = new float[sel->selected];
01372 int ret_val;
01373 if (argc == 2) {
01374 ret_val = tcl_get_weights(interp, app, sel, NULL, weight);
01375 } else {
01376 ret_val = tcl_get_weights(interp, app, sel, objv[3], weight);
01377 }
01378 
01379 if (ret_val < 0) {
01380 Tcl_AppendResult(interp, "measure rmsdmat_qcp: ", measure_error(ret_val),
01381 NULL);
01382 delete [] weight;
01383 return TCL_ERROR;
01384 }
01385 }
01386 
01387 //If no last frame is given, set it to be the last frame so the framecount is correct.
01388 if (last < 0)
01389 last = app->molecule_numframes(sel->molid()) - 1;
01390 int framecount = (last - first + 1) / step;
01391 float *rmsdmat = (float *) calloc(1, framecount * framecount * sizeof(float));
01392 
01393 // compute the rmsd matrix
01394 int ret_val = measure_rmsdmat_qcp(app, sel, app->moleculeList, 
01395 sel->selected, weight, 
01396 first, last, step, rmsdmat);
01397 delete [] weight;
01398 
01399 if (ret_val < 0) {
01400 Tcl_AppendResult(interp, "measure rmsdmat_qcp: ", measure_error(ret_val), NULL);
01401 return TCL_ERROR;
01402 }
01403 
01404 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
01405 long j;
01406 for (j=0; j<framecount; j++) {
01407 Tcl_Obj *rmsdlist = Tcl_NewListObj(0, NULL);
01408 for (i=0; i<framecount; i++) {
01409 Tcl_ListObjAppendElement(interp, rmsdlist, 
01410 Tcl_NewDoubleObj(rmsdmat[j*framecount + i]));
01411 }
01412 Tcl_ListObjAppendElement(interp, tcl_result, rmsdlist);
01413 }
01414 Tcl_SetObjResult(interp, tcl_result);
01415 
01416 free(rmsdmat);
01417 
01418 return TCL_OK;
01419 }
01420 
01421 
01423 // Experimental out-of-core variant 
01424 //
01425 // measure rmsdmat_qcp $sel1 [weight <weights>] start s end e step s
01426 static int vmd_measure_rmsdmat_qcp_ooc(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01427 int first = 0; // start with first frame by default
01428 int last = -1; // finish with last frame by default
01429 int step = 1; // use all frames by default
01430 int i;
01431 float *weight = NULL;
01432 int nfiles = 0;
01433 const char **trjfileset = NULL;
01434 
01435 if (argc < 2) {
01436 Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<sel> [weight <weights>] [first <first>] [last <last>] [step <step>] files [file1] [fileN]");
01437 return TCL_ERROR;
01438 }
01439 AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL)
01440 );
01441 if (!sel) {
01442 Tcl_AppendResult(interp, "measure rmsdmat_qcp: no atom selection", NULL);
01443 return TCL_ERROR;
01444 }
01445 
01446 for (i=2; i<argc; i+=2) {
01447 char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
01448 if (!strupncmp(argvcur, "first", CMDLEN)) {
01449 if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
01450 Tcl_AppendResult(interp, "measure rmsdmat_qcp_ooc: bad first frame value", NULL);
01451 return TCL_ERROR;
01452 }
01453 } else if (!strupncmp(argvcur, "last", CMDLEN)) {
01454 if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
01455 Tcl_AppendResult(interp, "measure rmsdmat_qcp_ooc: bad last frame value", NULL);
01456 return TCL_ERROR;
01457 }
01458 } else if (!strupncmp(argvcur, "step", CMDLEN)) {
01459 if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK) {
01460 Tcl_AppendResult(interp, "measure rmsdmat_qcp_ooc: bad frame step value", NULL);
01461 return TCL_ERROR;
01462 }
01463 } else if (!strupncmp(argvcur, "files", CMDLEN)) {
01464 int list_num;
01465 Tcl_Obj **list_data;
01466 if (Tcl_ListObjGetElements(interp, objv[i+1], &list_num, &list_data) != TCL_OK) {
01467 Tcl_AppendResult(interp, "measure rmsdmat_qcp_ooc: bad trajectory file list", NULL);
01468 return TCL_ERROR;
01469 }
01470 
01471 int f;
01472 nfiles = list_num;
01473 trjfileset = (const char **) calloc(1, nfiles * sizeof(const char *));
01474 for (f=0; f<nfiles; f++) {
01475 trjfileset[f] = Tcl_GetStringFromObj(list_data[f], NULL);
01476 printf("File[%d] '%s'\n", f, trjfileset[f]);
01477 }
01478 } else {
01479 Tcl_AppendResult(interp, "measure rmsdmat_qcp_ooc: invalid syntax, no such keyword: ", argvcur, NULL);
01480 return TCL_ERROR;
01481 }
01482 }
01483 
01484 #if 0
01485 if (0) {
01486 weight = new float[sel->selected];
01487 int ret_val;
01488 if (argc == 2) {
01489 ret_val = tcl_get_weights(interp, app, sel, NULL, weight);
01490 } else {
01491 ret_val = tcl_get_weights(interp, app, sel, objv[3], weight);
01492 }
01493 
01494 if (ret_val < 0) {
01495 Tcl_AppendResult(interp, "measure rmsdmat_qcp: ", measure_error(ret_val),
01496 NULL);
01497 delete [] weight;
01498 return TCL_ERROR;
01499 }
01500 }
01501 #endif
01502 
01503 // compute the rmsd matrix
01504 float *rmsdmat = NULL;
01505 int framecount = 0; // returned after work is done
01506 int ret_val = measure_rmsdmat_qcp_ooc(app, sel, app->moleculeList, 
01507 nfiles, trjfileset,
01508 sel->selected, weight, 
01509 first, last, step, 
01510 framecount, rmsdmat);
01511 delete [] weight;
01512 
01513 if (ret_val < 0) {
01514 Tcl_AppendResult(interp, "measure rmsdmat_qcp_ooc: ", measure_error(ret_val), NULL);
01515 return TCL_ERROR;
01516 }
01517 
01518 #if 0
01519 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
01520 long j;
01521 for (j=0; j<framecount; j++) {
01522 Tcl_Obj *rmsdlist = Tcl_NewListObj(0, NULL);
01523 for (i=0; i<framecount; i++) {
01524 Tcl_ListObjAppendElement(interp, rmsdlist, 
01525 Tcl_NewDoubleObj(rmsdmat[j*framecount + i]));
01526 }
01527 Tcl_ListObjAppendElement(interp, tcl_result, rmsdlist);
01528 }
01529 Tcl_SetObjResult(interp, tcl_result);
01530 #endif
01531 
01532 if (trjfileset != NULL)
01533 free(trjfileset);
01534 
01535 if (rmsdmat != NULL)
01536 free(rmsdmat);
01537 
01538 return TCL_OK;
01539 }
01540 
01541 
01542 
01544 // measure fit $sel1 $sel2 [weight <weights>][
01545 static int vmd_measure_fit(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp)
01546 {
01547 AtomSel *sel1, *sel2;
01548 int *order = NULL;
01549 float *weight = NULL;
01550 int rc;
01551 
01552 if (argc != 3 && argc != 5 
01553 && argc != 7) {
01554 Tcl_WrongNumArgs(interp, 2, objv-1, 
01555 (char *)"<sel1> <sel2> [weight <weights>] [order <index list>]");
01556 return TCL_ERROR;
01557 } else if (argc == 5
01558 && strcmp("weight", Tcl_GetStringFromObj(objv[3], NULL)) 
01559 && strcmp("order", Tcl_GetStringFromObj(objv[3], NULL))) {
01560 Tcl_WrongNumArgs(interp, 2, objv-1, 
01561 (char *)"<sel1> <sel2> [weight <weights>] [order <index list>]");
01562 return TCL_ERROR;
01563 } else if (argc == 7 && 
01564 (strcmp("weight", Tcl_GetStringFromObj(objv[3], NULL)) || 
01565 strcmp("order", Tcl_GetStringFromObj(objv[5], NULL)))) {
01566 Tcl_WrongNumArgs(interp, 2, objv-1, 
01567 (char *)"<sel1> <sel2> [weight <weights>] [order <index list>]");
01568 return TCL_ERROR;
01569 }
01570 
01571 // get the selections
01572 sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
01573 sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2],NULL));
01574 
01575 if (!sel1 || !sel2) {
01576 Tcl_AppendResult(interp, "measure fit: no atom selection", NULL);
01577 return TCL_ERROR;
01578 }
01579 
01580 int num = sel1->selected;
01581 if (!num) {
01582 Tcl_AppendResult(interp, "measure fit: no atoms selected", NULL);
01583 return TCL_ERROR;
01584 }
01585 if (num != sel2->selected) {
01586 Tcl_AppendResult(interp, "measure fit: selections must have the same number of atoms", NULL);
01587 return TCL_ERROR;
01588 }
01589 
01590 // get the weights
01591 weight = new float[num]; 
01592 if (argc > 3 && !strcmp("weight", Tcl_GetStringFromObj(objv[3], NULL))) {
01593 // get user weight parameter
01594 rc = tcl_get_weights(interp, app, sel1, objv[4], weight);
01595 } else {
01596 // default to weights of 1.0
01597 rc = tcl_get_weights(interp, app, sel1, NULL, weight);
01598 }
01599 if (rc < 0) {
01600 Tcl_AppendResult(interp, "measure fit: ", measure_error(rc), NULL);
01601 delete [] weight;
01602 return TCL_ERROR;
01603 }
01604 
01605 
01606 int orderparam = 0;
01607 if (argc == 5 && !strcmp("order", Tcl_GetStringFromObj(objv[3], NULL))) {
01608 orderparam = 4;
01609 } else if (argc == 7 && !strcmp("order", Tcl_GetStringFromObj(objv[5], NULL))) {
01610 orderparam = 6;
01611 }
01612 
01613 if (orderparam != 0) {
01614 // get the atom order
01615 order = new int[num];
01616 rc = tcl_get_orders(interp, num, objv[orderparam], order);
01617 if (rc < 0) {
01618 Tcl_AppendResult(interp, "measure fit: ", measure_error(rc), NULL);
01619 delete [] order;
01620 return TCL_ERROR;
01621 }
01622 }
01623 
01624 // compute the transformation matrix
01625 Matrix4 T;
01626 const float *x = sel1->coordinates(app->moleculeList);
01627 const float *y = sel2->coordinates(app->moleculeList);
01628 
01629 int ret_val = MEASURE_ERR_NOMOLECULE;
01630 if (x && y) 
01631 ret_val = measure_fit(sel1, sel2, x, y, weight, order, &T);
01632 
01633 delete [] weight;
01634 
01635 if (order != NULL)
01636 delete [] order;
01637 
01638 if (ret_val < 0) {
01639 Tcl_AppendResult(interp, "measure fit: ", measure_error(ret_val), NULL);
01640 return TCL_ERROR;
01641 }
01642 
01643 // and return the matrix
01644 tcl_append_matrix(interp, T.mat);
01645 return TCL_OK;
01646 }
01647 
01649 // measure inverse 4x4matrix
01650 static int vmd_measure_inverse(int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01651 // make there there is exactly one matrix
01652 if (argc != 2) {
01653 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<matrix>");
01654 return TCL_ERROR;
01655 }
01656 // Get the first matrix
01657 Matrix4 inv;
01658 if (tcl_get_matrix("measure inverse: ",interp,objv[1],inv.mat) != TCL_OK) {
01659 return TCL_ERROR;
01660 }
01661 if (inv.inverse()) {
01662 Tcl_AppendResult(interp, "Singular Matrix; inverse not computed", NULL);
01663 return TCL_ERROR;
01664 }
01665 tcl_append_matrix(interp, inv.mat);
01666 return TCL_OK;
01667 }
01668 
01669 // Find all atoms p in sel1 and q in sel2 within the cutoff. 
01670 static int vmd_measure_contacts(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01671 
01672 // Cutoff, and either one or two atom selections
01673 if (argc != 3 && argc != 4) {
01674 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<cutoff> <sel1> [<sel2>]");
01675 return TCL_ERROR;
01676 }
01677 AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2],NULL));
01678 if (!sel1) {
01679 Tcl_AppendResult(interp, "measure contacts: no atom selection", NULL);
01680 return TCL_ERROR;
01681 }
01682 AtomSel *sel2 = NULL;
01683 if (argc == 4) {
01684 sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[3],NULL));
01685 if (!sel2) {
01686 Tcl_AppendResult(interp, "measure contacts: no atom selection", NULL);
01687 return TCL_ERROR;
01688 }
01689 }
01690 if (!sel2) sel2 = sel1;
01691 
01692 double cutoff;
01693 if (Tcl_GetDoubleFromObj(interp, objv[1], &cutoff) != TCL_OK)
01694 return TCL_ERROR;
01695 
01696 const float *pos1 = sel1->coordinates(app->moleculeList);
01697 const float *pos2 = sel2->coordinates(app->moleculeList);
01698 if (!pos1 || !pos2) {
01699 Tcl_AppendResult(interp, "measure contacts: error, molecule contains no coordinates", NULL);
01700 return TCL_ERROR;
01701 }
01702 Molecule *mol1 = app->moleculeList->mol_from_id(sel1->molid());
01703 Molecule *mol2 = app->moleculeList->mol_from_id(sel2->molid());
01704 
01705 GridSearchPair *pairlist = vmd_gridsearch3(pos1, sel1->num_atoms, sel1->on, pos2, sel2->num_atoms, sel2->on, (float) cutoff, -1, (sel1->num_atoms + sel2->num_atoms) * 27L);
01706 GridSearchPair *p, *tmp;
01707 Tcl_Obj *list1 = Tcl_NewListObj(0, NULL);
01708 Tcl_Obj *list2 = Tcl_NewListObj(0, NULL);
01709 for (p=pairlist; p != NULL; p=tmp) {
01710 // throw out pairs that are already bonded
01711 MolAtom *a1 = mol1->atom(p->ind1);
01712 if (mol1 != mol2 || !a1->bonded(p->ind2)) {
01713 Tcl_ListObjAppendElement(interp, list1, Tcl_NewIntObj(p->ind1));
01714 Tcl_ListObjAppendElement(interp, list2, Tcl_NewIntObj(p->ind2));
01715 }
01716 tmp = p->next;
01717 free(p);
01718 }
01719 Tcl_Obj *result = Tcl_NewListObj(0, NULL);
01720 Tcl_ListObjAppendElement(interp, result, list1);
01721 Tcl_ListObjAppendElement(interp, result, list2);
01722 Tcl_SetObjResult(interp, result);
01723 return TCL_OK;
01724 }
01725 
01726 
01727 // measure g(r) for two selections, with delta, rmax, usepbc, first/last/step 
01728 // frame parameters the code will compute the normalized histogram.
01729 static int vmd_measure_gofr(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01730 int i;
01731 // initialize optional arguments to default values
01732 double rmax=10.0;
01733 double delta=0.1;
01734 int usepbc=0;
01735 int selupdate=0;
01736 int first=-1, last=-1, step=1;
01737 int rc;
01738 
01739 // argument error message
01740 const char *argerrmsg = "<sel1> <sel2> [delta <value>] [rmax <value>] [usepbc <bool>] [selupdate <bool>] [first <first>] [last <last>] [step <step>]";
01741 
01742 // Two atom selections and optional keyword/value pairs.
01743 if ((argc < 3) || (argc > 17) || (argc % 2 == 0) ) {
01744 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
01745 return TCL_ERROR;
01746 }
01747 
01748 // check atom selections
01749 AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1], NULL));
01750 if (!sel1) {
01751 Tcl_AppendResult(interp, "measure gofr: invalid first atom selection", NULL);
01752 return TCL_ERROR;
01753 }
01754 
01755 AtomSel *sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2], NULL));
01756 if (!sel2) {
01757 Tcl_AppendResult(interp, "measure gofr: invalid second atom selection", NULL);
01758 return TCL_ERROR;
01759 }
01760 
01761 // parse optional arguments
01762 for (i=3; i<argc; i+=2) {
01763 const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
01764 if (i==(argc-1)) {
01765 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
01766 return TCL_ERROR;
01767 }
01768 if (!strcmp(opt, "delta")) {
01769 if (Tcl_GetDoubleFromObj(interp, objv[i+1], &delta) != TCL_OK)
01770 return TCL_ERROR;
01771 if (delta <= 0.0) {
01772 Tcl_AppendResult(interp, "measure gofr: invalid 'delta' value", NULL);
01773 return TCL_ERROR;
01774 }
01775 } else if (!strcmp(opt, "rmax")) {
01776 if (Tcl_GetDoubleFromObj(interp, objv[i+1], &rmax) != TCL_OK)
01777 return TCL_ERROR;
01778 if (rmax <= 0.0) {
01779 Tcl_AppendResult(interp, "measure gofr: invalid 'rmax' value", NULL);
01780 return TCL_ERROR;
01781 }
01782 } else if (!strcmp(opt, "usepbc")) {
01783 if (Tcl_GetBooleanFromObj(interp, objv[i+1], &usepbc) != TCL_OK)
01784 return TCL_ERROR;
01785 } else if (!strcmp(opt, "selupdate")) {
01786 if (Tcl_GetBooleanFromObj(interp, objv[i+1], &selupdate) != TCL_OK)
01787 return TCL_ERROR;
01788 } else if (!strcmp(opt, "first")) {
01789 if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK)
01790 return TCL_ERROR;
01791 } else if (!strcmp(opt, "last")) {
01792 if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK)
01793 return TCL_ERROR;
01794 } else if (!strcmp(opt, "step")) {
01795 if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK)
01796 return TCL_ERROR;
01797 } else { // unknown keyword.
01798 Tcl_AppendResult(interp, "unknown keyword '", opt, "'. usage: measure gofr ", argerrmsg, NULL);
01799 return TCL_ERROR;
01800 }
01801 }
01802 
01803 // allocate and initialize histogram arrays
01804 int count_h = (int)(rmax / delta + 1.0);
01805 double *gofr = new double[count_h];
01806 double *numint = new double[count_h];
01807 double *histog = new double[count_h];
01808 int *framecntr = new int[3];
01809 
01810 // do the gofr calculation
01811 rc = measure_gofr(sel1, sel2, app->moleculeList,
01812 count_h, gofr, numint, histog,
01813 (float) delta,
01814 first, last, step, framecntr,
01815 usepbc, selupdate);
01816 
01817 // XXX: this needs a 'case' structure to provide more meaninful error messages.
01818 if (rc != MEASURE_NOERR) { 
01819 Tcl_AppendResult(interp, "measure gofr: error during g(r) calculation.", NULL);
01820 return TCL_ERROR;
01821 }
01822 
01823 // convert the results of the lowlevel call to tcl lists
01824 // and build a list from them as return value.
01825 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
01826 Tcl_Obj *tcl_rlist = Tcl_NewListObj(0, NULL);
01827 Tcl_Obj *tcl_gofr = Tcl_NewListObj(0, NULL);
01828 Tcl_Obj *tcl_numint = Tcl_NewListObj(0, NULL);
01829 Tcl_Obj *tcl_histog = Tcl_NewListObj(0, NULL);
01830 Tcl_Obj *tcl_frames = Tcl_NewListObj(0, NULL);
01831 
01832 // build lists with results ready for plotting
01833 for (i=0; i<count_h; i++) { 
01834 Tcl_ListObjAppendElement(interp, tcl_rlist, Tcl_NewDoubleObj(delta * ((double)i + 0.5)));
01835 Tcl_ListObjAppendElement(interp, tcl_gofr, Tcl_NewDoubleObj(gofr[i]));
01836 Tcl_ListObjAppendElement(interp, tcl_numint, Tcl_NewDoubleObj(numint[i]));
01837 Tcl_ListObjAppendElement(interp, tcl_histog, Tcl_NewDoubleObj(histog[i]));
01838 }
01839 
01840 // build list with number of frames: 
01841 // total, skipped and processed (one entry for each algorithm).
01842 Tcl_ListObjAppendElement(interp, tcl_frames, Tcl_NewIntObj(framecntr[0]));
01843 Tcl_ListObjAppendElement(interp, tcl_frames, Tcl_NewIntObj(framecntr[1]));
01844 Tcl_ListObjAppendElement(interp, tcl_frames, Tcl_NewIntObj(framecntr[2]));
01845 
01846 // build final list-of-lists as return value
01847 Tcl_ListObjAppendElement(interp, tcl_result, tcl_rlist);
01848 Tcl_ListObjAppendElement(interp, tcl_result, tcl_gofr);
01849 Tcl_ListObjAppendElement(interp, tcl_result, tcl_numint);
01850 Tcl_ListObjAppendElement(interp, tcl_result, tcl_histog);
01851 Tcl_ListObjAppendElement(interp, tcl_result, tcl_frames);
01852 Tcl_SetObjResult(interp, tcl_result);
01853 
01854 delete [] gofr;
01855 delete [] numint;
01856 delete [] histog;
01857 delete [] framecntr;
01858 return TCL_OK;
01859 }
01860 
01861 
01862 // measure g(r) for two selections, with delta, rmax, usepbc, first/last/step 
01863 // frame parameters the code will compute the normalized histogram.
01864 static int vmd_measure_rdf(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01865 int i;
01866 // initialize optional arguments to default values
01867 double rmax=10.0;
01868 double delta=0.1;
01869 int usepbc=0;
01870 int selupdate=0;
01871 int first=-1, last=-1, step=1;
01872 int rc;
01873 
01874 // argument error message
01875 const char *argerrmsg = "<sel1> <sel2> [delta <value>] [rmax <value>] [usepbc <bool>] [selupdate <bool>] [first <first>] [last <last>] [step <step>]";
01876 
01877 // Two atom selections and optional keyword/value pairs.
01878 if ((argc < 3) || (argc > 17) || (argc % 2 == 0) ) {
01879 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
01880 return TCL_ERROR;
01881 }
01882 
01883 // check atom selections
01884 AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1], NULL));
01885 if (!sel1) {
01886 Tcl_AppendResult(interp, "measure rdf: invalid first atom selection", NULL);
01887 return TCL_ERROR;
01888 }
01889 
01890 AtomSel *sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2], NULL));
01891 if (!sel2) {
01892 Tcl_AppendResult(interp, "measure rdf: invalid second atom selection", NULL);
01893 return TCL_ERROR;
01894 }
01895 
01896 // parse optional arguments
01897 for (i=3; i<argc; i+=2) {
01898 const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
01899 if (i==(argc-1)) {
01900 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
01901 return TCL_ERROR;
01902 }
01903 if (!strcmp(opt, "delta")) {
01904 if (Tcl_GetDoubleFromObj(interp, objv[i+1], &delta) != TCL_OK)
01905 return TCL_ERROR;
01906 if (delta <= 0.0) {
01907 Tcl_AppendResult(interp, "measure rdf: invalid 'delta' value", NULL);
01908 return TCL_ERROR;
01909 }
01910 } else if (!strcmp(opt, "rmax")) {
01911 if (Tcl_GetDoubleFromObj(interp, objv[i+1], &rmax) != TCL_OK)
01912 return TCL_ERROR;
01913 if (rmax <= 0.0) {
01914 Tcl_AppendResult(interp, "measure rdf: invalid 'rmax' value", NULL);
01915 return TCL_ERROR;
01916 }
01917 } else if (!strcmp(opt, "usepbc")) {
01918 if (Tcl_GetBooleanFromObj(interp, objv[i+1], &usepbc) != TCL_OK)
01919 return TCL_ERROR;
01920 } else if (!strcmp(opt, "selupdate")) {
01921 if (Tcl_GetBooleanFromObj(interp, objv[i+1], &selupdate) != TCL_OK)
01922 return TCL_ERROR;
01923 } else if (!strcmp(opt, "first")) {
01924 if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK)
01925 return TCL_ERROR;
01926 } else if (!strcmp(opt, "last")) {
01927 if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK)
01928 return TCL_ERROR;
01929 } else if (!strcmp(opt, "step")) {
01930 if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK)
01931 return TCL_ERROR;
01932 } else { // unknown keyword.
01933 Tcl_AppendResult(interp, "unknown keyword '", opt, "'. usage: measure rdf ", argerrmsg, NULL);
01934 return TCL_ERROR;
01935 }
01936 }
01937 
01938 // allocate and initialize histogram arrays
01939 int count_h = (int)(rmax / delta + 1.0);
01940 double *gofr = new double[count_h];
01941 double *numint = new double[count_h];
01942 double *histog = new double[count_h];
01943 int *framecntr = new int[3];
01944 
01945 // do the gofr calculation
01946 rc = measure_rdf(app, sel1, sel2, app->moleculeList,
01947 count_h, gofr, numint, histog,
01948 (float) delta,
01949 first, last, step, framecntr,
01950 usepbc, selupdate);
01951 
01952 // XXX: this needs a 'case' structure to provide more meaninful error messages.
01953 if (rc != MEASURE_NOERR) { 
01954 Tcl_AppendResult(interp, "measure rdf: error during rdf calculation.", NULL);
01955 return TCL_ERROR;
01956 }
01957 
01958 // convert the results of the lowlevel call to tcl lists
01959 // and build a list from them as return value.
01960 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
01961 Tcl_Obj *tcl_rlist = Tcl_NewListObj(0, NULL);
01962 Tcl_Obj *tcl_gofr = Tcl_NewListObj(0, NULL);
01963 Tcl_Obj *tcl_numint = Tcl_NewListObj(0, NULL);
01964 Tcl_Obj *tcl_histog = Tcl_NewListObj(0, NULL);
01965 Tcl_Obj *tcl_frames = Tcl_NewListObj(0, NULL);
01966 
01967 // build lists with results ready for plotting
01968 for (i=0; i<count_h; i++) { 
01969 Tcl_ListObjAppendElement(interp, tcl_rlist, Tcl_NewDoubleObj(delta * ((double)i + 0.5)));
01970 Tcl_ListObjAppendElement(interp, tcl_gofr, Tcl_NewDoubleObj(gofr[i]));
01971 Tcl_ListObjAppendElement(interp, tcl_numint, Tcl_NewDoubleObj(numint[i]));
01972 Tcl_ListObjAppendElement(interp, tcl_histog, Tcl_NewDoubleObj(histog[i]));
01973 }
01974 
01975 // build list with number of frames: 
01976 // total, skipped and processed (one entry for each algorithm).
01977 Tcl_ListObjAppendElement(interp, tcl_frames, Tcl_NewIntObj(framecntr[0]));
01978 Tcl_ListObjAppendElement(interp, tcl_frames, Tcl_NewIntObj(framecntr[1]));
01979 Tcl_ListObjAppendElement(interp, tcl_frames, Tcl_NewIntObj(framecntr[2]));
01980 
01981 // build final list-of-lists as return value
01982 Tcl_ListObjAppendElement(interp, tcl_result, tcl_rlist);
01983 Tcl_ListObjAppendElement(interp, tcl_result, tcl_gofr);
01984 Tcl_ListObjAppendElement(interp, tcl_result, tcl_numint);
01985 Tcl_ListObjAppendElement(interp, tcl_result, tcl_histog);
01986 Tcl_ListObjAppendElement(interp, tcl_result, tcl_frames);
01987 Tcl_SetObjResult(interp, tcl_result);
01988 
01989 delete [] gofr;
01990 delete [] numint;
01991 delete [] histog;
01992 delete [] framecntr;
01993 return TCL_OK;
01994 }
01995 
01996 
01998 static int vmd_measure_cluster(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01999 int i,j;
02000 // initialize optional arguments to default values
02001 int algorithm=0; // will be MEASURE_CLUSTER_QT when finished
02002 int likeness=MEASURE_DIST_FITRMSD;
02003 int numcluster=5;
02004 double cutoff=1.0;
02005 float *weights=NULL;
02006 int selupdate=0;
02007 int first=0, last=-1, step=1;
02008 int rc;
02009 
02010 // argument error message
02011 const char *argerrmsg = "<sel> [num <#clusters>] [distfunc <flag>] "
02012 "[cutoff <cutoff>] [first <first>] [last <last>] [step <step>] "
02013 "[selupdate <bool>] [weight <weights>]";
02014 
02015 // Two atom selections and optional keyword/value pairs.
02016 if ((argc < 2) || (argc > 19) || ((argc-1) % 2 == 0) ) {
02017 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
02018 return TCL_ERROR;
02019 }
02020 
02021 // check atom selection
02022 AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1], NULL));
02023 if (!sel) {
02024 Tcl_AppendResult(interp, "measure cluster: invalid atom selection", NULL);
02025 return TCL_ERROR;
02026 }
02027 if (!app->molecule_valid_id(sel->molid())) return MEASURE_ERR_NOMOLECULE;
02028 
02029 // parse optional arguments
02030 for (i=2; i<argc; i+=2) {
02031 const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
02032 if (i==(argc-1)) {
02033 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
02034 return TCL_ERROR;
02035 }
02036 if (!strcmp(opt, "num")) {
02037 if (Tcl_GetIntFromObj(interp, objv[i+1], &numcluster) != TCL_OK)
02038 return TCL_ERROR;
02039 if (numcluster < 1) {
02040 Tcl_AppendResult(interp, "measure cluster: invalid 'num' value (cannot be smaller than 1)", NULL);
02041 return TCL_ERROR;
02042 }
02043 } else if (!strcmp(opt, "cutoff")) {
02044 if (Tcl_GetDoubleFromObj(interp, objv[i+1], &cutoff) != TCL_OK)
02045 return TCL_ERROR;
02046 if (cutoff <= 0.0) {
02047 Tcl_AppendResult(interp, "measure cluster: invalid 'cutoff' value (should be larger than 0.0)", NULL);
02048 return TCL_ERROR;
02049 }
02050 } else if (!strcmp(opt, "distfunc")) {
02051 char *argstr = Tcl_GetStringFromObj(objv[i+1], NULL);
02052 if (!strcmp(argstr,"rmsd")) {
02053 likeness = MEASURE_DIST_RMSD;
02054 } else if (!strcmp(argstr,"fitrmsd")) {
02055 likeness = MEASURE_DIST_FITRMSD;
02056 } else if (!strcmp(argstr,"rgyrd")) {
02057 likeness = MEASURE_DIST_RGYRD;
02058 } else {
02059 Tcl_AppendResult(interp, "measure cluster: unknown distance function (supported are 'rmsd', 'rgyrd' and 'fitrmsd')", NULL);
02060 return TCL_ERROR;
02061 }
02062 } else if (!strcmp(opt, "selupdate")) {
02063 if (Tcl_GetBooleanFromObj(interp, objv[i+1], &selupdate) != TCL_OK)
02064 return TCL_ERROR;
02065 } else if (!strcmp(opt, "weight")) {
02066 // NOTE: we cannot use tcl_get_weights here, since we have to 
02067 // get a full (all atoms) list of weights as we may be updating
02068 // the selection in the process. Also we don't support explicit
02069 // lists of weights for now.
02070 const char *weight_string = Tcl_GetStringFromObj(objv[i+1], NULL);
02071 weights = new float[sel->num_atoms];
02072 
02073 if (!weight_string || !strcmp(weight_string, "none")) {
02074 for (j=0; j<sel->num_atoms; j++) weights[j]=1.0f;
02075 } else {
02076 // if a selection string was given, check the symbol table
02077 SymbolTable *atomSelParser = app->atomSelParser; 
02078 // weights must return floating point values, so the symbol must not 
02079 // be a singleword, so macro is NULL.
02080 atomsel_ctxt context(atomSelParser,
02081 app->moleculeList->mol_from_id(sel->molid()), 
02082 sel->which_frame, NULL);
02083 
02084 int fctn = atomSelParser->find_attribute(weight_string);
02085 if (fctn >= 0) {
02086 // the keyword exists, so get the data
02087 // first, check to see that the function returns floats.
02088 // if it doesn't, it makes no sense to use it as a weight
02089 if (atomSelParser->fctns.data(fctn)->returns_a != SymbolTableElement::IS_FLOAT) {
02090 Tcl_AppendResult(interp, "weight attribute must have floating point values", NULL);
02091 delete [] weights;
02092 return MEASURE_ERR_BADWEIGHTPARM; // can't understand weight parameter 
02093 }
02094 
02095 double *tmp_data = new double[sel->num_atoms];
02096 int *all_on = new int[sel->num_atoms];
02097 for (j=0; j<sel->num_atoms; j++) all_on[j]=1;
02098 
02099 atomSelParser->fctns.data(fctn)->keyword_double(
02100 &context, sel->num_atoms, tmp_data, all_on);
02101 
02102 for (j=0; j<sel->num_atoms; j++) weights[j] = (float)tmp_data[j];
02103 
02104 // clean up.
02105 delete [] tmp_data;
02106 delete [] all_on;
02107 }
02108 }
02109 } else if (!strcmp(opt, "first")) {
02110 if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK)
02111 return TCL_ERROR;
02112 } else if (!strcmp(opt, "last")) {
02113 if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK)
02114 return TCL_ERROR;
02115 } else if (!strcmp(opt, "step")) {
02116 if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK)
02117 return TCL_ERROR;
02118 } else { // unknown keyword.
02119 Tcl_AppendResult(interp, "unknown keyword '", opt, "'. usage: measure cluster ", argerrmsg, NULL);
02120 return TCL_ERROR;
02121 }
02122 }
02123 
02124 // set default for weights if not already defined
02125 if (!weights) {
02126 weights = new float[sel->num_atoms];
02127 for (j=0; j<sel->num_atoms; j++) weights[j]=1.0f;
02128 }
02129 
02130 // Allocate temporary result storage. we add one more cluster 
02131 // slot for collecting unclustered frames in an additional "cluster".
02132 // NOTE: the individual cluster lists are going to
02133 // allocated in the ancilliary code.
02134 int *clustersize = new int [numcluster+1];
02135 int **clusterlist = new int *[numcluster+1];
02136 
02137 // do the cluster analysis
02138 rc = measure_cluster(sel, app->moleculeList, numcluster, algorithm, likeness, cutoff, 
02139 clustersize, clusterlist, first, last, step, selupdate, weights);
02140 
02141 if (weights) delete [] weights;
02142 
02143 // XXX: this needs a 'case' structure to provide more meaninful error messages.
02144 if (rc != MEASURE_NOERR) { 
02145 Tcl_AppendResult(interp, "measure cluster: error during cluster analysis calculation.", NULL);
02146 return TCL_ERROR;
02147 }
02148 
02149 // convert the results of the lowlevel call to tcl lists
02150 // and build a list from them as return value.
02151 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
02152 
02153 for (i=0; i <= numcluster; ++i) { 
02154 int j;
02155 Tcl_Obj *tcl_clist = Tcl_NewListObj(0, NULL);
02156 for (j=0; j < clustersize[i]; ++j) {
02157 Tcl_ListObjAppendElement(interp, tcl_clist, Tcl_NewIntObj(clusterlist[i][j]));
02158 }
02159 Tcl_ListObjAppendElement(interp, tcl_result, tcl_clist);
02160 }
02161 Tcl_SetObjResult(interp, tcl_result);
02162 
02163 // free temporary result storage
02164 for (i=0; i <= numcluster; ++i)
02165 delete[] clusterlist[i];
02166 
02167 delete[] clusterlist;
02168 delete[] clustersize;
02169 
02170 return TCL_OK;
02171 }
02172 
02174 static int vmd_measure_clustsize(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02175 int i;
02176 // initialize optional arguments to default values
02177 double cutoff=3.0; // arbitrary. took hbond cutoff.
02178 char *storesize=NULL;
02179 char *storenum=NULL;
02180 int usepbc=0;
02181 int minsize=2;
02182 int numshared=1;
02183 int rc;
02184 
02185 // argument error message
02186 const char *argerrmsg = "<sel> [cutoff <float>] [minsize <num>] [numshared <num>] "
02187 "[usepbc <bool>] [storesize <fieldname>] [storenum <fieldname>]";
02188 
02189 // Two atom selections and optional keyword/value pairs.
02190 if ((argc < 2) || (argc > 13) || ((argc-1) % 2 == 0) ) {
02191 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
02192 return TCL_ERROR;
02193 }
02194 
02195 // check atom selection
02196 AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1], NULL));
02197 if (!sel) {
02198 Tcl_AppendResult(interp, "measure clustsize: invalid atom selection", NULL);
02199 return TCL_ERROR;
02200 }
02201 if (!app->molecule_valid_id(sel->molid())) return MEASURE_ERR_NOMOLECULE;
02202 
02203 // parse optional arguments
02204 for (i=2; i<argc; i+=2) {
02205 const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
02206 if (i==(argc-1)) {
02207 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
02208 return TCL_ERROR;
02209 } else if (!strcmp(opt, "cutoff")) {
02210 if (Tcl_GetDoubleFromObj(interp, objv[i+1], &cutoff) != TCL_OK)
02211 return TCL_ERROR;
02212 if (cutoff <= 0.0) {
02213 Tcl_AppendResult(interp, "measure clustsize: invalid 'cutoff' value", NULL);
02214 return TCL_ERROR;
02215 }
02216 } else if (!strcmp(opt, "minsize")) {
02217 if (Tcl_GetIntFromObj(interp, objv[i+1], &minsize) != TCL_OK)
02218 return TCL_ERROR;
02219 if (minsize < 2) {
02220 Tcl_AppendResult(interp, "measure clustsize: invalid 'minsize' value", NULL);
02221 return TCL_ERROR;
02222 }
02223 } else if (!strcmp(opt, "numshared")) {
02224 if (Tcl_GetIntFromObj(interp, objv[i+1], &numshared) != TCL_OK)
02225 return TCL_ERROR;
02226 if (numshared < 0) {
02227 Tcl_AppendResult(interp, "measure clustsize: invalid 'numshared' value", NULL);
02228 return TCL_ERROR;
02229 }
02230 } else if (!strcmp(opt, "usepbc")) {
02231 if (Tcl_GetBooleanFromObj(interp, objv[i+1], &usepbc) != TCL_OK)
02232 return TCL_ERROR;
02233 } else if (!strcmp(opt, "storenum")) {
02234 storenum = Tcl_GetStringFromObj(objv[i+1], NULL);
02235 } else if (!strcmp(opt, "storesize")) {
02236 storesize = Tcl_GetStringFromObj(objv[i+1], NULL);
02237 } else { // unknown keyword.
02238 Tcl_AppendResult(interp, "unknown keyword '", opt, "'. usage: measure clustsize ", argerrmsg, NULL);
02239 return TCL_ERROR;
02240 }
02241 }
02242 
02243 if (usepbc) { 
02244 Tcl_AppendResult(interp, "measure clustsize: does not support periodic boundaries yet.", NULL);
02245 return TCL_ERROR;
02246 }
02247 
02248 // allocate temporary result storage
02249 // NOTE: the individual cluster lists are going to
02250 // allocated in the ancilliary code.
02251 int num_selected=sel->selected;
02252 int *clustersize = new int[num_selected];
02253 int *clusternum= new int [num_selected];
02254 int *clusteridx= new int [num_selected];
02255 for (i=0; i < num_selected; i++) {
02256 clustersize[i] = 0;
02257 clusternum[i] = -1;
02258 clusteridx[i] = -1;
02259 }
02260 
02261 // do the cluster analysis
02262 rc = measure_clustsize(sel, app->moleculeList, cutoff, 
02263 clustersize, clusternum, clusteridx,
02264 minsize, numshared, usepbc);
02265 
02266 // XXX: this needs a 'case' structure to provide more meaninful error messages.
02267 if (rc != MEASURE_NOERR) { 
02268 Tcl_AppendResult(interp, "measure clustsize: error during cluster size analysis calculation.", NULL);
02269 return TCL_ERROR;
02270 }
02271 
02272 
02273 if (storenum || storesize) {
02274 // field names were given to store the results. check the keywords and so on.
02275 SymbolTable *atomSelParser = app->atomSelParser; 
02276 atomsel_ctxt context(atomSelParser, 
02277 app->moleculeList->mol_from_id(sel->molid()), 
02278 sel->which_frame, NULL);
02279 
02280 // the keyword exists, set the data
02281 if (storenum) {
02282 int fctn = atomSelParser->find_attribute(storenum);
02283 if (fctn >= 0) {
02284 if (atomSelParser->fctns.data(fctn)->returns_a == SymbolTableElement::IS_FLOAT) {
02285 double *tmp_data = new double[sel->num_atoms];
02286 int j=0;
02287 for (int i=sel->firstsel; i<=sel->lastsel; i++) {
02288 if (sel->on[i])
02289 tmp_data[i] = (double) clusternum[j++];
02290 }
02291 atomSelParser->fctns.data(fctn)->set_keyword_double(&context, 
02292 sel->num_atoms,
02293 tmp_data, sel->on);
02294 delete[] tmp_data;
02295 
02296 } else if (atomSelParser->fctns.data(fctn)->returns_a == SymbolTableElement::IS_INT) {
02297 int *tmp_data = new int[sel->num_atoms];
02298 int j=0;
02299 for (int i=sel->firstsel; i<=sel->lastsel; i++) {
02300 if (sel->on[i])
02301 tmp_data[i] = clusternum[j++];
02302 }
02303 atomSelParser->fctns.data(fctn)->set_keyword_int(&context, 
02304 sel->num_atoms,
02305 tmp_data, sel->on);
02306 delete[] tmp_data;
02307 } else {
02308 Tcl_AppendResult(interp, "measure clustsize: storenum field must accept numbers", NULL);
02309 return TCL_ERROR;
02310 }
02311 } else {
02312 Tcl_AppendResult(interp, "measure clustsize: invalid field name for storenum", NULL);
02313 return TCL_ERROR;
02314 }
02315 }
02316 
02317 // the keyword exists, set the data
02318 if (storesize) {
02319 int fctn = atomSelParser->find_attribute(storesize);
02320 if (fctn >= 0) {
02321 if (atomSelParser->fctns.data(fctn)->returns_a == SymbolTableElement::IS_FLOAT) {
02322 double *tmp_data = new double[sel->num_atoms];
02323 int j=0;
02324 for (int i=sel->firstsel; i<=sel->lastsel; i++) {
02325 if (sel->on[i])
02326 tmp_data[i] = (double) clustersize[j++];
02327 }
02328 atomSelParser->fctns.data(fctn)->set_keyword_double(&context, 
02329 sel->num_atoms,
02330 tmp_data, sel->on);
02331 delete[] tmp_data;
02332 
02333 } else if (atomSelParser->fctns.data(fctn)->returns_a == SymbolTableElement::IS_INT) {
02334 int *tmp_data = new int[sel->num_atoms];
02335 int j=0;
02336 for (int i=sel->firstsel; i<=sel->lastsel; i++) {
02337 if (sel->on[i])
02338 tmp_data[i] = clustersize[j++];
02339 }
02340 atomSelParser->fctns.data(fctn)->set_keyword_int(&context, 
02341 sel->num_atoms,
02342 tmp_data, sel->on);
02343 delete[] tmp_data;
02344 } else {
02345 Tcl_AppendResult(interp, "measure clustsize: storenum field must accept numbers", NULL);
02346 return TCL_ERROR;
02347 }
02348 } else {
02349 Tcl_AppendResult(interp, "measure clustsize: invalid field name for storesize", NULL);
02350 return TCL_ERROR;
02351 }
02352 }
02353 } else {
02354 // convert the results of the lowlevel call to tcl lists
02355 // and build a list from them as return value.
02356 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
02357 
02358 Tcl_Obj *tcl_ilist = Tcl_NewListObj(0, NULL);
02359 Tcl_Obj *tcl_clist = Tcl_NewListObj(0, NULL);
02360 Tcl_Obj *tcl_nlist = Tcl_NewListObj(0, NULL);
02361 for (i=0; i<num_selected; i++) { 
02362 Tcl_ListObjAppendElement(interp, tcl_ilist, Tcl_NewIntObj(clusteridx[i]));
02363 Tcl_ListObjAppendElement(interp, tcl_clist, Tcl_NewIntObj(clustersize[i]));
02364 Tcl_ListObjAppendElement(interp, tcl_nlist, Tcl_NewIntObj(clusternum[i]));
02365 }
02366 Tcl_ListObjAppendElement(interp, tcl_result, tcl_ilist);
02367 Tcl_ListObjAppendElement(interp, tcl_result, tcl_clist);
02368 Tcl_ListObjAppendElement(interp, tcl_result, tcl_nlist);
02369 Tcl_SetObjResult(interp, tcl_result);
02370 }
02371 
02372 delete[] clustersize;
02373 delete[] clusternum;
02374 delete[] clusteridx;
02375 
02376 return TCL_OK;
02377 }
02378 
02379 static int vmd_measure_hbonds(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02380 
02381 // Cutoff, angle, and either one or two atom selections
02382 if (argc != 4 && argc != 5) {
02383 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<cutoff> <angle> <selection1> [<selection2>]");
02384 return TCL_ERROR;
02385 }
02386 AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[3],NULL));
02387 if (!sel1) {
02388 Tcl_AppendResult(interp, "measure hbonds: invalid first atom selection", NULL);
02389 return TCL_ERROR;
02390 }
02391 
02392 AtomSel *sel2 = NULL;
02393 if (argc == 5) {
02394 sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[4],NULL));
02395 if (!sel2) {
02396 Tcl_AppendResult(interp, "measure hbonds: invalid second atom selection", NULL);
02397 return TCL_ERROR;
02398 }
02399 }
02400 if (sel2 && sel2->molid() != sel1->molid()) {
02401 Tcl_AppendResult(interp, "measure hbonds: error, atom selections must come from same molecule.", NULL);
02402 return TCL_ERROR;
02403 }
02404 double cutoff;
02405 if (Tcl_GetDoubleFromObj(interp, objv[1], &cutoff) != TCL_OK) 
02406 return TCL_ERROR;
02407 
02408 double maxangle;
02409 if (Tcl_GetDoubleFromObj(interp, objv[2], &maxangle) != TCL_OK) 
02410 return TCL_ERROR;
02411 
02412 const float *pos = sel1->coordinates(app->moleculeList);
02413 if (!pos) {
02414 Tcl_AppendResult(interp, "measure bondsearch: error, molecule contains no coordinates", NULL);
02415 return TCL_ERROR;
02416 }
02417 
02418 // XXX the actual code for measuring hbonds doesn't belong here, it should
02419 // be moved into Measure.[Ch] where it really belongs. This file
02420 // only implements the Tcl interface, and should not be doing the
02421 // hard core math, particularly if we want to expose the same
02422 // feature via other scripting interfaces. Also, having a single
02423 // implementation avoids having different Tcl/Python bugs in the 
02424 // long-term. Too late to do anything about this now, but should be
02425 // addressed for the next major version when time allows.
02426 
02427 // XXX This code is close, but not identical to the HBonds code in 
02428 // DrawMolItem. Is there any good reason they aren't identical?
02429 // This version does a few extra tests that the other does not.
02430 
02431 Molecule *mol = app->moleculeList->mol_from_id(sel1->molid());
02432 
02433 int *donlist, *hydlist, *acclist;
02434 int maxsize = 2 * sel1->num_atoms; //This heuristic is based on ice, where there are < 2 hydrogen bonds per atom if hydrogens are in the selection, and exactly 2 if hydrogens are not considered.
02435 donlist = new int[maxsize];
02436 hydlist = new int[maxsize];
02437 acclist = new int[maxsize];
02438 int rc = measure_hbonds(mol, sel1, sel2, cutoff, maxangle, donlist, hydlist, acclist, maxsize);
02439 if (rc > maxsize) {
02440 delete [] donlist;
02441 delete [] hydlist;
02442 delete [] acclist;
02443 maxsize = rc;
02444 donlist = new int[maxsize];
02445 hydlist = new int[maxsize];
02446 acclist = new int[maxsize];
02447 rc = measure_hbonds(mol, sel1, sel2, cutoff, maxangle, donlist, hydlist, acclist, maxsize);
02448 }
02449 if (rc < 0) {
02450 Tcl_AppendResult(interp, "measure hbonds: internal error to measure_hbonds", NULL);
02451 return TCL_ERROR;
02452 }
02453 
02454 Tcl_Obj *newdonlist = Tcl_NewListObj(0, NULL);
02455 Tcl_Obj *newhydlist = Tcl_NewListObj(0, NULL);
02456 Tcl_Obj *newacclist = Tcl_NewListObj(0, NULL);
02457 for (int k = 0; k < rc; k++) {
02458 Tcl_ListObjAppendElement(interp, newdonlist, Tcl_NewIntObj(donlist[k]));
02459 Tcl_ListObjAppendElement(interp, newhydlist, Tcl_NewIntObj(hydlist[k]));
02460 Tcl_ListObjAppendElement(interp, newacclist, Tcl_NewIntObj(acclist[k]));
02461 }
02462 delete [] donlist;
02463 delete [] hydlist;
02464 delete [] acclist;
02465 Tcl_Obj *result = Tcl_NewListObj(0, NULL);
02466 Tcl_ListObjAppendElement(interp, result, newdonlist);
02467 Tcl_ListObjAppendElement(interp, result, newacclist);
02468 Tcl_ListObjAppendElement(interp, result, newhydlist);
02469 Tcl_SetObjResult(interp, result);
02470 return TCL_OK;
02471 }
02472 
02473 static int vmd_measure_sasaperresidue(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02474 
02475 int i;
02476 // srad and one atom selection, plus additional options
02477 if (argc < 3) {
02478 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<srad> <sel> [-restrict <restrictedsel>] [-samples <numsamples>]");
02479 return TCL_ERROR;
02480 }
02481 // parse options
02482 Tcl_Obj *ptsvar = NULL;
02483 AtomSel *restrictsel = NULL;
02484 int nsamples = -1, rescount;
02485 int *sampleptr = NULL;
02486 for (i=3; i<argc-1; i+=2) {
02487 const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
02488 if (!strcmp(opt, "-points")) {
02489 ptsvar = objv[i+1];
02490 } else if (!strcmp(opt, "-restrict")) {
02491 restrictsel = tcl_commands_get_sel(interp, 
02492 Tcl_GetStringFromObj(objv[i+1], NULL));
02493 if (!restrictsel) {
02494 Tcl_AppendResult(interp, "measure sasa: invalid restrict atom selection", NULL);
02495 return TCL_ERROR;
02496 }
02497 } else if (!strcmp(opt, "-samples")) {
02498 if (Tcl_GetIntFromObj(interp, objv[i+1], &nsamples) != TCL_OK)
02499 return TCL_ERROR;
02500 sampleptr = &nsamples;
02501 } else {
02502 Tcl_AppendResult(interp, "measure sasa per residue: unknown option '", opt, "'", 
02503 NULL);
02504 return TCL_ERROR;
02505 }
02506 }
02507 
02508 double srad;
02509 if (Tcl_GetDoubleFromObj(interp, objv[1], &srad) != TCL_OK) 
02510 return TCL_ERROR;
02511 AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2],NULL));
02512 if (!sel1) {
02513 Tcl_AppendResult(interp, "measure sasa per residue: invalid first atom selection", NULL);
02514 return TCL_ERROR;
02515 }
02516 
02517 const float *pos = sel1->coordinates(app->moleculeList);
02518 if (!pos) {
02519 Tcl_AppendResult(interp, "measure sasa per residue: error, molecule contains no coordinates", NULL);
02520 return TCL_ERROR;
02521 }
02522 Molecule *mol = app->moleculeList->mol_from_id(sel1->molid());
02523 const float *radius = mol->extraflt.data("radius");
02524 
02525 ResizeArray<float> sasapts;
02526 float *sasavals;
02527 sasavals = new float[sel1->selected];
02528 int rc = measure_sasa_perresidue(sel1, pos, radius, (float) srad, sasavals, &sasapts, 
02529 restrictsel, sampleptr,&rescount,app->moleculeList);
02530 if (rc < 0) {
02531 Tcl_AppendResult(interp, "measure: sasa per residue : ", measure_error(rc), NULL);
02532 delete [] sasavals;
02533 return TCL_ERROR;
02534 }
02535 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
02536 for (int i=0; i<rescount; i++) {
02537 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(sasavals[i]));
02538 }
02539 Tcl_SetObjResult(interp, tcl_result);
02540 if (ptsvar) {
02541 // construct list from sasapts
02542 Tcl_Obj *listobj = Tcl_NewListObj(0, NULL);
02543 i=0;
02544 while (i<sasapts.num()) {
02545 Tcl_Obj *elem = Tcl_NewListObj(0, NULL);
02546 Tcl_ListObjAppendElement(interp, elem, Tcl_NewDoubleObj(sasapts[i++]));
02547 Tcl_ListObjAppendElement(interp, elem, Tcl_NewDoubleObj(sasapts[i++]));
02548 Tcl_ListObjAppendElement(interp, elem, Tcl_NewDoubleObj(sasapts[i++]));
02549 Tcl_ListObjAppendElement(interp, listobj, elem);
02550 }
02551 Tcl_ObjSetVar2(interp, ptsvar, NULL, listobj, 0);
02552 }
02553 delete [] sasavals;
02554 return TCL_OK;
02555 }
02556 
02557 
02558 static int vmd_measure_sasa(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02559 
02560 int i;
02561 // srad and one atom selection, plus additional options
02562 if (argc < 3) {
02563 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<srad> <sel> [-points <varname>] [-restrict <restrictedsel>] [-samples <numsamples>]");
02564 return TCL_ERROR;
02565 }
02566 // parse options
02567 Tcl_Obj *ptsvar = NULL;
02568 AtomSel *restrictsel = NULL;
02569 int nsamples = -1;
02570 int *sampleptr = NULL;
02571 for (i=3; i<argc-1; i+=2) {
02572 const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
02573 if (!strcmp(opt, "-points")) {
02574 ptsvar = objv[i+1];
02575 } else if (!strcmp(opt, "-restrict")) {
02576 restrictsel = tcl_commands_get_sel(interp, 
02577 Tcl_GetStringFromObj(objv[i+1], NULL));
02578 if (!restrictsel) {
02579 Tcl_AppendResult(interp, "measure sasa: invalid restrict atom selection", NULL);
02580 return TCL_ERROR;
02581 }
02582 } else if (!strcmp(opt, "-samples")) {
02583 if (Tcl_GetIntFromObj(interp, objv[i+1], &nsamples) != TCL_OK)
02584 return TCL_ERROR;
02585 sampleptr = &nsamples;
02586 } else {
02587 Tcl_AppendResult(interp, "measure sasa: unknown option '", opt, "'", 
02588 NULL);
02589 return TCL_ERROR;
02590 }
02591 }
02592 
02593 double srad;
02594 if (Tcl_GetDoubleFromObj(interp, objv[1], &srad) != TCL_OK) 
02595 return TCL_ERROR;
02596 AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2],NULL));
02597 if (!sel1) {
02598 Tcl_AppendResult(interp, "measure sasa: invalid first atom selection", NULL);
02599 return TCL_ERROR;
02600 }
02601 
02602 const float *pos = sel1->coordinates(app->moleculeList);
02603 if (!pos) {
02604 Tcl_AppendResult(interp, "measure sasa: error, molecule contains no coordinates", NULL);
02605 return TCL_ERROR;
02606 }
02607 Molecule *mol = app->moleculeList->mol_from_id(sel1->molid());
02608 const float *radius = mol->extraflt.data("radius");
02609 
02610 ResizeArray<float> sasapts;
02611 float sasa = 0;
02612 int rc = measure_sasa(sel1, pos, radius, (float) srad, &sasa, &sasapts, 
02613 restrictsel, sampleptr);
02614 if (rc < 0) {
02615 Tcl_AppendResult(interp, "measure: sasa: ", measure_error(rc), NULL);
02616 return TCL_ERROR;
02617 }
02618 Tcl_SetObjResult(interp, Tcl_NewDoubleObj(sasa));
02619 if (ptsvar) {
02620 // construct list from sasapts
02621 Tcl_Obj *listobj = Tcl_NewListObj(0, NULL);
02622 i=0;
02623 while (i<sasapts.num()) {
02624 Tcl_Obj *elem = Tcl_NewListObj(0, NULL);
02625 Tcl_ListObjAppendElement(interp, elem, Tcl_NewDoubleObj(sasapts[i++]));
02626 Tcl_ListObjAppendElement(interp, elem, Tcl_NewDoubleObj(sasapts[i++]));
02627 Tcl_ListObjAppendElement(interp, elem, Tcl_NewDoubleObj(sasapts[i++]));
02628 Tcl_ListObjAppendElement(interp, listobj, elem);
02629 }
02630 Tcl_ObjSetVar2(interp, ptsvar, NULL, listobj, 0);
02631 }
02632 return TCL_OK;
02633 }
02634 
02635 
02636 #if 1
02637 
02638 static int vmd_measure_sasalist(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02639 
02640 int i;
02641 // srad and one atom selection, plus additional options
02642 if (argc < 3) {
02643 Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<srad> <sel list> [-samples <numsamples>]");
02644 return TCL_ERROR;
02645 }
02646 
02647 // parse options
02648 int nsamples = -1;
02649 int *sampleptr = NULL;
02650 for (i=3; i<argc-1; i+=2) {
02651 const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
02652 
02653 if (!strcmp(opt, "-samples")) {
02654 if (Tcl_GetIntFromObj(interp, objv[i+1], &nsamples) != TCL_OK)
02655 return TCL_ERROR;
02656 sampleptr = &nsamples;
02657 } else {
02658 Tcl_AppendResult(interp, "measure sasa: unknown option '", opt, "'", NULL);
02659 return TCL_ERROR;
02660 }
02661 }
02662 
02663 double srad;
02664 if (Tcl_GetDoubleFromObj(interp, objv[1], &srad) != TCL_OK) 
02665 return TCL_ERROR;
02666 
02667 int numsels;
02668 Tcl_Obj **sel_list;
02669 if (Tcl_ListObjGetElements(interp, objv[2], &numsels, &sel_list) != TCL_OK) {
02670 Tcl_AppendResult(interp, "measure sasalist: bad selection list", NULL);
02671 return TCL_ERROR;
02672 }
02673 
02674 #if 0
02675 printf("measure sasalist: numsels %d\n", numsels);
02676 #endif
02677 
02678 AtomSel **asels = (AtomSel **) calloc(1, numsels * sizeof(AtomSel *));
02679 float *sasalist = (float *) calloc(1, numsels * sizeof(float));
02680 
02681 int s;
02682 for (s=0; s<numsels; s++) {
02683 asels[s] = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(sel_list[s], NULL));
02684 if (!asels[s]) {
02685 printf("measure sasalist: invalid selection %d\n", s);
02686 Tcl_AppendResult(interp, "measure sasalist: invalid atom selection list element", NULL);
02687 return TCL_ERROR;
02688 }
02689 }
02690 
02691 int rc = measure_sasalist(app->moleculeList, (const AtomSel **) asels, 
02692 numsels, (float) srad, sasalist, sampleptr);
02693 free(asels);
02694 if (rc < 0) {
02695 Tcl_AppendResult(interp, "measure: sasalist: ", measure_error(rc), NULL);
02696 return TCL_ERROR;
02697 }
02698 
02699 // construct list from sasa values
02700 Tcl_Obj *listobj = Tcl_NewListObj(0, NULL);
02701 for (i=0; i<numsels; i++) {
02702 Tcl_ListObjAppendElement(interp, listobj, Tcl_NewDoubleObj(sasalist[i]));
02703 }
02704 Tcl_SetObjResult(interp, listobj);
02705 free(sasalist);
02706 
02707 return TCL_OK;
02708 }
02709 
02710 #endif
02711 
02712 
02713 // Function: vmd_measure_energy 
02714 // Returns: the energy for the specified bond/angle/dihed/imprp/vdw/elec
02715 // FIXME -- usage doesn't match user guide
02716 static int vmd_measure_energy(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02717 
02718 if (argc<3) {
02719 Tcl_WrongNumArgs(interp, 2, objv-1,
02720 (char *) "bond|angle|dihed|imprp|vdw|elec {{<atomid1> ?<molid1>?} {<atomid2> ?<molid2>?}} ?molid <default molid>? [?frame <frame|all>? | ?first <first>? ?last <last>?]");
02721 return TCL_ERROR;
02722 }
02723 
02724 int geomtype, reqatoms;
02725 char *geomname = Tcl_GetStringFromObj(objv[1],NULL);
02726 if (!strncmp(geomname, "bond", 4)) {
02727 reqatoms = 2; geomtype = MEASURE_BOND;
02728 } else if (!strncmp(geomname, "angl", 4)) {
02729 reqatoms = 3; geomtype = MEASURE_ANGLE;
02730 } else if (!strncmp(geomname, "dihe", 4)) {
02731 reqatoms = 4; geomtype = MEASURE_DIHED;
02732 } else if (!strncmp(geomname, "impr", 4)) {
02733 reqatoms = 4; geomtype = MEASURE_IMPRP;
02734 } else if (!strncmp(geomname, "vdw", 3)) {
02735 reqatoms = 2; geomtype = MEASURE_VDW;
02736 } else if (!strncmp(geomname, "elec", 4)) {
02737 reqatoms = 2; geomtype = MEASURE_ELECT;
02738 } else {
02739 Tcl_AppendResult(interp, " measure energy: bad syntax (must specify bond|angle|dihed|imprp|vdw|elec)", NULL);
02740 return TCL_ERROR;
02741 }
02742 
02743 int molid[4];
02744 int atmid[4];
02745 int defmolid = -1;
02746 bool allframes = false;
02747 char errstring[200];
02748 
02749 // Read the atom list
02750 int numatms;
02751 Tcl_Obj **data;
02752 if (Tcl_ListObjGetElements(interp, objv[2], &numatms, &data) != TCL_OK) {
02753 Tcl_AppendResult(interp, " measure energy: bad syntax", NULL);
02754 Tcl_AppendResult(interp, " Usage: measure energy bond|angle|dihed|imprp|vdw|elec {{<atomid1> ?<molid1>?} {<atomid2> ?<molid2>?} ...} ?molid <default molid>? [?frame <frame|all>? | ?first <first>? ?last <last>?]", NULL);
02755 return TCL_ERROR;
02756 }
02757 
02758 if (numatms!=reqatoms) {
02759 sprintf(errstring, " measure energy %s: must specify exactly %i atoms in list", geomname, reqatoms);
02760 Tcl_AppendResult(interp, errstring, NULL);
02761 return TCL_ERROR;
02762 }
02763 
02764 int first=-1, last=-1, frame=-1;
02765 double params[6];
02766 memset(params, 0, 6L*sizeof(double));
02767 
02768 if (argc>4) {
02769 int i;
02770 for (i=3; i<argc-1; i+=2) {
02771 char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
02772 if (!strupncmp(argvcur, "molid", CMDLEN)) {
02773 if (Tcl_GetIntFromObj(interp, objv[i+1], &defmolid) != TCL_OK) {
02774 Tcl_AppendResult(interp, " measure energy: bad molid", NULL);
02775 return TCL_ERROR;
02776 }
02777 } else if (!strupncmp(argvcur, "k", CMDLEN)) {
02778 if (Tcl_GetDoubleFromObj(interp, objv[i+1], params) != TCL_OK) {
02779 Tcl_AppendResult(interp, " measure energy: bad force constant value", NULL);
02780 return TCL_ERROR;
02781 }
02782 } else if (!strupncmp(argvcur, "x0", CMDLEN)) {
02783 if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+1) != TCL_OK) {
02784 Tcl_AppendResult(interp, " measure energy: bad equilibrium value", NULL);
02785 return TCL_ERROR;
02786 }
02787 } else if (!strupncmp(argvcur, "kub", CMDLEN)) {
02788 if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+2) != TCL_OK) {
02789 Tcl_AppendResult(interp, " measure energy: bad Urey-Bradley force constant value", NULL);
02790 return TCL_ERROR;
02791 }
02792 } else if (!strupncmp(argvcur, "s0", CMDLEN)) {
02793 if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+3) != TCL_OK) {
02794 Tcl_AppendResult(interp, " measure energy: bad Urey-Bradley equilibrium distance", NULL);
02795 return TCL_ERROR;
02796 }
02797 } else if (!strupncmp(argvcur, "n", CMDLEN)) {
02798 if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+1) != TCL_OK) {
02799 Tcl_AppendResult(interp, " measure energy: bad dihedral periodicity", NULL);
02800 return TCL_ERROR;
02801 }
02802 } else if (!strupncmp(argvcur, "delta", CMDLEN)) {
02803 if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+2) != TCL_OK) {
02804 Tcl_AppendResult(interp, " measure energy: bad dihedral phase shift", NULL);
02805 return TCL_ERROR;
02806 }
02807 } else if (!strupncmp(argvcur, "eps1", CMDLEN)) {
02808 if (Tcl_GetDoubleFromObj(interp, objv[i+1], params) != TCL_OK) {
02809 Tcl_AppendResult(interp, " measure energy: bad vdw well depth", NULL);
02810 return TCL_ERROR;
02811 }
02812 } else if (!strupncmp(argvcur, "rmin1", CMDLEN)) {
02813 if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+1) != TCL_OK) {
02814 Tcl_AppendResult(interp, " measure energy: bad vdw equilibrium distance", NULL);
02815 return TCL_ERROR;
02816 }
02817 } else if (!strupncmp(argvcur, "eps2", CMDLEN)) {
02818 if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+2) != TCL_OK) {
02819 Tcl_AppendResult(interp, " measure energy: bad vdw well depth", NULL);
02820 return TCL_ERROR;
02821 }
02822 } else if (!strupncmp(argvcur, "rmin2", CMDLEN)) {
02823 if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+3) != TCL_OK) {
02824 Tcl_AppendResult(interp, " measure energy: bad vdw equilibrium distance", NULL);
02825 return TCL_ERROR;
02826 }
02827 } else if (!strupncmp(argvcur, "q1", CMDLEN)) {
02828 if (Tcl_GetDoubleFromObj(interp, objv[i+1], params) != TCL_OK) {
02829 Tcl_AppendResult(interp, " measure energy: bad charge value", NULL);
02830 return TCL_ERROR;
02831 }
02832 params[2]=1.0;
02833 } else if (!strupncmp(argvcur, "q2", CMDLEN)) {
02834 if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+1) != TCL_OK) {
02835 Tcl_AppendResult(interp, " measure energy: bad charge value", NULL);
02836 return TCL_ERROR;
02837 }
02838 params[3]=1.0;
02839 } else if (!strupncmp(argvcur, "cutoff", CMDLEN)) {
02840 if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+4) != TCL_OK) {
02841 Tcl_AppendResult(interp, " measure energy: bad electrostatic cutoff value", NULL);
02842 return TCL_ERROR;
02843 }
02844 } else if (!strupncmp(argvcur, "switchdist", CMDLEN)) {
02845 if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+5) != TCL_OK) {
02846 Tcl_AppendResult(interp, " measure energy: bad switching distance value", NULL);
02847 return TCL_ERROR;
02848 }
02849 } else if (!strupncmp(argvcur, "first", CMDLEN)) {
02850 if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
02851 Tcl_AppendResult(interp, " measure energy: bad first frame value", NULL);
02852 return TCL_ERROR;
02853 }
02854 } else if (!strupncmp(argvcur, "last", CMDLEN)) {
02855 if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
02856 Tcl_AppendResult(interp, " measure energy: bad last frame value", NULL);
02857 return TCL_ERROR;
02858 }
02859 } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
02860 if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "all")) {
02861 allframes = true;
02862 } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
02863 Tcl_AppendResult(interp, " measure energy: bad frame value", NULL);
02864 return TCL_ERROR;
02865 }
02866 } else {
02867 Tcl_AppendResult(interp, " measure energy: invalid syntax, no such keyword: ", argvcur, NULL);
02868 return TCL_ERROR;
02869 }
02870 }
02871 }
02872 
02873 if ((allframes || frame>=0) && (first>=0 || last>=0)) {
02874 Tcl_AppendResult(interp, "measure energy: Ambiguous syntax: You cannot specify a frame AND a frame range (using first or last).", NULL);
02875 Tcl_AppendResult(interp, "\nUsage:\nmeasure bond <molid1>/<atomid1> <molid2>/<atomid2> [?frame <frame|all>? | ?first <first>? ?last <last>?]", NULL);
02876 return TCL_ERROR; 
02877 }
02878 
02879 if (allframes) first=0;
02880 
02881 // If no molecule was specified use top as default
02882 if (defmolid<0) defmolid = app->molecule_top();
02883 
02884 // Assign atom IDs and molecule IDs
02885 int i,numelem;
02886 Tcl_Obj **atmmol;
02887 for (i=0; i<numatms; i++) {
02888 if (Tcl_ListObjGetElements(interp, data[i], &numelem, &atmmol) != TCL_OK) {
02889 return TCL_ERROR;
02890 }
02891 
02892 if (Tcl_GetIntFromObj(interp, atmmol[0], atmid+i) != TCL_OK) {
02893 Tcl_AppendResult(interp, " measure energy: bad atom index", NULL);
02894 return TCL_ERROR;
02895 }
02896 
02897 if (numelem==2) {
02898 if (Tcl_GetIntFromObj(interp, atmmol[1], molid+i) != TCL_OK) {
02899 Tcl_AppendResult(interp, " measure energy: bad molid", NULL);
02900 return TCL_ERROR;
02901 }
02902 } else molid[i] = defmolid;
02903 }
02904 
02905 
02906 // Compute the value
02907 ResizeArray<float> gValues(1024);
02908 int ret_val;
02909 ret_val = measure_energy(app->moleculeList, molid, atmid, reqatoms, &gValues, frame, first, last, defmolid, params, geomtype);
02910 if (ret_val<0) {
02911 printf("ERROR\n %s\n", measure_error(ret_val));
02912 Tcl_AppendResult(interp, measure_error(ret_val), NULL);
02913 return TCL_ERROR;
02914 }
02915 
02916 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
02917 int numvalues = gValues.num();
02918 for (int count = 0; count < numvalues; count++) {
02919 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(gValues[count]));
02920 }
02921 Tcl_SetObjResult(interp, tcl_result);
02922 
02923 return TCL_OK;
02924 }
02925 
02926 
02927 //
02928 // Function: vmd_measure_surface <selection> <gridsize> <radius> <depth>
02929 //
02930 static int vmd_measure_surface(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02931 if (argc!=5) {
02932 Tcl_WrongNumArgs(interp, 2, objv-1,
02933 (char *) "<sel> <gridsize> <radius> <depth>");
02934 return TCL_ERROR;
02935 }
02936 
02937 AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
02938 if (!sel) {
02939 Tcl_AppendResult(interp, "measure surface: no atom selection", NULL);
02940 return TCL_ERROR;
02941 }
02942 
02943 const float *framepos = sel->coordinates(app->moleculeList);
02944 if (!framepos) return TCL_ERROR;
02945 
02946 double gridsz;
02947 if (Tcl_GetDoubleFromObj(interp, objv[2], &gridsz) != TCL_OK) 
02948 return TCL_ERROR;
02949 
02950 double radius;
02951 if (Tcl_GetDoubleFromObj(interp, objv[3], &radius) != TCL_OK) 
02952 return TCL_ERROR;
02953 
02954 double depth;
02955 if (Tcl_GetDoubleFromObj(interp, objv[4], &depth) != TCL_OK) 
02956 return TCL_ERROR;
02957 
02958 int *surface;
02959 int n_surf;
02960 
02961 int ret_val = measure_surface(sel, app->moleculeList, framepos,
02962 gridsz, radius, depth, &surface, &n_surf);
02963 if (ret_val < 0) {
02964 Tcl_AppendResult(interp, "measure surface: ", measure_error(ret_val));
02965 return TCL_ERROR;
02966 }
02967 
02968 Tcl_Obj *surf_list = Tcl_NewListObj(0, NULL);
02969 
02970 int i;
02971 for(i=0; i < n_surf; i++) {
02972 Tcl_ListObjAppendElement(interp, surf_list, Tcl_NewIntObj(surface[i]));
02973 }
02974 Tcl_SetObjResult(interp, surf_list);
02975 delete [] surface;
02976 
02977 return TCL_OK;
02978 }
02979 
02980 
02981 // Function: vmd_measure_pbc2onc <center> ?molid <default>? ?frame <frame|last>?
02982 // Returns: the transformation matrix to wrap a nonorthogonal pbc unicell
02983 // into an orthonormal cell.
02984 static int vmd_measure_pbc2onc_transform(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02985 if (argc < 2) {
02986 Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<center> [molid <default>] [frame <frame|last>]");
02987 return TCL_ERROR;
02988 }
02989 
02990 // Read the center
02991 int ndim;
02992 Tcl_Obj **centerObj;
02993 if (Tcl_ListObjGetElements(interp, objv[1], &ndim, &centerObj) != TCL_OK) {
02994 Tcl_AppendResult(interp, " measure pbc2onc: bad syntax", NULL);
02995 Tcl_AppendResult(interp, " Usage: measure pbc2onc <center> [molid <default>] [frame <frame|last>]", NULL);
02996 return TCL_ERROR;
02997 }
02998 
02999 if (ndim!=3) {
03000 Tcl_AppendResult(interp, " measure pbc2onc: need three numbers for a vector", NULL);
03001 return TCL_ERROR;
03002 }
03003 
03004 int i;
03005 double tmp;
03006 float center[3];
03007 for (i=0; i<3; i++) {
03008 if (Tcl_GetDoubleFromObj(interp, centerObj[i], &tmp) != TCL_OK) {
03009 Tcl_AppendResult(interp, " measure pbc2onc: non-numeric in center", NULL);
03010 return TCL_ERROR;
03011 }
03012 center[i] = (float)tmp;
03013 }
03014 
03015 int molid = app->molecule_top();
03016 int frame = -2;
03017 if (argc>3) {
03018 for (i=2; i<argc; i+=2) {
03019 char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
03020 if (!strupncmp(argvcur, "molid", CMDLEN)) {
03021 // XXX lame test formulation should be corrected
03022 if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "top")) {
03023 // top is already default
03024 } else if (Tcl_GetIntFromObj(interp, objv[i+1], &molid) != TCL_OK) {
03025 Tcl_AppendResult(interp, " measure pbc2onc: bad molid", NULL);
03026 return TCL_ERROR;
03027 }
03028 } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
03029 if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "last")) {
03030 frame=-1;
03031 } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
03032 Tcl_AppendResult(interp, " measure pbc2onc: bad frame value", NULL);
03033 return TCL_ERROR;
03034 }
03035 } else {
03036 Tcl_AppendResult(interp, " measure pbc2onc: invalid syntax, no such keyword: ", argvcur, NULL);
03037 return TCL_ERROR;
03038 }
03039 }
03040 }
03041 
03042 
03043 Matrix4 transform;
03044 int ret_val = measure_pbc2onc(app->moleculeList, molid, frame, center, transform);
03045 if (ret_val < 0) {
03046 Tcl_AppendResult(interp, "measure pbc2onc: ", measure_error(ret_val), NULL);
03047 return TCL_ERROR;
03048 }
03049 
03050 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
03051 for (i=0; i<4; i++) {
03052 Tcl_Obj *rowListObj = Tcl_NewListObj(0, NULL);
03053 Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(transform.mat[i+0]));
03054 Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(transform.mat[i+4]));
03055 Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(transform.mat[i+8]));
03056 Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(transform.mat[i+12]));
03057 Tcl_ListObjAppendElement(interp, tcl_result, rowListObj);
03058 }
03059 Tcl_SetObjResult(interp, tcl_result);
03060 
03061 return TCL_OK;
03062 }
03063 
03064 // Function: vmd_measure_pbc_neighbors <center> <cutoff>
03065 // Returns: All image atoms that are within cutoff Angstrom of the pbc unitcell.
03066 // Two lists are returned. The first list holds the atom coordinates while
03067 // the second one is an indexlist mapping the image atoms to the atoms in
03068 // the unitcell.
03069 
03070 // Input: Since the pbc cell <center> is not stored in DCDs and cannot be set in
03071 // VMD it must be provided by the user as the first argument.
03072 // The second argument <cutoff> is the maximum distance (in Angstrom) from
03073 // the PBC unit cell for atoms to be considered.
03074 
03075 // Options: ?sel <selection>? :
03076 // If an atomselection is provided after the keyword 'sel' then only those
03077 // image atoms are returned that are within cutoff of the selected atoms
03078 // of the main cell. In case cutoff is a vector the largest value will be
03079 // used.
03080 
03081 // ?align <matrix>? :
03082 // In case the molecule was aligned you can supply the alignment matrix
03083 // which is then used to correct for the rotation and shift of the pbc cell.
03084 
03085 // ?boundingbox PBC | {<mincoord> <maxcoord>}?
03086 // With this option the atoms are wrapped into a rectangular bounding box.
03087 // If you provide "PBC" as an argument then the bounding box encloses the
03088 // PBC box but then the cutoff is added to the bounding box. Negative 
03089 // values for the cutoff dimensions are allowed and lead to a smaller box.
03090 // Instead you can also provide a custom bounding box in form of the 
03091 // minmax coordinates (list containing two coordinate vectors such as 
03092 // returned by the measure minmax command). Here, again, the cutoff is
03093 // added to the bounding box.
03094 static int vmd_measure_pbc_neighbors(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
03095 if (argc < 3) {
03096 Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<center> <cutoff> ?sel <selection>? ?align <matrix>? ?molid <default>? ?frame <frame|last>? ?boundingbox PBC|{<mincoord> <maxcoord>}?");
03097 return TCL_ERROR;
03098 }
03099 
03100 // Read the center
03101 int ndim;
03102 Tcl_Obj **centerObj;
03103 if (Tcl_ListObjGetElements(interp, objv[1], &ndim, &centerObj) != TCL_OK) {
03104 Tcl_AppendResult(interp, " measure pbcneighbors: bad syntax", NULL);
03105 Tcl_AppendResult(interp, " Usage: measure pbcneighbors <center> <cutoff> [<sel <sel>] [align <matrix>] [molid <default>] [frame <frame|last>] [boundingbox <PBC|{<mincoord> <maxcoord>}>]", NULL);
03106 return TCL_ERROR;
03107 }
03108 
03109 if (ndim!=3) {
03110 Tcl_AppendResult(interp, " measure pbcneighbors: need three numbers for a vector", NULL);
03111 return TCL_ERROR;
03112 }
03113 
03114 int i;
03115 double tmp;
03116 float center[3];
03117 for (i=0; i<3; i++) {
03118 if (Tcl_GetDoubleFromObj(interp, centerObj[i], &tmp) != TCL_OK) {
03119 Tcl_AppendResult(interp, " measure pbcneighbors: non-numeric in center", NULL);
03120 return TCL_ERROR;
03121 }
03122 center[i] = float(tmp);
03123 }
03124 
03125 // Read the cutoff
03126 Tcl_Obj **cutoffObj;
03127 if (Tcl_ListObjGetElements(interp, objv[2], &ndim, &cutoffObj) != TCL_OK) {
03128 Tcl_AppendResult(interp, " measure pbcneighbors: bad syntax", NULL);
03129 Tcl_AppendResult(interp, " Usage: measure pbcneighbors <center> <cutoff> [<sel <sel>] [align <matrix>] [molid <default>] [frame <frame|last>] [boundingbox <PBC|{<mincoord> <maxcoord>}>]", NULL);
03130 return TCL_ERROR;
03131 }
03132 
03133 if (ndim!=3 && ndim!=1) {
03134 Tcl_AppendResult(interp, " measure pbcneighbors: need either one or three numbers for cutoff", NULL);
03135 return TCL_ERROR;
03136 }
03137 
03138 float cutoff[3];
03139 for (i=0; i<ndim; i++) {
03140 if (Tcl_GetDoubleFromObj(interp, cutoffObj[i], &tmp) != TCL_OK) {
03141 Tcl_AppendResult(interp, " measure pbcneighbors: non-numeric in cutoff", NULL);
03142 return TCL_ERROR;
03143 }
03144 cutoff[i] = float(tmp);
03145 }
03146 
03147 if (ndim==1) { cutoff[2] = cutoff[1] = cutoff[0]; }
03148 
03149 bool molidprovided=0;
03150 float *boxminmax=NULL;
03151 int molid = app->molecule_top();
03152 int frame = -2;
03153 AtomSel *sel = NULL;
03154 Matrix4 alignment;
03155 if (argc>4) {
03156 for (i=3; i<argc; i+=2) {
03157 char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
03158 if (!strupncmp(argvcur, "sel", CMDLEN)) {
03159 // Read the atom selection
03160 sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[i+1],NULL));
03161 if (!sel) {
03162 Tcl_AppendResult(interp, "measure pbcneighbors: invalid atom selection", NULL);
03163 return TCL_ERROR;
03164 }
03165 if (!app->molecule_valid_id(sel->molid())) {
03166 Tcl_AppendResult(interp, "measure pbcneighbors: ",
03167 measure_error(MEASURE_ERR_NOMOLECULE), NULL);
03168 return TCL_ERROR;
03169 }
03170 if (!sel->selected) {
03171 Tcl_AppendResult(interp, "measure pbcneighbors: selection contains no atoms.", NULL);
03172 return TCL_ERROR;
03173 }
03174 } else if (!strupncmp(argvcur, "molid", CMDLEN)) {
03175 if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "top")) {
03176 // top is already default
03177 } else if (Tcl_GetIntFromObj(interp, objv[i+1], &molid) != TCL_OK) {
03178 Tcl_AppendResult(interp, " measure pbcneighbors: bad molid", NULL);
03179 return TCL_ERROR;
03180 }
03181 molidprovided = 1;
03182 } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
03183 if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "last")) {
03184 frame=-1;
03185 } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
03186 Tcl_AppendResult(interp, " measure pbcneighbors: bad frame value", NULL);
03187 return TCL_ERROR;
03188 }
03189 } else if (!strupncmp(argvcur, "align", CMDLEN)) {
03190 // Get the alignment matrix (as returned by 'measure fit')
03191 if (tcl_get_matrix("measure pbcneighbors: ", interp, objv[i+1], alignment.mat) != TCL_OK) {
03192 return TCL_ERROR;
03193 }
03194 } else if (!strupncmp(argvcur, "boundingbox", CMDLEN)) {
03195 // Determine if the atoms shall be wrapped to the smallest rectangular
03196 // bounding box that still encloses the unitcell plus the given cutoff.
03197 char *argv2 = Tcl_GetStringFromObj(objv[i+1],NULL);
03198 if (!strupncmp(argv2, "on", CMDLEN) || !strupncmp(argv2, "pbc", CMDLEN)) {
03199 boxminmax = new float[6];
03200 compute_pbcminmax(app->moleculeList, molid, frame, center, &alignment,
03201 boxminmax, boxminmax+3);
03202 } else {
03203 // Read the bounding box
03204 int j, k, ncoor;
03205 Tcl_Obj **boxListObj;
03206 if (Tcl_ListObjGetElements(interp, objv[i+1], &ncoor, &boxListObj) != TCL_OK) {
03207 Tcl_AppendResult(interp, " measure pbcneighbors: invalid bounding box parameter", NULL);
03208 return TCL_ERROR;
03209 }
03210 if (ncoor!=2) {
03211 Tcl_AppendResult(interp, " measure pbcneighbors: need 2 points for bounding box", NULL);
03212 return TCL_ERROR;
03213 }
03214 int ndim = 0;
03215 double tmp;
03216 Tcl_Obj **boxObj;
03217 boxminmax = new float[6];
03218 for (j=0; j<2; j++) {
03219 if (Tcl_ListObjGetElements(interp, boxListObj[j], &ndim, &boxObj) != TCL_OK) {
03220 Tcl_AppendResult(interp, " measure pbcneighbors: bad syntax in boundingbox", NULL);
03221 return TCL_ERROR;
03222 }
03223 
03224 for (k=0; k<3; k++) {
03225 if (Tcl_GetDoubleFromObj(interp, boxObj[k], &tmp) != TCL_OK) {
03226 Tcl_AppendResult(interp, " measure pbcneighbors: non-numeric in boundingbox", NULL);
03227 return TCL_ERROR;
03228 }
03229 boxminmax[3L*j+k] = (float)tmp;
03230 }
03231 }
03232 }
03233 
03234 } else {
03235 Tcl_AppendResult(interp, " measure pbcneighbors: invalid syntax, no such keyword: ", argvcur, NULL);
03236 return TCL_ERROR;
03237 }
03238 }
03239 }
03240 
03241 // If no molid was provided explicitely but a selection was given
03242 // then use that molid.
03243 if (sel && !molidprovided) {
03244 molid = sel->molid();
03245 }
03246 
03247 ResizeArray<float> extcoord_array;
03248 ResizeArray<int> indexmap_array;
03249 
03250 int ret_val = measure_pbc_neighbors(app->moleculeList, sel, molid, frame, &alignment, center, cutoff, boxminmax, &extcoord_array, &indexmap_array);
03251 delete [] boxminmax;
03252 
03253 if (ret_val < 0) {
03254 Tcl_AppendResult(interp, "measure pbcneighbors: ", measure_error(ret_val), NULL);
03255 return TCL_ERROR;
03256 }
03257 printf("measure pbcneighbors: %ld neighbor atoms found\n", 
03258 long(indexmap_array.num()));
03259 
03260 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
03261 Tcl_Obj *coorListObj = Tcl_NewListObj(0, NULL);
03262 Tcl_Obj *indexListObj = Tcl_NewListObj(0, NULL);
03263 for (i=0; i<indexmap_array.num(); i++) {
03264 Tcl_Obj *rowListObj = Tcl_NewListObj(0, NULL);
03265 Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(extcoord_array[3L*i]));
03266 Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(extcoord_array[3L*i+1]));
03267 Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(extcoord_array[3L*i+2]));
03268 Tcl_ListObjAppendElement(interp, coorListObj, rowListObj);
03269 Tcl_ListObjAppendElement(interp, indexListObj, Tcl_NewIntObj(indexmap_array[i]));
03270 }
03271 Tcl_ListObjAppendElement(interp, tcl_result, coorListObj);
03272 Tcl_ListObjAppendElement(interp, tcl_result, indexListObj);
03273 Tcl_SetObjResult(interp, tcl_result);
03274 
03275 return TCL_OK;
03276 }
03277 
03278 
03279 
03280 // Function: vmd_measure_inertia <selection> [moments] [eigenvals]
03281 // Returns: The center of mass and the principles axes of inertia for the 
03282 // selected atoms. 
03283 // Options: -moments -- also return the moments of inertia tensor
03284 // -eigenvals -- also return the corresponding eigenvalues
03285 static int vmd_measure_inertia(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
03286 bool moments = FALSE;
03287 bool eigenvals = FALSE;
03288 if (argc < 2 || argc > 4) {
03289 Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<selection> [moments] [eigenvals]");
03290 return TCL_ERROR;
03291 }
03292 AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
03293 if (!sel) {
03294 Tcl_AppendResult(interp, "measure inertia: no atom selection", NULL);
03295 return TCL_ERROR;
03296 }
03297 
03298 if (!app->molecule_valid_id(sel->molid())) {
03299 Tcl_AppendResult(interp, "measure inertia: ",
03300 measure_error(MEASURE_ERR_NOMOLECULE), NULL);
03301 return TCL_ERROR;
03302 }
03303 
03304 if (argc>2) {
03305 int i;
03306 for (i=2; i<argc; i++) {
03307 char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
03308 // Allow syntax with and without leading dash
03309 if (argvcur[0]=='-') argvcur++;
03310 
03311 if (!strupncmp(argvcur, "moments", CMDLEN)) {
03312 moments = TRUE; 
03313 }
03314 else if (!strupncmp(argvcur, "eigenvals", CMDLEN)) {
03315 eigenvals = TRUE; 
03316 }
03317 else {
03318 Tcl_AppendResult(interp, " measure inertia: unrecognized option\n", NULL);
03319 Tcl_AppendResult(interp, " Usage: measure inertia <selection> [moments] [eigenvals]", NULL);
03320 return TCL_ERROR;
03321 }
03322 }
03323 }
03324 
03325 float priaxes[3][3];
03326 float itensor[4][4];
03327 float evalue[3], rcom[3];
03328 int ret_val = measure_inertia(sel, app->moleculeList, NULL, rcom, priaxes, itensor, evalue);
03329 if (ret_val < 0) {
03330 Tcl_AppendResult(interp, "measure inertia: ", measure_error(ret_val), NULL);
03331 return TCL_ERROR;
03332 }
03333 
03334 // return COM and list of 3 principal axes
03335 int i;
03336 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
03337 
03338 Tcl_Obj *rcomObj = Tcl_NewListObj(0, NULL);
03339 Tcl_ListObjAppendElement(interp, rcomObj, Tcl_NewDoubleObj(rcom[0]));
03340 Tcl_ListObjAppendElement(interp, rcomObj, Tcl_NewDoubleObj(rcom[1]));
03341 Tcl_ListObjAppendElement(interp, rcomObj, Tcl_NewDoubleObj(rcom[2]));
03342 Tcl_ListObjAppendElement(interp, tcl_result, rcomObj);
03343 
03344 Tcl_Obj *axesListObj = Tcl_NewListObj(0, NULL);
03345 for (i=0; i<3; i++) {
03346 Tcl_Obj *axesObj = Tcl_NewListObj(0, NULL);
03347 Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(priaxes[i][0]));
03348 Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(priaxes[i][1]));
03349 Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(priaxes[i][2]));
03350 Tcl_ListObjAppendElement(interp, axesListObj, axesObj);
03351 }
03352 Tcl_ListObjAppendElement(interp, tcl_result, axesListObj);
03353 
03354 if (moments) {
03355 Tcl_Obj *momListObj = Tcl_NewListObj(0, NULL);
03356 for (i=0; i<3; i++) {
03357 Tcl_Obj *momObj = Tcl_NewListObj(0, NULL);
03358 Tcl_ListObjAppendElement(interp, momObj, Tcl_NewDoubleObj(itensor[i][0]));
03359 Tcl_ListObjAppendElement(interp, momObj, Tcl_NewDoubleObj(itensor[i][1]));
03360 Tcl_ListObjAppendElement(interp, momObj, Tcl_NewDoubleObj(itensor[i][2]));
03361 Tcl_ListObjAppendElement(interp, momListObj, momObj);
03362 }
03363 Tcl_ListObjAppendElement(interp, tcl_result, momListObj);
03364 }
03365 
03366 if (eigenvals) {
03367 Tcl_Obj *eigvListObj = Tcl_NewListObj(0, NULL);
03368 Tcl_ListObjAppendElement(interp, eigvListObj, Tcl_NewDoubleObj(evalue[0]));
03369 Tcl_ListObjAppendElement(interp, eigvListObj, Tcl_NewDoubleObj(evalue[1]));
03370 Tcl_ListObjAppendElement(interp, eigvListObj, Tcl_NewDoubleObj(evalue[2]));
03371 Tcl_ListObjAppendElement(interp, tcl_result, eigvListObj);
03372 }
03373 Tcl_SetObjResult(interp, tcl_result);
03374 
03375 return TCL_OK;
03376 }
03377 
03378 
03379 // measure symmetry <sel> [plane|I|C<n>|S<n> [<vector>]] [-tol <value>]
03380 // [-nobonds] [-verbose <level>]
03381 //
03382 // This function evaluates the molecular symmetry of an atom selection.
03383 // The underlying algorithm finds the symmetry elements such as 
03384 // inversion center, mirror planes, rotary axes and rotary reflections.
03385 // Based on the found elements it guesses the underlying point group.
03386 // The guess is fairly robust and can handle molecules whose
03387 // coordinatesthat deviate to a certain extent from the ideal symmetry.
03388 // The closest match with the highest symmetry will be returned.
03389 //
03390 // Options:
03391 // --------
03392 // -tol <value>
03393 // Allows one to control tolerance of the algorithm when
03394 // considering wether something is symmetric or not.
03395 // A smaller value signifies a lower tolerance, the default
03396 // is 0.1.
03397 // -nobonds 
03398 // If this flag is set then the bond order and orientation
03399 // are not considered when comparing structures.
03400 // -verbose <level>
03401 // Controls the amount of console output.
03402 // A level of 0 means no output, 1 gives some statistics at the
03403 // end of the search (default). Level 2 gives additional info
03404 // about each stage, level 3 more output for each iteration
03405 // and 3, 4 yield yet more additional info.
03406 // -I
03407 // Instead of guessing the symmetry pointgroup of the selection
03408 // determine if the selection's center off mass represents an
03409 // inversion center. The returned value is a score between 0
03410 // and 1 where 1 denotes a perfect match.
03411 // -plane <vector>
03412 // Instead of guessing the symmetry pointgroup of the selection
03413 // determine if the plane with the defined by its normal
03414 // <vector> is a mirror plane of the selection. The
03415 // returned value is a score between 0 and 1 where 1 denotes
03416 // a perfect match.
03417 // -Cn | -Sn <vector>
03418 // Instead of guessing the symmetry pointgroup of the selection
03419 // determine if the rotation or rotary reflection axis Cn/Sn
03420 // with order n defined by <vector> exists for the
03421 // selection. E.g., if you want to query wether the Y-axis
03422 // has a C3 rotational symmetry you specify "C3 {0 1 0}".
03423 // The returned value is a score between 0 and 1 where 1
03424 // denotes a perfect match.
03425 //
03426 // Result:
03427 // -------
03428 // The return value is a TCL list of pairs consisting of a label
03429 // string and a value or list. For each label the data following
03430 // it are described below:
03431 //
03432 // * [pointgroup] The guessed pointgroup:
03433 // * [order] Point group order (the n from above)
03434 // * [elements] Summary of found elements.
03435 // For instance {(i) (C3) 3*(C2) (S6) 3*(sigma)} for D3d.
03436 // * [missing] Elements missing with respect to ideal number of
03437 // elements (same format as above). If this is not an empty
03438 // list then something has gone awfully wrong with the symmetry
03439 // finding algorithm.
03440 // * [additional] Additional elements that would not be expected
03441 // for this point group (same format as above). If this is not
03442 // an empty list then something has gone awfully wrong with the
03443 // symmetry finding algorithm.
03444 // * [com] Center of mass of the selection based on the idealized
03445 // coordinates.
03446 // * [inertia] List of the three axes of inertia, the eigenvalues
03447 // of the moments of inertia tensor and a list of three 0/1 flags 
03448 // specifying for each axis wether it is unique or not.
03449 // * [inversion] Flag 0/1 signifying if there is an inversion center.
03450 // * [axes] Normalized vectors defining rotary axes
03451 // * [rotreflect] Normalized vectors defining rotary reflections
03452 // * [planes] Normalized vectors defining mirror planes.
03453 // * [ideal] Idealized symmetric coordinates for all atoms of
03454 // the selection. The coordinates are listed in the order of 
03455 // increasing atom indices (same order asa returned by the
03456 // atomselect command ``get {x y z}''). Thus you can use the list
03457 // to set the atoms of your selection to the ideal coordinates
03458 // * [unique] Index list defining a set of atoms with unique
03459 // coordinates
03460 // * [orient] Matrix that aligns molecule with GAMESS standard
03461 // orientation
03462 //
03463 // If a certain item is not present (e.g. no planes or no axes)
03464 // then the corresponding value is an empty list.
03465 // The pair format allows to use the result as a TCL array for
03466 // convenient access of the different return items.
03467 
03468 static int vmd_measure_symmetry(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
03469 if (argc<2) {
03470 Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<sel> [element [<vector>]] [-tol <value>] [-nobonds] [-verbose <level>]");
03471 return TCL_ERROR;
03472 }
03473 AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
03474 if (!sel) {
03475 Tcl_AppendResult(interp, "measure symmetry: no atom selection", NULL);
03476 return TCL_ERROR;
03477 }
03478 if (!app->molecule_valid_id(sel->molid())) {
03479 Tcl_AppendResult(interp, "measure symmetry: ",
03480 measure_error(MEASURE_ERR_NOMOLECULE), NULL);
03481 return TCL_ERROR;
03482 }
03483 
03484 double sigma = 0.1;
03485 float axis[3];
03486 int checkelement = 0;
03487 int checkbonds = 1;
03488 int order = 0;
03489 int verbose = 1;
03490 int impose = 0;
03491 int imposeinvers = 0;
03492 int numimposeplan = 0;
03493 int numimposeaxes = 0;
03494 int numimposerref = 0;
03495 float *imposeplan = NULL;
03496 float *imposeaxes = NULL;
03497 float *imposerref = NULL;
03498 int *imposeaxesord = NULL;
03499 int *imposerreford = NULL;
03500 AtomSel *idealsel = NULL;
03501 
03502 if (argc>2) {
03503 int bailout = 0;
03504 int i;
03505 for (i=2; (i<argc && !bailout); i++) {
03506 char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
03507 // Allow syntax with and without leading dash
03508 if (argvcur[0]=='-') argvcur++;
03509 
03510 if (!strupncmp(argvcur, "tol", CMDLEN)) {
03511 if (Tcl_GetDoubleFromObj(interp, objv[i+1], &sigma) != TCL_OK) {
03512 Tcl_AppendResult(interp, " measure symmetry: bad tolerance value", NULL);
03513 bailout = 1; continue;
03514 }
03515 i++;
03516 }
03517 else if (!strupncmp(argvcur, "verbose", CMDLEN)) {
03518 if (Tcl_GetIntFromObj(interp, objv[i+1], &verbose) != TCL_OK) {
03519 Tcl_AppendResult(interp, " measure symmetry: bad verbosity level value", NULL);
03520 bailout = 1; continue;
03521 }
03522 i++;
03523 }
03524 else if (!strupncmp(argvcur, "nobonds", CMDLEN)) {
03525 checkbonds = 0;
03526 }
03527 else if (!strupcmp(argvcur, "I")) {
03528 checkelement = 1;
03529 }
03530 else if (!strupncmp(argvcur, "plane", CMDLEN)) {
03531 if (tcl_get_vector(Tcl_GetStringFromObj(objv[i+1],NULL), axis, interp)!=TCL_OK) {
03532 bailout = 1; continue;
03533 }
03534 checkelement = 2;
03535 i++;
03536 }
03537 else if (!strupncmp(argvcur, "C", 1) || !strupncmp(argvcur, "S", 1)) {
03538 char *begptr = argvcur+1;
03539 char *endptr;
03540 order = strtol(begptr, &endptr, 10);
03541 if (endptr==begptr || *endptr!='0円') {
03542 Tcl_AppendResult(interp, " measure symmetry: bad symmetry element format (must be I, C*, S*, plane, where * is the order). ", NULL);
03543 bailout = 1; continue;
03544 }
03545 
03546 if (tcl_get_vector(Tcl_GetStringFromObj(objv[i+1],NULL), axis, interp)!=TCL_OK) {
03547 bailout = 1; continue;
03548 }
03549 
03550 if (!strupncmp(argvcur, "C", 1)) checkelement = 3;
03551 if (!strupncmp(argvcur, "S", 1)) checkelement = 4;
03552 i++;
03553 }
03554 else if (!strupncmp(argvcur, "idealsel", CMDLEN)) {
03555 idealsel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[i+1],NULL));
03556 if (!sel) {
03557 Tcl_AppendResult(interp, "measure symmetry: no atom selection for idealized coordinates", NULL);
03558 bailout = 1; continue;
03559 }
03560 if (idealsel->molid()!=sel->molid()) {
03561 Tcl_AppendResult(interp, "measure symmetry: selection and idealsel must be from the same molecule", NULL);
03562 bailout = 1; continue;
03563 }
03564 if (idealsel->selected<sel->selected) {
03565 Tcl_AppendResult(interp, "measure symmetry: selection must be a subset of idealsel", NULL);
03566 bailout = 1; continue;
03567 }
03568 // make sure sel is subset of idealsel
03569 int j;
03570 for (j=0; j<sel->num_atoms; j++) {
03571 if (sel->on[j] && !idealsel->on[j]) {
03572 Tcl_AppendResult(interp, "measure symmetry: selection must be a subset of idealsel", NULL);
03573 bailout = 1; continue;
03574 }
03575 }
03576 
03577 i++;
03578 }
03579 else if (!strupncmp(argvcur, "imposeinversion", CMDLEN)) {
03580 imposeinvers = 1;
03581 impose = 1;
03582 }
03583 else if (!strupncmp(argvcur, "imposeplanes", CMDLEN)) {
03584 // Format: list of normal vectors {{x y z} {x y z} ...}
03585 int nelem;
03586 Tcl_Obj **elemListObj;
03587 if (i+1>=argc ||
03588 Tcl_ListObjGetElements(interp, objv[i+1], &nelem, &elemListObj) != TCL_OK) {
03589 Tcl_AppendResult(interp, " measure symmetry: bad syntax for imposeplanes option", NULL);
03590 bailout = 1; continue;
03591 }
03592 float *elem = new float[3L*nelem];
03593 int k;
03594 for (k=0; k<nelem; k++) {
03595 int nobj;
03596 Tcl_Obj **vecObj;
03597 if (Tcl_ListObjGetElements(interp, elemListObj[k], &nobj, &vecObj) != TCL_OK) {
03598 delete [] elem;
03599 Tcl_AppendResult(interp, " measure symmetry: bad syntax for imposeplanes option", NULL);
03600 bailout = 1; continue;
03601 }
03602 if (nobj!=3) {
03603 delete [] elem;
03604 Tcl_AppendResult(interp, " measure symmetry imposeplanes: vector must have 3 elements", NULL);
03605 bailout = 1; continue;
03606 }
03607 
03608 int m;
03609 for (m=0; m<3; m++) {
03610 double d;
03611 if (Tcl_GetDoubleFromObj(interp, vecObj[m], &d) != TCL_OK) {
03612 delete [] elem;
03613 bailout = 1; continue;
03614 }
03615 elem[3L*k+m] = (float)d;
03616 }
03617 }
03618 if (imposeplan) delete [] imposeplan;
03619 imposeplan = elem;
03620 numimposeplan = nelem;
03621 impose = 1;
03622 i++;
03623 }
03624 else if (!strupncmp(argvcur, "imposeaxes", CMDLEN) ||
03625 !strupncmp(argvcur, "imposerotref", CMDLEN)) {
03626 // Format: list of axes and orders {{x y z} 3 {x y z} 2 ...}
03627 int nelem;
03628 Tcl_Obj **elemListObj;
03629 if (i+1>=argc ||
03630 Tcl_ListObjGetElements(interp, objv[i+1], &nelem, &elemListObj) != TCL_OK ||
03631 nelem%2) {
03632 Tcl_AppendResult(interp, " measure symmetry: bad syntax for imposeaxes/imposerotref option", NULL);
03633 bailout = 1; continue;
03634 }
03635 nelem /= 2;
03636 
03637 if (nelem<=0) {
03638 i++;
03639 continue;
03640 }
03641 float *elem = new float[3L*nelem];
03642 int *axorder = new int[nelem];
03643 int k;
03644 for (k=0; k<nelem; k++) {
03645 int nobj;
03646 Tcl_Obj **vecObj;
03647 if (Tcl_ListObjGetElements(interp, elemListObj[2L*k], &nobj, &vecObj) != TCL_OK) {
03648 delete [] elem;
03649 delete [] axorder;
03650 Tcl_AppendResult(interp, " measure symmetry impose: bad syntax for axis vector", NULL);
03651 bailout = 1; continue;
03652 }
03653 if (Tcl_GetIntFromObj(interp, elemListObj[2L*k+1], &axorder[k]) != TCL_OK) {
03654 delete [] elem;
03655 delete [] axorder;
03656 bailout = 1; continue;
03657 }
03658 if (nobj!=3) {
03659 delete [] elem;
03660 delete [] axorder;
03661 Tcl_AppendResult(interp, " measure symmetry impose: axis vector must have 3 elements", NULL);
03662 bailout = 1; continue;
03663 }
03664 
03665 int m;
03666 for (m=0; m<3; m++) {
03667 double d;
03668 if (Tcl_GetDoubleFromObj(interp, vecObj[m], &d) != TCL_OK) {
03669 delete [] elem;
03670 delete [] axorder;
03671 bailout = 1; continue;
03672 }
03673 elem[3L*k+m] = (float)d;
03674 }
03675 }
03676 if (!strupncmp(argvcur, "imposeaxes", CMDLEN)) {
03677 if (imposeaxes) delete [] imposeaxes;
03678 if (imposeaxesord) delete [] imposeaxesord;
03679 imposeaxes = elem;
03680 imposeaxesord = axorder;
03681 numimposeaxes = nelem;
03682 } else if (!strupncmp(argvcur, "imposerotref", CMDLEN)) {
03683 if (imposerref) delete [] imposerref;
03684 if (imposerreford) delete [] imposerreford;
03685 imposerref = elem;
03686 imposerreford = axorder;
03687 numimposerref = nelem;
03688 }
03689 
03690 impose = 1;
03691 i++;
03692 }
03693 else {
03694 Tcl_AppendResult(interp, " measure symmetry: unrecognized option ", NULL);
03695 Tcl_AppendResult(interp, argvcur);
03696 Tcl_AppendResult(interp, ".\n Usage: measure symmetry <selection> [element [<vector>]] [-tol <value>] [-nobonds] [-verbose <level>]", NULL);
03697 bailout = 1; continue;
03698 }
03699 }
03700 
03701 if (bailout) {
03702 if (imposeplan) delete [] imposeplan;
03703 if (imposeaxes) delete [] imposeaxes;
03704 if (imposerref) delete [] imposerref;
03705 if (imposeaxesord) delete [] imposeaxesord;
03706 if (imposerreford) delete [] imposerreford;
03707 return TCL_ERROR;
03708 }
03709 }
03710 
03711 // Initialize
03712 Symmetry sym = Symmetry(sel, app->moleculeList, verbose);
03713 
03714 // Set tolerance for atomic overlapping
03715 sym.set_overlaptol(float(sigma));
03716 
03717 // Wether to include bonds into the analysis
03718 sym.set_checkbonds(checkbonds);
03719 
03720 if (checkelement) {
03721 // We are interested only in the score for a specific
03722 // symmetry element.
03723 float overlap = 0.0;
03724 if (checkelement==1) {
03725 overlap = sym.score_inversion();
03726 }
03727 else if (checkelement==2) {
03728 overlap = sym.score_plane(axis);
03729 }
03730 else if (checkelement==3) {
03731 overlap = sym.score_axis(axis, order);
03732 }
03733 else if (checkelement==4) {
03734 overlap = sym.score_rotary_reflection(axis, order);
03735 }
03736 
03737 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
03738 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(overlap));
03739 Tcl_SetObjResult(interp, tcl_result);
03740 return TCL_OK;
03741 }
03742 
03743 if (impose) {
03744 sym.impose(imposeinvers, numimposeplan, imposeplan,
03745 numimposeaxes, imposeaxes, imposeaxesord,
03746 numimposerref, imposerref, imposerreford);
03747 if (imposeplan) delete [] imposeplan;
03748 if (imposeaxes) delete [] imposeaxes;
03749 if (imposerref) delete [] imposerref;
03750 if (imposeaxesord) delete [] imposeaxesord;
03751 if (imposerreford) delete [] imposerreford;
03752 }
03753 
03754 int ret_val = sym.guess(float(sigma));
03755 if (ret_val < 0) {
03756 Tcl_AppendResult(interp, "measure symmetry: ", measure_error(ret_val), NULL);
03757 return TCL_ERROR;
03758 }
03759 
03760 int natoms = sel->selected;
03761 Symmetry *s = &sym;
03762 
03763 if (idealsel) {
03764 Symmetry *isym = new Symmetry(idealsel, app->moleculeList, verbose);
03765 isym->set_overlaptol(float(sigma));
03766 isym->set_checkbonds(checkbonds);
03767 int j;
03768 float *plane = new float[3L*sym.numplanes()];
03769 for (j=0; j<sym.numplanes(); j++) {
03770 vec_copy(&plane[3L*j], sym.plane(j));
03771 }
03772 int *axisorder = new int[sym.numaxes()];
03773 float *axis = new float[3L*sym.numaxes()];
03774 for (j=0; j<sym.numaxes(); j++) {
03775 axisorder[j] = sym.get_axisorder(j);
03776 vec_copy(&axis[3L*j], sym.axis(j));
03777 }
03778 int *rrorder = new int[sym.numrotreflect()];
03779 float *rraxis = new float[3L*sym.numrotreflect()];
03780 for (j=0; j<sym.numrotreflect(); j++) {
03781 rrorder[j] = sym.get_rotreflectorder(j);
03782 vec_copy(&rraxis[3L*j], sym.rotreflect(j));
03783 }
03784 // XXX must check if sel is subset of idealsel
03785 int k=0, m=0;
03786 for (j=0; j<sel->num_atoms; j++) {
03787 if (idealsel->on[j]) {
03788 if (sel->on[j]) {
03789 vec_copy(isym->idealpos(k), sym.idealpos(m));
03790 m++;
03791 }
03792 k++;
03793 }
03794 }
03795 isym->impose(sym.has_inversion(),
03796 sym.numplanes(), plane,
03797 sym.numaxes(), axis, axisorder,
03798 sym.numrotreflect(), rraxis, rrorder);
03799 
03800 ret_val = isym->guess(float(sigma));
03801 if (ret_val < 0) {
03802 Tcl_AppendResult(interp, "measure symmetry: ", measure_error(ret_val), NULL);
03803 return TCL_ERROR;
03804 }
03805 
03806 natoms = idealsel->selected;
03807 s = isym;
03808 }
03809 
03810 int pgorder;
03811 char pointgroup[6];
03812 s->get_pointgroup(pointgroup, &pgorder);
03813 
03814 int i;
03815 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
03816 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("pointgroup", -1));
03817 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj(pointgroup, -1));
03818 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("order", -1));
03819 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewIntObj(pgorder));
03820 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("rmsd", -1));
03821 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(s->get_rmsd()));
03822 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("elements", -1));
03823 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj(s->get_element_summary(), -1));
03824 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("missing", -1));
03825 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj(s->get_missing_elements(), -1));
03826 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("additional", -1));
03827 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj(s->get_additional_elements(), -1));
03828 
03829 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("com", -1));
03830 Tcl_Obj *invObj = Tcl_NewListObj(0, NULL);
03831 float *com = s->center_of_mass();
03832 Tcl_ListObjAppendElement(interp, invObj, Tcl_NewDoubleObj(com[0]));
03833 Tcl_ListObjAppendElement(interp, invObj, Tcl_NewDoubleObj(com[1]));
03834 Tcl_ListObjAppendElement(interp, invObj, Tcl_NewDoubleObj(com[2]));
03835 Tcl_ListObjAppendElement(interp, tcl_result, invObj);
03836 
03837 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("inertia", -1));
03838 Tcl_Obj *inertListListObj = Tcl_NewListObj(0, NULL);
03839 float *inertia = s->get_inertia_axes();
03840 float *eigenval = s->get_inertia_eigenvals();
03841 int *unique = s->get_inertia_unique();
03842 for (i=0; i<3; i++) {
03843 Tcl_Obj *inertObj = Tcl_NewListObj(0, NULL);
03844 Tcl_ListObjAppendElement(interp, inertObj, Tcl_NewDoubleObj(inertia[3L*i]));
03845 Tcl_ListObjAppendElement(interp, inertObj, Tcl_NewDoubleObj(inertia[3L*i+1]));
03846 Tcl_ListObjAppendElement(interp, inertObj, Tcl_NewDoubleObj(inertia[3L*i+2]));
03847 Tcl_Obj *inertListObj = Tcl_NewListObj(0, NULL);
03848 Tcl_ListObjAppendElement(interp, inertListObj, inertObj);
03849 Tcl_ListObjAppendElement(interp, inertListObj, Tcl_NewDoubleObj(eigenval[i]));
03850 Tcl_ListObjAppendElement(interp, inertListObj, Tcl_NewIntObj(unique[i]));
03851 Tcl_ListObjAppendElement(interp, inertListListObj, inertListObj);
03852 }
03853 Tcl_ListObjAppendElement(interp, tcl_result, inertListListObj);
03854 
03855 
03856 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("inversion", -1));
03857 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewIntObj(s->has_inversion()));
03858 
03859 
03860 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("axes", -1));
03861 Tcl_Obj *axesListListObj = Tcl_NewListObj(0, NULL);
03862 for (i=0; i<s->numaxes(); i++) {
03863 Tcl_Obj *axesObj = Tcl_NewListObj(0, NULL);
03864 //printf("Tcl %i: %.2f %.2f %.2f\n", i, s->axis(i)[0], s->axis(i)[1], s->axis(i)[2]);
03865 Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->axis(i)[0]));
03866 Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->axis(i)[1]));
03867 Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->axis(i)[2]));
03868 Tcl_Obj *axesListObj = Tcl_NewListObj(0, NULL);
03869 Tcl_ListObjAppendElement(interp, axesListObj, axesObj);
03870 Tcl_ListObjAppendElement(interp, axesListObj, Tcl_NewIntObj(s->get_axisorder(i)));
03871 int axistype = s->get_axistype(i);
03872 if (axistype & PRINCIPAL_AXIS && 
03873 !(!s->is_spherical_top() && (axistype & PERPENDICULAR_AXIS))) {
03874 Tcl_ListObjAppendElement(interp, axesListObj, Tcl_NewStringObj("principal", -1));
03875 } else if (!s->is_spherical_top() && (axistype & PERPENDICULAR_AXIS)) {
03876 Tcl_ListObjAppendElement(interp, axesListObj, Tcl_NewStringObj("perpendicular", -1));
03877 } else {
03878 Tcl_ListObjAppendElement(interp, axesListObj, Tcl_NewListObj(0, NULL));
03879 }
03880 Tcl_ListObjAppendElement(interp, axesListListObj, axesListObj);
03881 }
03882 Tcl_ListObjAppendElement(interp, tcl_result, axesListListObj);
03883 
03884 
03885 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("rotreflect", -1));
03886 Tcl_Obj *rraxesListListObj = Tcl_NewListObj(0, NULL);
03887 for (i=0; i<s->numrotreflect(); i++) {
03888 Tcl_Obj *axesObj = Tcl_NewListObj(0, NULL);
03889 //printf("Tcl %i: %.2f %.2f %.2f\n", i, s->axis(i)[0], s->axis(i)[1], s->axis(i)[2]);
03890 Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->rotreflect(i)[0]));
03891 Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->rotreflect(i)[1]));
03892 Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->rotreflect(i)[2]));
03893 Tcl_Obj *rraxesListObj = Tcl_NewListObj(0, NULL);
03894 Tcl_ListObjAppendElement(interp, rraxesListObj, axesObj);
03895 Tcl_ListObjAppendElement(interp, rraxesListObj, Tcl_NewIntObj(s->get_rotreflectorder(i)));
03896 Tcl_ListObjAppendElement(interp, rraxesListObj, Tcl_NewIntObj(s->get_rotrefltype(i)));
03897 Tcl_ListObjAppendElement(interp, rraxesListListObj, rraxesListObj);
03898 }
03899 Tcl_ListObjAppendElement(interp, tcl_result, rraxesListListObj);
03900 
03901 
03902 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("planes", -1));
03903 Tcl_Obj *planesListListObj = Tcl_NewListObj(0, NULL);
03904 for (i=0; i<s->numplanes(); i++) {
03905 Tcl_Obj *planesObj = Tcl_NewListObj(0, NULL);
03906 //printf("Tcl %i: %.2f %.2f %.2f\n", i, s->plane(i)[0], s->plane(i)[1], s->plane(i)[2]);
03907 Tcl_ListObjAppendElement(interp, planesObj, Tcl_NewDoubleObj(s->plane(i)[0]));
03908 Tcl_ListObjAppendElement(interp, planesObj, Tcl_NewDoubleObj(s->plane(i)[1]));
03909 Tcl_ListObjAppendElement(interp, planesObj, Tcl_NewDoubleObj(s->plane(i)[2]));
03910 Tcl_Obj *planesListObj = Tcl_NewListObj(0, NULL);
03911 Tcl_ListObjAppendElement(interp, planesListObj, planesObj);
03912 switch (s->get_planetype(i)) {
03913 case 1:
03914 Tcl_ListObjAppendElement(interp, planesListObj, Tcl_NewStringObj("vertical", -1));
03915 break;
03916 case 3:
03917 Tcl_ListObjAppendElement(interp, planesListObj, Tcl_NewStringObj("dihedral", -1));
03918 break;
03919 case 4:
03920 Tcl_ListObjAppendElement(interp, planesListObj, Tcl_NewStringObj("horizontal", -1));
03921 break;
03922 default:
03923 Tcl_ListObjAppendElement(interp, planesListObj, Tcl_NewListObj(0, NULL));
03924 }
03925 Tcl_ListObjAppendElement(interp, planesListListObj, planesListObj);
03926 }
03927 Tcl_ListObjAppendElement(interp, tcl_result, planesListListObj);
03928 
03929 
03930 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("ideal", -1));
03931 Tcl_Obj *idealcoorListObj = Tcl_NewListObj(0, NULL);
03932 for (i=0; i<natoms; i++) {
03933 Tcl_Obj *idealcoorObj = Tcl_NewListObj(0, NULL);
03934 //printf("Tcl %i: %.2f %.2f %.2f\n", i, s->plane(i)[0], s->plane(i)[1], s->plane(i)[2]);
03935 Tcl_ListObjAppendElement(interp, idealcoorObj, Tcl_NewDoubleObj(s->idealpos(i)[0]));
03936 Tcl_ListObjAppendElement(interp, idealcoorObj, Tcl_NewDoubleObj(s->idealpos(i)[1]));
03937 Tcl_ListObjAppendElement(interp, idealcoorObj, Tcl_NewDoubleObj(s->idealpos(i)[2]));
03938 Tcl_ListObjAppendElement(interp, idealcoorListObj, idealcoorObj);
03939 }
03940 Tcl_ListObjAppendElement(interp, tcl_result, idealcoorListObj);
03941 
03942 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("unique", -1));
03943 Tcl_Obj *uniquecoorListObj = Tcl_NewListObj(0, NULL);
03944 for (i=0; i<natoms; i++) {
03945 if (!s->get_unique_atom(i)) continue; 
03946 Tcl_ListObjAppendElement(interp, uniquecoorListObj, Tcl_NewIntObj(i));
03947 }
03948 Tcl_ListObjAppendElement(interp, tcl_result, uniquecoorListObj);
03949 
03950 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("orient", -1));
03951 Matrix4 *orient = s->get_orientation();
03952 Tcl_Obj *matrixObj = Tcl_NewListObj(0, NULL);
03953 for (i=0; i<4; i++) {
03954 Tcl_Obj *rowObj = Tcl_NewListObj(0, NULL);
03955 Tcl_ListObjAppendElement(interp, rowObj, Tcl_NewDoubleObj(orient->mat[i+0]));
03956 Tcl_ListObjAppendElement(interp, rowObj, Tcl_NewDoubleObj(orient->mat[i+4]));
03957 Tcl_ListObjAppendElement(interp, rowObj, Tcl_NewDoubleObj(orient->mat[i+8]));
03958 Tcl_ListObjAppendElement(interp, rowObj, Tcl_NewDoubleObj(orient->mat[i+12]));
03959 Tcl_ListObjAppendElement(interp, matrixObj, rowObj);
03960 }
03961 Tcl_ListObjAppendElement(interp, tcl_result, matrixObj);
03962 
03963 Tcl_SetObjResult(interp, tcl_result);
03964 if (idealsel) {
03965 delete s;
03966 }
03967 return TCL_OK;
03968 
03969 }
03970 
03971 // Function: vmd_measure_transoverlap <selection>
03972 // Returns: The structural overlap of a selection with a copy of itself
03973 // that is transformed according to a given transformation matrix.
03974 // The normalized sum over all gaussian function values of the pair
03975 // distances between atoms in the original and the transformed
03976 // selection.
03977 // Input: the atom selection and the transformation matrix.
03978 // Option: with -sigma you can specify the sigma value of the overlap 
03979 // gaussian function. The default is 0.1 Angstrom.
03980 static int vmd_measure_trans_overlap(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
03981 if (argc!=3 && argc!=5) {
03982 Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<sel> <matrix> [-sigma <value>]");
03983 return TCL_ERROR;
03984 }
03985 AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
03986 if (!sel) {
03987 Tcl_AppendResult(interp, "measure transoverlap: no atom selection", NULL);
03988 return TCL_ERROR;
03989 }
03990 if (!app->molecule_valid_id(sel->molid())) {
03991 Tcl_AppendResult(interp, "measure transoverlap: ",
03992 measure_error(MEASURE_ERR_NOMOLECULE), NULL);
03993 return TCL_ERROR;
03994 }
03995 
03996 // Get the transformation matrix
03997 Matrix4 trans;
03998 if (tcl_get_matrix("measure transoverlap: ",interp,objv[2], trans.mat) != TCL_OK) {
03999 return TCL_ERROR;
04000 }
04001 
04002 double sigma = 0.1;
04003 if (argc==5) {
04004 if (!strupncmp(Tcl_GetStringFromObj(objv[3],NULL), "-sigma", CMDLEN)) {
04005 if (Tcl_GetDoubleFromObj(interp, objv[4], &sigma) != TCL_OK) {
04006 Tcl_AppendResult(interp, " measure transoverlap: bad sigma value", NULL);
04007 return TCL_ERROR;
04008 }
04009 } else {
04010 Tcl_AppendResult(interp, " measure transoverlap: unrecognized option\n", NULL);
04011 Tcl_AppendResult(interp, " Usage: measure transoverlap <sel> <matrix> [-sigma <value>]", NULL);
04012 return TCL_ERROR;
04013 }
04014 }
04015 
04016 int maxnatoms = 100;// XXX
04017 float overlap;
04018 int ret_val = measure_trans_overlap(sel, app->moleculeList, &trans,
04019 float(sigma), NOSKIP_IDENTICAL, 
04020 maxnatoms, overlap);
04021 if (ret_val < 0) {
04022 Tcl_AppendResult(interp, "measure transoverlap: ", measure_error(ret_val), NULL);
04023 return TCL_ERROR;
04024 }
04025 
04026 Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
04027 Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(overlap));
04028 Tcl_SetObjResult(interp, tcl_result);
04029 
04030 return TCL_OK;
04031 }
04032 
04033 
04034 
04035 //
04036 // Raycasting brute-force approach by Juan R. Perilla <juan@perilla.me>
04037 //
04038 int vmd_measure_volinterior(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
04039 int verbose = 0;
04040 int recognized=0;
04041 if (argc < 3) {
04042 // " options: --allframes (average over all frames)\n"
04043 Tcl_SetResult(interp, (char *) "usage: volinterior "
04044 " <selection1> [options]\n"
04045 "-res (default 10.0)\n"
04046 "-spacing (default res/3)\n"
04047 "-loadmap (load synth map)\n"
04048 "-probmap (compute and load probability map)\n"
04049 " --> -count_pmap (return percentile-based voxel counts)\n"
04050 "-discretize [float cutoff] (creates discrete map with cutoff probability)\n"
04051 "-mol molid [default: top]\n"
04052 "-vol volid [0]\n"
04053 "-nrays number of rays to cast [6]\n"
04054 "-isovalue [float: 1.0]\n"
04055 "-verbose\n"
04056 "-overwrite volid\n",
04057 TCL_STATIC);
04058 return TCL_ERROR;
04059 }
04060 
04061 //atom selection
04062 AtomSel *sel = NULL;
04063 sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
04064 if (!sel) {
04065 Tcl_AppendResult(interp, "volinterior: no atom selection.", NULL);
04066 return TCL_ERROR;
04067 }
04068 if (!sel->selected) {
04069 Tcl_AppendResult(interp, "volinterior: no atoms selected.", NULL);
04070 return TCL_ERROR;
04071 }
04072 if (!app->molecule_valid_id(sel->molid())) {
04073 Tcl_AppendResult(interp, "invalid mol id.", NULL);
04074 return TCL_ERROR;
04075 }
04076 int loadsynthmap = 0;
04077 int loadpmap=0; 
04078 int count_pmap=0;
04079 int print_raydirs=0; 
04080 int molid = -1;
04081 int volid = -1;
04082 int volidOverwrite = -1;
04083 int overwrite=0;
04084 int nrays=6;
04085 long Nout = 0;
04086 float isovalue=1.0;
04087 float proCutoff=0.90f;
04088 int process=0;
04089 float radscale;
04090 double resolution = 10.0;
04091 double gspacing;
04092 MoleculeList *mlist = app->moleculeList;
04093 Molecule *mymol = mlist->mol_from_id(sel->molid());
04094 
04095 for (int i=2; i < argc; i++) {
04096 recognized=0;
04097 char *opt = Tcl_GetStringFromObj(objv[i], NULL);
04098 if (!strcmp(opt, "volinterior")) {
04099 recognized=1;
04100 }
04101 if (!strcmp(opt, "-mol")) {
04102 if (i == argc-1) {
04103 Tcl_AppendResult(interp, "No molid specified",NULL);
04104 return TCL_ERROR;
04105 } else if ( Tcl_GetIntFromObj(interp, objv[i+1], &molid) != TCL_OK) {
04106 Tcl_AppendResult(interp, "\n molid incorrectly specified",NULL);
04107 return TCL_ERROR;
04108 }
04109 recognized=1;
04110 }
04111 if (!strcmp(opt, "-probmap")) {
04112 loadpmap=1;
04113 recognized=1;
04114 }
04115 if (!strcmp(opt, "-checkrays")) {
04116 print_raydirs=1;
04117 recognized=1;
04118 }
04119 if (!strcmp(opt, "-count_pmap")) {
04120 count_pmap=1;
04121 recognized=1;
04122 }
04123 if (!strcmp(opt, "-discretize")) {
04124 double tcutoff;
04125 if (i==argc-1) {
04126 Tcl_AppendResult(interp, "No cutoff probability given for -discretize flag",NULL);
04127 return TCL_ERROR;
04128 } else if (Tcl_GetDoubleFromObj(interp, objv[i+1], &tcutoff)!=TCL_OK) {
04129 Tcl_AppendResult(interp, "\n Cutoff probability for -discretize flag incorrectly specified.",NULL);
04130 return TCL_ERROR;
04131 }
04132 proCutoff=(float)tcutoff;
04133 recognized=1;
04134 process=1;
04135 }
04136 if (!strcmp(opt, "-vol")) {
04137 if (i == argc-1){
04138 Tcl_AppendResult(interp, "No volume id specified",NULL);
04139 return TCL_ERROR;
04140 } else if ( Tcl_GetIntFromObj(interp, objv[i+1], &volid) != TCL_OK) {
04141 Tcl_AppendResult(interp, "\n volume id incorrectly specified",NULL);
04142 return TCL_ERROR;
04143 }
04144 recognized=1;
04145 }
04146 
04147 if (!strcmp(opt, "-overwrite")) {
04148 if (i == argc-1){
04149 Tcl_AppendResult(interp, "No volume id specified",NULL);
04150 return TCL_ERROR;
04151 } else if (Tcl_GetIntFromObj(interp, objv[i+1], &volidOverwrite) != TCL_OK) {
04152 Tcl_AppendResult(interp, "\n volume id incorrectly specified",NULL);
04153 return TCL_ERROR;
04154 }
04155 overwrite=1;
04156 recognized=1;
04157 }
04158 
04159 // Calculate synthmap
04160 if (!strcmp(opt, "-loadmap")) {
04161 loadsynthmap = 1;
04162 recognized=1;
04163 }
04164 if (!strcmp(opt, "-mol")) {
04165 if (i == argc-1) {
04166 Tcl_AppendResult(interp, "No molid specified",NULL);
04167 return TCL_ERROR;
04168 } else if ( Tcl_GetIntFromObj(interp, objv[i+1], &molid) != TCL_OK) {
04169 Tcl_AppendResult(interp, "\n molid incorrectly specified",NULL);
04170 return TCL_ERROR;
04171 }
04172 recognized=1;
04173 }
04174 if (!strcmp(opt, "-res")) {
04175 if (i == argc-1) {
04176 Tcl_AppendResult(interp, "No resolution specified",NULL);
04177 return TCL_ERROR;
04178 } else if (Tcl_GetDoubleFromObj(interp, objv[i+1], &resolution) != TCL_OK) { 
04179 Tcl_AppendResult(interp, "\nResolution incorrectly specified",NULL);
04180 return TCL_ERROR;
04181 }
04182 recognized=1;
04183 }
04184 if (!strcmp(opt, "-spacing")) {
04185 if (i == argc-1) {
04186 Tcl_AppendResult(interp, "No spacing specified",NULL);
04187 return TCL_ERROR;
04188 } else if ( Tcl_GetDoubleFromObj(interp, objv[i+1], &gspacing) != TCL_OK) {
04189 Tcl_AppendResult(interp, "\nspacing incorrectly specified",NULL);
04190 return TCL_ERROR;
04191 }
04192 recognized=1;
04193 }
04194 
04195 if (!strcmp(opt,"-nrays")) {
04196 if (i == argc-1) {
04197 Tcl_AppendResult(interp, "No number of rays specified",NULL);
04198 return TCL_ERROR;
04199 } else if ( Tcl_GetIntFromObj(interp, objv[i+1], &nrays) != TCL_OK) {
04200 Tcl_AppendResult(interp, "\n number of rays incorrectly specified",NULL);
04201 return TCL_ERROR;
04202 }
04203 recognized=1;
04204 }
04205 
04206 if (!strcmp(opt,"-isovalue")) {
04207 double tmp;
04208 if (i == argc-1) {
04209 Tcl_AppendResult(interp,"No isovalue specified",NULL);
04210 return TCL_ERROR;
04211 } else if (Tcl_GetDoubleFromObj(interp,objv[i+1],&tmp) != TCL_OK) {
04212 Tcl_AppendResult(interp,"\n Isovalue incorrectly specified", NULL);
04213 return TCL_ERROR;
04214 }
04215 isovalue=(float) tmp;
04216 recognized=1;
04217 }
04218 
04219 // if (!strcmp(opt, "-o")) {
04220 // if (i == argc-1) {
04221 // // Tcl_AppendResult(interp, "No output file specified",NULL);
04222 // // return TCL_ERROR;
04223 // if (verbose) 
04224 // printf("No output file specified.");
04225 // } else {
04226 // outputmap = Tcl_GetStringFromObj(objv[i+1], NULL);
04227 // }
04228 // }
04229 
04230 if (!strcmp(opt, "-verbose") || (getenv("VMDVOLINVERBOSE") != NULL)) {
04231 verbose = 1;
04232 recognized=1;
04233 }
04234 
04235 if (recognized==0) {
04236 // XXX what case is this supposed to handle? Dangling number parms?
04237 if (isdigit(opt[0]) || isfloat(opt)) { 
04238 continue;
04239 } else {
04240 Tcl_AppendResult(interp,"\n unrecognized option: '",opt,"'",NULL);
04241 return TCL_ERROR;
04242 }
04243 }
04244 }
04245 
04246 if ((process||count_pmap) && !loadpmap) {
04247 Tcl_AppendResult(interp,"\n Cannot give -process or -count_pmap flags given without -probmap!",NULL);
04248 return TCL_ERROR;
04249 }
04250 
04251 Molecule *volmolOverwrite = NULL;
04252 VolumetricData *volmapOverwrite = NULL;
04253 Molecule *pmolOverwrite=NULL;
04254 VolumetricData *pmapOverwrite=NULL;
04255 if (overwrite==1) {
04256 if (loadpmap!=1) {
04257 volmolOverwrite = mlist->mol_from_id(molid);
04258 volmapOverwrite = volmolOverwrite->modify_volume_data(volidOverwrite);
04259 if(volmapOverwrite==NULL) {
04260 Tcl_AppendResult(interp, "\n no overwrite volume correctly specified",NULL);
04261 return TCL_ERROR;
04262 }
04263 } else {
04264 pmolOverwrite=mlist->mol_from_id(molid);
04265 pmapOverwrite=pmolOverwrite->modify_volume_data(volidOverwrite);
04266 if (pmapOverwrite==NULL) {
04267 Tcl_AppendResult(interp,"\n no pmap overwrite volume correctly specified",NULL);
04268 return TCL_ERROR;
04269 }
04270 }
04271 }
04272 
04273 VolumetricData *pmapT = NULL;
04274 VolumetricData *procmap = NULL;
04275 VolumetricData *pmap = NULL; 
04276 VolumetricData *target = NULL;
04277 VolumetricData *volmapA = NULL;
04278 
04279 
04280 const float *framepos = sel->coordinates(app->moleculeList);
04281 const float *radii = mymol->radius();
04282 radscale= 0.2f * float(resolution);
04283 if (gspacing == 0) {
04284 gspacing=resolution*0.33;
04285 }
04286 
04287 int quality=0;
04288 QuickSurf *qs = new QuickSurf();
04289 if (resolution >= 9)
04290 quality = 0;
04291 else
04292 quality = 3;
04293 
04294 if (molid != -1 && volid != -1 ) {
04295 Molecule *volmol = mlist->mol_from_id(molid);
04296 volmapA = (VolumetricData *) volmol->get_volume_data(volid);
04297 if (volmapA==NULL) {
04298 Tcl_AppendResult(interp, "\n no target volume correctly specified", NULL);
04299 return TCL_ERROR;
04300 }
04301 } else {
04302 if (verbose) printf("\n Calculating grid ... \n");
04303 int cuda_err=-1;
04304 #if defined(VMDCUDA)
04305 cuda_err = vmd_cuda_calc_density(sel, app->moleculeList, quality,
04306 radscale, gspacing, &volmapA, 
04307 NULL, NULL, verbose);
04308 #endif
04309 if (cuda_err == -1 ) {
04310 if (verbose) printf("Using CPU version of the code ... \n");
04311 volmapA = qs->calc_density_map(sel, mymol, framepos, radii,
04312 quality, radscale, float(gspacing));
04313 }
04314 if (verbose) printf("Done.\n");
04315 }
04316 
04317 if (volmapA == NULL) {
04318 Tcl_AppendResult(interp, "\n no test volume or molid correctly specified",NULL);
04319 return TCL_ERROR;
04320 }
04321 target=CreateEmptyGrid(volmapA);
04322 
04323 if (loadpmap == 1) {
04324 pmapT = CreateProbGrid(volmapA);
04325 }
04326 
04327 // Normalize dir vector 
04328 // TODO: Let user define direction vectors
04329 
04330 //vec_normalize(rayDir);
04331 
04332 // Create Grid
04333 #if 0
04334 float rayDir[3] = {1,0,0};
04335 Nout += RaycastGrid(volmapA,target,isovalue,rayDir);
04336 rayDir[0] = -1; rayDir[1]=0; rayDir[2]=0;
04337 Nout += RaycastGrid(volmapA,target,isovalue,rayDir);
04338 rayDir[0] = 0; rayDir[1]=1; rayDir[2]=0;
04339 Nout += RaycastGrid(volmapA,target,isovalue,rayDir);
04340 rayDir[0] = 0; rayDir[1]=-1; rayDir[2]=0;
04341 Nout += RaycastGrid(volmapA,target,isovalue,rayDir);
04342 rayDir[0] = 0; rayDir[1]=0; rayDir[2]=1;
04343 Nout += RaycastGrid(volmapA,target,isovalue,rayDir);
04344 rayDir[0] = 0; rayDir[1]=0; rayDir[2]=-1;
04345 Nout += RaycastGrid(volmapA,target,isovalue,rayDir);
04346 #endif
04347 #if 0
04348 if (verbose) printf("Marking down boundary.\n");
04349 long nVoxIso=markIsoGrid(volmapA, target,isovalue);
04350 if (verbose) printf("Casting rays ...\n");
04351 float rayDir[3] = {0.1f,0.1f,1.0f};
04352 Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04353 if (verbose) printf("Dir 1 %ld \n",Nout);
04354 rayDir[0] = -0.1f; rayDir[1]=-0.1f; rayDir[2]=-1.0f;
04355 Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04356 if (verbose) printf("Dir 2 %ld \n",Nout);
04357 rayDir[0] = 0.1f; rayDir[1]=1.0f; rayDir[2]=0.1f;
04358 Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04359 if (verbose) printf("Dir 3 %ld \n",Nout);
04360 rayDir[0] = -0.1f; rayDir[1]=-1.0f; rayDir[2]=-0.1f;
04361 Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04362 if (verbose) printf("Dir 4 %ld \n",Nout);
04363 rayDir[0] = 1.0f; rayDir[1]=0.1f; rayDir[2]=0.1f;
04364 Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04365 if (verbose) printf("Dir 5 %ld \n",Nout);
04366 rayDir[0] = -1.0f; rayDir[1]=-0.1f; rayDir[2]=-0.1f;
04367 Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04368 if (verbose) printf("Dir 6 %ld \n",Nout);
04369 rayDir[0] = -0.5f; rayDir[1]=-0.5f; rayDir[2]=-0.5f;
04370 Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04371 if (verbose) printf("Dir 7 %ld \n",Nout);
04372 rayDir[0] = 0.5f; rayDir[1]=0.5f; rayDir[2]=0.5f;
04373 Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04374 if (verbose) printf("Dir 9 %ld \n",Nout);
04375 rayDir[0] = -0.5f; rayDir[1]=0.5f; rayDir[2]=0.5f;
04376 Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04377 if (verbose) printf("Dir 10 %ld \n",Nout);
04378 rayDir[0] = 0.5f; rayDir[1]=-0.5f; rayDir[2]=0.5f;
04379 Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04380 if (verbose) printf("Dir 11 %ld \n",Nout);
04381 rayDir[0] = 0.5f; rayDir[1]=0.5f; rayDir[2]=-0.5f;
04382 Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04383 if (verbose) printf("Dir 12 %ld \n",Nout);
04384 #endif
04385 if (verbose) printf("Marking down boundary.\n");
04386 long nVoxIso=markIsoGrid(volmapA, target,isovalue);
04387 float rayDir[3] = {0.1f,0.1f,1.0f};
04388 static const float RAND_MAX_INV = 1.0f/float(VMD_RAND_MAX);
04389 
04390 float *poisson_set = new float[(nrays+1)*2];
04391 int numcandidates=40;
04392 int converged=poisson_sample_on_sphere(poisson_set,nrays,numcandidates,verbose);
04393 if (converged==0) {
04394 if (verbose) {
04395 printf("Poisson disk sampler failed for measure volinterior!\n");
04396 printf("... continuing with standard ray direction generator.\n");
04397 }
04398 }
04399 
04400 if (print_raydirs && converged)
04401 print_xyz(poisson_set,nrays);
04402 
04403 if (verbose) printf("Casting rays...\n");
04404 // Seed the random number generator before each calculation. 
04405 //vmd_srandom(103467829);
04406 // Cast nrays uniformly over the sphere
04407 
04408 // XXX Use of sincosf() now in place. It would be much more work-efficient
04409 // to build up batches of rays and pass them into the multithreaded
04410 // work routines, as launching the worker threads has a non-trivial
04411 // cost. It would be better if they plow through many/all rays before
04412 // returning, since the control loops here aren't checking for any
04413 // conditions to be met other than that we've processed all of the rays.
04414 
04415 float sine1, cosine1, sine2, cosine2;
04416 for (int ray=0; ray < nrays; ray++) { 
04417 float u1 = (float) vmd_random();
04418 float u2 = (float) vmd_random();
04419 float z = 2.0f*u1*RAND_MAX_INV -1.0f;
04420 float phi = (float) (2.0f*VMD_PI*u2*RAND_MAX_INV);
04421 float R = sqrtf(1.0f-z*z);
04422 if (converged==1) {
04423 sincosf(poisson_set[ray*2+0],&sine1,&cosine1);
04424 sincosf(poisson_set[ray*2+1],&sine2,&cosine2);
04425 rayDir[0]=cosine1*sine2;
04426 rayDir[1]=sine1*sine2;
04427 rayDir[2]=cosine2;
04428 } else {
04429 sincosf(phi,&sine1,&cosine1);
04430 rayDir[0]=R*cosine1;
04431 rayDir[1]=R*sine1;
04432 rayDir[2]=z;
04433 }
04434 
04435 // XXX rather than launching threads per ray, we should be launching
04436 // the worker threads and processing _all_ of the rays...
04437 // That would also lead toward an implementation that could
04438 // eventually be migrated to the GPU for higher performance.
04439 if (loadpmap == 1) {
04440 Nout += volin_threaded_prob(volmapA, target, pmapT, isovalue, rayDir);
04441 } else {
04442 Nout += volin_threaded(volmapA, target, isovalue, rayDir);
04443 }
04444 
04445 if (verbose) printf("Ray(%d) (%4.3f %4.3f %4.3f). Voxels : %ld \n",ray+1,rayDir[0],rayDir[1],rayDir[2],Nout); 
04446 }
04447 if (verbose) printf("Done.\n");
04448 
04449 delete [] poisson_set;
04450 
04451 if (verbose) printf("Counting voxels above isovalue ...\n");
04452 
04453 Nout=countIsoGrids(target,EXTERIORVOXEL);
04454 long nIn=countIsoGrids(target,INTERIORVOXEL);
04455 
04456 if (loadsynthmap == 1 ) {
04457 app->molecule_add_volumetric(molid, volmapA->name, volmapA->origin, 
04458 volmapA->xaxis, volmapA->yaxis, volmapA->zaxis,
04459 volmapA->xsize, volmapA->ysize, volmapA->zsize,
04460 volmapA->data);
04461 volmapA->compute_volume_gradient();
04462 volmapA->data = NULL; // prevent destruction of density array; 
04463 }
04464 
04465 if (overwrite != 1 ) {
04466 if (loadpmap==1) {
04467 if (verbose) printf("Normalizing probability map ...\n");
04468 pmap = normalize_pmap(pmapT, nrays);
04469 app->molecule_add_volumetric(molid, pmap->name, pmap->origin,
04470 pmap->xaxis, pmap->yaxis, pmap->zaxis,
04471 pmap->xsize, pmap->ysize, pmap->zsize, 
04472 pmap->data);
04473 pmap->compute_volume_gradient();
04474 } else {
04475 app->molecule_add_volumetric(molid, target->name, target->origin, 
04476 target->xaxis, target->yaxis, target->zaxis,
04477 target->xsize, target->ysize, target->zsize,
04478 target->data);
04479 target->compute_volume_gradient();
04480 target->data = NULL; // prevent destruction of density array; 
04481 }
04482 } else {
04483 if (loadpmap==1) {
04484 if (verbose) printf("Normalizing probability map ...\n");
04485 pmap = normalize_pmap(pmapT, nrays);
04486 if (verbose) printf("Overwriting volume...\n");
04487 pmapOverwrite->name=pmap->name;
04488 pmapOverwrite->xsize=pmap->xsize;
04489 pmapOverwrite->ysize=pmap->ysize;
04490 pmapOverwrite->zsize=pmap->zsize;
04491 pmapOverwrite->data=pmap->data;
04492 pmap->data=NULL;
04493 pmapOverwrite->compute_volume_gradient();
04494 if (verbose) printf("Done!\n");
04495 } else {
04496 if (verbose) printf("Overwriting volume ... \n");
04497 volmapOverwrite->name = target -> name;
04498 volmapOverwrite->xsize = target->xsize;
04499 volmapOverwrite->ysize = target->ysize;
04500 volmapOverwrite->zsize = target->zsize;
04501 volmapOverwrite->data = target->data;
04502 target->data=NULL;
04503 volmapOverwrite->compute_volume_gradient();
04504 if (verbose) printf("Done.\n ");
04505 } 
04506 }
04507 
04508 if ((loadpmap&&process)&&pmap->data!=NULL) {
04509 //Tcl_AppendResult(interp,"Processing probability map with cutoff: ",proCutoff,"...",NULL);
04510 procmap=process_pmap(pmap, proCutoff);
04511 app->molecule_add_volumetric(molid, procmap->name, procmap->origin,
04512 procmap->xaxis, procmap->yaxis, procmap->zaxis,
04513 procmap->xsize, procmap->ysize, procmap->zsize, procmap->data);
04514 procmap->compute_volume_gradient();
04515 procmap->data=NULL;
04516 if (verbose) printf("Loaded processed map\n");
04517 } else if ((loadpmap&&process)&&pmapOverwrite->data!=NULL) {
04518 procmap=process_pmap(pmapOverwrite, proCutoff);
04519 app->molecule_add_volumetric(molid, procmap->name, procmap->origin,
04520 procmap->xaxis, procmap->yaxis, procmap->zaxis,
04521 procmap->xsize, procmap->ysize, procmap->zsize, procmap->data);
04522 procmap->compute_volume_gradient();
04523 procmap->data=NULL;
04524 if (verbose) printf("Loaded processed map (they don't overwrite).\n");
04525 }
04526 
04527 if (count_pmap==1 && pmap->data!=NULL) {
04528 if (loadpmap!=1) {
04529 printf("-count_pmap flag specified without -probmap!\n");
04530 return TCL_ERROR;
04531 } else {
04532 long Pvol=0;
04533 float currProb=0.0f;
04534 Tcl_Obj *analysis=Tcl_NewListObj(0,NULL);
04535 for (int c=0; c<10; c++) {
04536 currProb+=0.1f;
04537 if (c==9) currProb=((float)nrays-((float)nrays*0.01f))/(float)nrays;
04538 Pvol=vol_probability(pmap, currProb, float(gspacing));
04539 Tcl_ListObjAppendElement(interp, analysis, Tcl_NewLongObj(Pvol));
04540 }
04541 Tcl_SetObjResult(interp,analysis);
04542 if (molid != -1 && volid != -1 ) {
04543 target->data = NULL; // prevent destruction of density array;
04544 pmap->data = NULL;
04545 } else {
04546 delete pmapT;
04547 delete qs;
04548 delete volmapA;
04549 } 
04550 
04551 return TCL_OK;
04552 }
04553 } else if (count_pmap==1 && overwrite==1 && pmapOverwrite->data!=NULL) {
04554 long Pvol=0;
04555 float currProb=0.0f;
04556 Tcl_Obj *analysis=Tcl_NewListObj(0,NULL);
04557 for (int c=0; c<10; c++) {
04558 currProb+=0.1f;
04559 if (c==9) currProb=((float)nrays-((float)nrays*0.01f))/(float)nrays;
04560 Pvol=vol_probability(pmapOverwrite, currProb, float(gspacing));
04561 Tcl_ListObjAppendElement(interp, analysis, Tcl_NewLongObj(Pvol));
04562 }
04563 Tcl_SetObjResult(interp,analysis);
04564 if (molid != -1 && volid != -1 ) {
04565 target->data = NULL; // prevent destruction of density array;
04566 pmap->data = NULL;
04567 } else {
04568 delete pmapT;
04569 delete qs;
04570 delete volmapA;
04571 }
04572 
04573 return TCL_OK;
04574 }
04575 
04576 if (molid != -1 && volid != -1 ) {
04577 target->data = NULL; // prevent destruction of density array;
04578 procmap->data=NULL;
04579 pmap->data = NULL;
04580 } else {
04581 delete pmapT;
04582 delete qs;
04583 delete volmapA;
04584 }
04585 
04586 long Ntotal = target->xsize * target->ysize * target->zsize;
04587 //printf("VolIn: %ld external voxels.\n",Nout);
04588 
04589 Tcl_Obj *tcl_result = Tcl_NewListObj(0,NULL);
04590 Tcl_ListObjAppendElement(interp,tcl_result,Tcl_NewLongObj(Ntotal));
04591 Tcl_ListObjAppendElement(interp,tcl_result,Tcl_NewLongObj(Nout));
04592 Tcl_ListObjAppendElement(interp,tcl_result,Tcl_NewLongObj(nIn));
04593 Tcl_ListObjAppendElement(interp,tcl_result,Tcl_NewLongObj(nVoxIso));
04594 Tcl_SetObjResult(interp,tcl_result);
04595 return TCL_OK;
04596 }
04597 
04598 int obj_measure(ClientData cd, Tcl_Interp *interp, int argc,
04599 Tcl_Obj * const objv[]) {
04600 
04601 if (argc < 2) {
04602 Tcl_SetResult(interp, 
04603 (char *) "usage: measure <command> [args...]\n"
04604 "\nMeasure Commands:\n"
04605 " avpos <sel> [first <first>] [last <last>] [step <step>] -- average position\n"
04606 " center <sel> [weight <weights>] -- geometrical (or weighted) center\n"
04607 " centerperresidue <sel> [weight <weights>] -- geometrical center for every residue in sel\n"
04608 " cluster <sel> [num <#clusters>] [distfunc <flag>] [cutoff <cutoff>]\n"
04609 " [first <first>] [last <last>] [step <step>] [selupdate <bool>]\n"
04610 " [weight <weights>]\n"
04611 " -- perform a cluster analysis (cluster similar timesteps)\n"
04612 " clustsize <sel> [cutoff <float>] [minsize <num>] [numshared <num>]\n"
04613 " [usepbc <bool>] [storesize <fieldname>] [storenum <fieldname>]\n"
04614 " -- perform a cluster size analysis (find clusters of atoms)\n"
04615 " contacts <cutoff> <sel1> [<sel2>] -- list contacts\n" 
04616 " dipole <sel> [-elementary|-debye] [-geocenter|-masscenter|-origincenter]\n"
04617 " -- dipole moment\n"
04618 " fit <sel1> <sel2> [weight <weights>] [order <index list>]\n"
04619 " -- transformation matrix from selection 1 to 2\n"
04620 " gofr <sel1> <sel2> [delta <value>] [rmax <value>] [usepbc <bool>]\n"
04621 " [selupdate <bool>] [first <first>] [last <last>] [step <step>]\n"
04622 " -- atomic pair distribution function g(r)\n"
04623 " hbonds <cutoff> <angle> <sel1> [<sel2>]\n"
04624 " -- list donors, acceptors, hydrogens involved in hydrogen bonds\n"
04625 " inverse <matrix> -- inverse matrix\n"
04626 " inertia <sel> [-moments] [-eigenvals] -- COM and principle axes of inertia\n"
04627 " minmax <sel> [-withradii] -- bounding box\n"
04628 " rgyr <sel> [weight <weights>] -- radius of gyration\n"
04629 " rmsd <sel1> <sel2> [weight <weights>] -- RMS deviation\n"
04630 " rmsdperresidue <sel1> <sel2> [weight <weights>] -- deviation per residue\n"
04631 " rmsf <sel> [first <first>] [last <last>] [step <step>] -- RMS fluctuation\n"
04632 " rmsfperresidue <sel> [first <first>] [last <last>] [step <step>] -- fluctuation per residue\n"
04633 " sasa <srad> <sel> [-points <varname>] [-restrict <restrictedsel>]\n"
04634 " [-samples <numsamples>] -- solvent-accessible surface area\n"
04635 " sasaperresidue <srad> <sel> [-points <varname>] [-restrict <restrictedsel>]\n"
04636 " [-samples <numsamples>] -- solvent-accessible surface area per residue\n"
04637 " sumweights <sel> weight <weights> -- sum of selected weights\n"
04638 " bond {{<atomid1> [<molid1>]} {<atomid2> [<molid2>]}}\n"
04639 " [molid <default mol>] [frame <frame|all|last> | first <first> last <last>]\n"
04640 " -- bond length between atoms 1 and 2\n"
04641 " angle {{<atomid1> [<molid1>]} {<atomid2> [<molid2>]} {<atomid3> [<molid3>]}}\n"
04642 " [molid <default mol>] [frame <frame|all|last> | first <first> last <last>]\n"
04643 " -- angle between atoms 1-3\n"
04644 " dihed {{<atomid1> [<molid1>]} ... {<atomid4> [<molid4>]}}\n"
04645 " [molid <default mol>] [frame <frame|all|last> | first <first> last <last>]\n"
04646 " -- dihedral angle between atoms 1-4\n"
04647 " imprp {{<atomid1> [<molid1>]} ... {<atomid4> [<molid4>]}}\n"
04648 " [molid <default mol>] [frame <frame|all|last> | first <first> last <last>]\n"
04649 " -- improper angle between atoms 1-4\n"
04650 // FIXME: Complete 'measure energy' usage info here?
04651 " energy bond|angle|dihed|impr|vdw|elec -- compute energy\n"
04652 " surface <sel> <gridsize> <radius> <thickness> -- surface of selection\n"
04653 " pbc2onc <center> [molid <default>] [frame <frame|last>]\n"
04654 " -- transformation matrix to wrap a nonorthogonal PBC unit cell\n"
04655 " into an orthonormal cell\n"
04656 " pbcneighbors <center> <cutoff> [sel <sel>] [align <matrix>] [molid <default>]\n"
04657 " [frame <frame|last>] [boundingbox <PBC|{<mincoord> <maxcoord>}>]\n"
04658 " -- all image atoms that are within cutoff Angstrom of the pbc unit cell\n"
04659 " symmetry <sel> [element [<vector>]] [-tol <value>] [-nobonds] [-verbose <level>]\n"
04660 " transoverlap <sel> <matrix> [-sigma <value>]\n"
04661 " -- overlap of a structure with a transformed copy of itself\n"
04662 " volinterior <sel> [options]\n"
04663 " -- type 'measure volinterior' for usage\n",
04664 TCL_STATIC);
04665 return TCL_ERROR;
04666 }
04667 VMDApp *app = (VMDApp *)cd;
04668 char *argv1 = Tcl_GetStringFromObj(objv[1],NULL);
04669 if (!strupncmp(argv1, "avpos", CMDLEN))
04670 return vmd_measure_avpos(app, argc-1, objv+1, interp);
04671 else if (!strupncmp(argv1, "center", CMDLEN))
04672 return vmd_measure_center(app, argc-1, objv+1, interp);
04673 #if 1
04674 // XXX in test
04675 else if (!strupncmp(argv1, "centerperresidue", CMDLEN))
04676 return vmd_measure_centerperresidue(app, argc-1, objv+1, interp);
04677 #endif
04678 else if (!strupncmp(argv1, "cluster", CMDLEN))
04679 return vmd_measure_cluster(app, argc-1, objv+1, interp);
04680 else if (!strupncmp(argv1, "clustsize", CMDLEN))
04681 return vmd_measure_clustsize(app, argc-1, objv+1, interp);
04682 else if (!strupncmp(argv1, "contacts", CMDLEN))
04683 return vmd_measure_contacts(app, argc-1, objv+1, interp);
04684 else if (!strupncmp(argv1, "dipole", CMDLEN))
04685 return vmd_measure_dipole(app, argc-1, objv+1, interp);
04686 else if (!strupncmp(argv1, "fit", CMDLEN)) 
04687 return vmd_measure_fit(app, argc-1, objv+1, interp);
04688 else if (!strupncmp(argv1, "minmax", CMDLEN))
04689 return vmd_measure_minmax(app, argc-1, objv+1, interp);
04690 else if (!strupncmp(argv1, "gofr", CMDLEN))
04691 return vmd_measure_gofr(app, argc-1, objv+1, interp);
04692 else if (!strupncmp(argv1, "rdf", CMDLEN))
04693 return vmd_measure_rdf(app, argc-1, objv+1, interp);
04694 else if (!strupncmp(argv1, "hbonds", CMDLEN))
04695 return vmd_measure_hbonds(app, argc-1, objv+1, interp);
04696 else if (!strupncmp(argv1, "inverse", CMDLEN)) 
04697 return vmd_measure_inverse(argc-1, objv+1, interp);
04698 else if (!strupncmp(argv1, "inertia", CMDLEN)) 
04699 return vmd_measure_inertia(app, argc-1, objv+1, interp);
04700 else if (!strupncmp(argv1, "rgyr", CMDLEN))
04701 return vmd_measure_rgyr(app, argc-1, objv+1, interp);
04702 else if (!strupncmp(argv1, "rmsd", CMDLEN))
04703 return vmd_measure_rmsd(app, argc-1, objv+1, interp);
04704 #if 1
04705 // XXX in test
04706 else if (!strupncmp(argv1, "rmsdperresidue", CMDLEN))
04707 return vmd_measure_rmsdperresidue(app, argc-1, objv+1, interp);
04708 #endif
04709 #if 1
04710 else if (!strupncmp(argv1, "rmsd_qcp", CMDLEN))
04711 return vmd_measure_rmsd_qcp(app, argc-1, objv+1, interp);
04712 else if (!strupncmp(argv1, "rmsdmat_qcp", CMDLEN))
04713 return vmd_measure_rmsdmat_qcp(app, argc-1, objv+1, interp);
04714 else if (!strupncmp(argv1, "rmsdmat_qcp_ooc", CMDLEN))
04715 return vmd_measure_rmsdmat_qcp_ooc(app, argc-1, objv+1, interp);
04716 #endif
04717 else if (!strupncmp(argv1, "rmsf", CMDLEN))
04718 return vmd_measure_rmsf(app, argc-1, objv+1, interp);
04719 #if 1
04720 // XXX in test
04721 else if (!strupncmp(argv1, "rmsfperresidue", CMDLEN))
04722 return vmd_measure_rmsfperresidue(app, argc-1, objv+1, interp);
04723 #endif
04724 else if (!strupncmp(argv1, "sasa", CMDLEN))
04725 return vmd_measure_sasa(app, argc-1, objv+1, interp);
04726 else if (!strupncmp(argv1, "sasaperresidue", CMDLEN))
04727 return vmd_measure_sasaperresidue(app, argc-1, objv+1, interp);
04728 #if 1
04729 else if (!strupncmp(argv1, "sasalist", CMDLEN))
04730 return vmd_measure_sasalist(app, argc-1, objv+1, interp);
04731 #endif
04732 else if (!strupncmp(argv1, "sumweights", CMDLEN))
04733 return vmd_measure_sumweights(app, argc-1, objv+1, interp);
04734 else if (!strupncmp(argv1, "imprp", CMDLEN))
04735 return vmd_measure_dihed(app, argc-1, objv+1, interp);
04736 else if (!strupncmp(argv1, "dihed", CMDLEN))
04737 return vmd_measure_dihed(app, argc-1, objv+1, interp);
04738 else if (!strupncmp(argv1, "angle", CMDLEN))
04739 return vmd_measure_angle(app, argc-1, objv+1, interp);
04740 else if (!strupncmp(argv1, "bond", CMDLEN))
04741 return vmd_measure_bond(app, argc-1, objv+1, interp);
04742 else if (!strupncmp(argv1, "energy", CMDLEN))
04743 return vmd_measure_energy(app, argc-1, objv+1, interp);
04744 else if (!strupncmp(argv1, "pbc2onc", CMDLEN))
04745 return vmd_measure_pbc2onc_transform(app, argc-1, objv+1, interp);
04746 else if (!strupncmp(argv1, "pbcneighbors", CMDLEN))
04747 return vmd_measure_pbc_neighbors(app, argc-1, objv+1, interp);
04748 else if (!strupncmp(argv1, "surface", CMDLEN))
04749 return vmd_measure_surface(app, argc-1, objv+1, interp);
04750 else if (!strupncmp(argv1, "transoverlap", CMDLEN))
04751 return vmd_measure_trans_overlap(app, argc-1, objv+1, interp);
04752 else if (!strupncmp(argv1, "symmetry", CMDLEN))
04753 return vmd_measure_symmetry(app, argc-1, objv+1, interp);
04754 else if (!strupncmp(argv1, "volinterior", CMDLEN))
04755 return vmd_measure_volinterior(app, argc-1, objv+1, interp);
04756 
04757 Tcl_SetResult(interp, (char *) "Type 'measure' for summary of usage\n", TCL_VOLATILE);
04758 return TCL_OK;
04759 }
04760 
04761 
04762 

Generated on Mon Nov 17 02:47:15 2025 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002

AltStyle によって変換されたページ (->オリジナル) /