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: py_imd.C,v $ 00013 * $Author: dgomes $ $Locker: $ $State: Exp $ 00014 * $Revision: 1.30 $ $Date: 2024年04月26日 15:46:00 $ 00015 * 00016 *************************************************************************** 00017 * DESCRIPTION: 00018 * Python IMD interface. 00019 ***************************************************************************/ 00020 00021 #include "py_commands.h" 00022 00023 #ifdef VMDIMD 00024 00025 #include "CmdIMD.h" 00026 #include "CommandQueue.h" 00027 #include "VMDApp.h" 00028 #include "MoleculeList.h" 00029 #include "IMDMgr.h" 00030 00031 static const char connect_doc[] = 00032 "Connect to an IMD server\n\n" 00033 "Args:\n" 00034 " host (str): Server hostname\n" 00035 " port (int): Port running IMD server"; 00036 static PyObject* py_imdconnect(PyObject *self, PyObject *args, PyObject *kwargs) { 00037 const char *kwlist[] = {"host", "port", NULL}; 00038 char *host; 00039 int port; 00040 00041 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "si:imd.connect", 00042 (char**) kwlist, &host, &port)) 00043 return NULL; 00044 00045 VMDApp *app; 00046 if (!(app = get_vmdapp())) 00047 return NULL; 00048 00049 Molecule *mol = app->moleculeList->top(); 00050 if (!mol) { 00051 PyErr_SetString(PyExc_ValueError, "No molecule loaded"); 00052 return NULL; 00053 } 00054 00055 if (app->imdMgr->connected()) { 00056 PyErr_SetString(PyExc_ValueError, "Can't create new IMD connection: " 00057 "already connected."); 00058 return NULL; 00059 } 00060 00061 if (!app->imd_connect(mol->id(), host, port)) { 00062 PyErr_Format(PyExc_RuntimeError, "Unable to connect to IMD server '%s:%d'", 00063 host, port); 00064 return NULL; 00065 } 00066 00067 Py_INCREF(Py_None); 00068 return Py_None; 00069 } 00070 00071 00072 static const char pause_doc[] = 00073 "Pause a running IMD simulation"; 00074 static PyObject* py_imdpause(PyObject *self, PyObject *args) { 00075 VMDApp *app; 00076 if (!(app = get_vmdapp())) 00077 return NULL; 00078 00079 if (!app->imdMgr->connected()) { 00080 PyErr_SetString(PyExc_ValueError, "Not connected to an IMD server"); 00081 return NULL; 00082 } 00083 00084 app->imdMgr->togglepause(); 00085 app->commandQueue->runcommand(new CmdIMDSim(CmdIMDSim::PAUSE_TOGGLE)); 00086 00087 Py_INCREF(Py_None); 00088 return Py_None; 00089 } 00090 00091 00092 static const char detach_doc[] = 00093 "Detach from a running IMD simulation"; 00094 static PyObject *py_imddetach(PyObject *self, PyObject *args) { 00095 VMDApp *app; 00096 if (!(app = get_vmdapp())) 00097 return NULL; 00098 00099 if (!app->imdMgr->connected()) { 00100 PyErr_SetString(PyExc_ValueError, "Not connected to an IMD server"); 00101 return NULL; 00102 } 00103 00104 app->imdMgr->detach(); 00105 app->commandQueue->runcommand(new CmdIMDSim(CmdIMDSim::DETACH)); 00106 00107 Py_INCREF(Py_None); 00108 return Py_None; 00109 } 00110 00111 00112 static const char kill_doc[] = 00113 "Halt a running IMD simulation. Also detaches."; 00114 static PyObject* py_imdkill(PyObject *self, PyObject *args) { 00115 VMDApp *app; 00116 if (!(app = get_vmdapp())) 00117 return NULL; 00118 00119 if (!app->imdMgr->connected()) { 00120 PyErr_SetString(PyExc_ValueError, "Not connected to an IMD server"); 00121 return NULL; 00122 } 00123 00124 app->imdMgr->kill(); 00125 app->commandQueue->runcommand(new CmdIMDSim(CmdIMDSim::KILL)); 00126 00127 Py_INCREF(Py_None); 00128 return Py_None; 00129 } 00130 00131 00132 static const char transfer_doc[] = 00133 "Get and/or set how often timesteps are sent to the IMD server\n\n" 00134 "Args:\n" 00135 " rate (int): New transfer rate, or None to query. Rate must be greater\n" 00136 " than 0\n" 00137 "Returns:\n" 00138 " (int): Updated transfer rate"; 00139 static PyObject* py_imdtransfer(PyObject *self, PyObject *args, 00140 PyObject *kwargs) { 00141 const char *kwlist[] = {"rate", NULL}; 00142 PyObject *rateobj = NULL; 00143 int rate; 00144 00145 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:imd.transfer", 00146 (char**) kwlist, &rateobj)) 00147 return NULL; 00148 00149 VMDApp *app; 00150 if (!(app = get_vmdapp())) 00151 return NULL; 00152 00153 if (rateobj) { 00154 rate = as_int(rateobj); 00155 00156 if (rate <= 0) { 00157 PyErr_SetString(PyExc_ValueError, "transfer rate must be > 0"); 00158 return NULL; 00159 } 00160 00161 app->imdMgr->set_trans_rate(rate); 00162 app->commandQueue->runcommand(new CmdIMDRate(CmdIMDRate::TRANSFER, rate)); 00163 } 00164 00165 return as_pyint(app->imdMgr->get_trans_rate()); 00166 } 00167 00168 00169 static const char keep_doc[] = 00170 "Get and/or set how often timesteps are saved.\n\n" 00171 "Args:\n" 00172 " rate (int): Save frequency, or None to query. Rate must be greater than\n" 00173 " or equal to 0\n" 00174 "Returns:\n" 00175 " (int): Updated save frequency"; 00176 static PyObject* py_imdkeep(PyObject *self, PyObject *args, PyObject *kwargs) { 00177 const char *kwlist[] = {"rate", NULL}; 00178 PyObject *rateobj = NULL; 00179 int rate; 00180 00181 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:imd.keep", 00182 (char**) kwlist, &rateobj)) 00183 return NULL; 00184 00185 VMDApp *app; 00186 if (!(app = get_vmdapp())) 00187 return NULL; 00188 00189 if (rateobj) { 00190 rate = as_int(rateobj); 00191 00192 if (rate < 0) { 00193 PyErr_SetString(PyExc_ValueError, "keep value must be >= 0"); 00194 return NULL; 00195 } 00196 00197 app->imdMgr->set_keep_rate(rate); 00198 app->commandQueue->runcommand(new CmdIMDRate(CmdIMDRate::KEEP, rate)); 00199 } 00200 00201 return as_pyint(app->imdMgr->get_keep_rate()); 00202 } 00203 00204 00205 static const char copy_doc[] = 00206 "Set if unit cell information should be copied from previous frame\n\n" 00207 "Args:\n" 00208 " copy (bool): If cell information should be copied"; 00209 static PyObject* py_copyunitcell(PyObject *self, PyObject *args, 00210 PyObject *kwargs) { 00211 const char *kwlist[] = {"copy", NULL}; 00212 CmdIMDCopyUnitCell::CmdIMDCopyUnitCellCommand c; 00213 int copy; 00214 00215 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&:imd.copyunitcell", 00216 (char**) kwlist, convert_bool, ©)) 00217 return NULL; 00218 00219 VMDApp *app; 00220 if (!(app = get_vmdapp())) 00221 return NULL; 00222 00223 app->imdMgr->set_copyunitcell(copy); 00224 c = copy ? CmdIMDCopyUnitCell::COPYCELL_ON : CmdIMDCopyUnitCell::COPYCELL_OFF; 00225 app->commandQueue->runcommand(new CmdIMDCopyUnitCell(c)); 00226 00227 Py_INCREF(Py_None); 00228 return Py_None; 00229 } 00230 00231 00232 static const char connected_doc[] = 00233 "Query if an IMD connection exists\n\n" 00234 "Returns:\n" 00235 " (bool): True if a connection exists, False otherwise"; 00236 static PyObject* py_imdconnected(PyObject *self, PyObject *args) { 00237 VMDApp *app; 00238 if (!(app = get_vmdapp())) 00239 return NULL; 00240 00241 PyObject *result = app->imdMgr->connected() ? Py_True : Py_False; 00242 Py_INCREF(result); 00243 return result; 00244 } 00245 00246 static const char sendforces_doc[] = 00247 "Send forces to connected MD engine\n\n" 00248 "Args:\n" 00249 " indices (list of ints): Atomic indices to which forces will be applied\n" 00250 " forces (flat nx3 list): forces that will be applied to the individual atoms\n"; 00251 static PyObject* py_sendforces(PyObject *self, PyObject *args, 00252 PyObject *kwargs) { 00253 VMDApp *app; 00254 if (!(app = get_vmdapp())) 00255 return NULL; 00256 if (! app->imdMgr->connected()) { 00257 PyErr_SetString(PyExc_ValueError, "Not connected to an IMD server"); 00258 return NULL; 00259 } 00260 const char *kwlist[] = {"indices", "forces", NULL}; 00261 PyObject *idxs, *idxseq; 00262 PyObject *forces, *forceseq; 00263 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO:imd.sendforces", 00264 (char**) kwlist, &idxs, &forces)) 00265 return NULL; 00266 int n; 00267 if (!(idxseq = PySequence_Fast(idxs, "indices must be a python sequence"))) { 00268 Py_XDECREF(idxseq); 00269 return NULL; 00270 } 00271 if (!(forceseq = PySequence_Fast(forces, "forces must be a python sequence"))) { 00272 Py_XDECREF(idxseq); 00273 Py_XDECREF(forceseq); 00274 return NULL; 00275 } 00276 n = PySequence_Fast_GET_SIZE(idxseq); 00277 if (PySequence_Fast_GET_SIZE(forceseq) != 3 * n) { 00278 Py_XDECREF(idxseq); 00279 Py_XDECREF(forceseq); 00280 PyErr_SetString(PyExc_ValueError, "The force list passed is not 3 times as long as the index list."); 00281 return NULL; 00282 } 00283 int *cidxs = new int[n]; 00284 float *cforces = new float[3*n]; 00285 for (int i = 0; i < n; i++) { 00286 int result = (int) PyLong_AsLong(PySequence_Fast_GET_ITEM(idxseq,i)); 00287 if (result < 0) { 00288 Py_XDECREF(idxseq); 00289 Py_XDECREF(forceseq); 00290 PyErr_SetString(PyExc_ValueError, "A value in the index list is not a positive integer"); 00291 return NULL; 00292 } 00293 cidxs[i] = result; 00294 } 00295 for (int i = 0; i < 3*n; i++) { 00296 cforces[i] = (float) PyFloat_AS_DOUBLE(PySequence_Fast_GET_ITEM(forceseq,i)); 00297 } 00298 app->imd_sendforces(n, cidxs, cforces); 00299 delete cidxs; 00300 delete cforces; 00301 Py_XDECREF(idxseq); 00302 Py_XDECREF(forceseq); 00303 Py_INCREF(Py_None); 00304 return Py_None; 00305 } 00306 00307 static PyMethodDef methods[] = { 00308 {"connected", (PyCFunction)py_imdconnected, METH_NOARGS, connected_doc}, 00309 {"connect", (PyCFunction)py_imdconnect, METH_VARARGS | METH_KEYWORDS, connect_doc}, 00310 {"pause", (PyCFunction)py_imdpause, METH_NOARGS, pause_doc}, 00311 {"detach", (PyCFunction)py_imddetach, METH_NOARGS, detach_doc}, 00312 {"kill", (PyCFunction)py_imdkill, METH_NOARGS, kill_doc}, 00313 {"transfer", (PyCFunction)py_imdtransfer, METH_VARARGS | METH_KEYWORDS, transfer_doc}, 00314 {"keep", (PyCFunction)py_imdkeep, METH_VARARGS | METH_KEYWORDS, keep_doc}, 00315 {"copyunitcell", (PyCFunction)py_copyunitcell, METH_VARARGS | METH_KEYWORDS, copy_doc}, 00316 {"sendforces", (PyCFunction)py_sendforces, METH_VARARGS | METH_KEYWORDS, sendforces_doc}, 00317 {NULL, NULL} 00318 }; 00319 00320 00321 static const char imd_moddoc[] = 00322 "Methods for controlling interactive molecular dynamics simulations"; 00323 00324 00325 #if PY_MAJOR_VERSION >= 3 00326 static struct PyModuleDef imddef = { 00327 PyModuleDef_HEAD_INIT, 00328 "imd", 00329 imd_moddoc, 00330 -1, 00331 methods, 00332 }; 00333 #endif 00334 00335 00336 PyObject* initimd() { 00337 #if PY_MAJOR_VERSION >= 3 00338 PyObject *m = PyModule_Create(&imddef); 00339 #else 00340 PyObject *m = Py_InitModule3("imd", methods, imd_moddoc); 00341 #endif 00342 return m; 00343 } 00344 00345 #endif 00346