1 /*
2 * This file is part of the GROMACS molecular simulation package.
3 *
4 * Copyright (c) 2009-2018, The GROMACS development team.
5 * Copyright (c) 2019,2021, by the GROMACS development team, led by
6 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7 * and including many others, as listed in the AUTHORS file in the
8 * top-level source directory and at http://www.gromacs.org.
9 *
10 * GROMACS is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2.1
13 * of the License, or (at your option) any later version.
14 *
15 * GROMACS is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with GROMACS; if not, see
22 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 *
25 * If you want to redistribute modifications to GROMACS, please
26 * consider that scientific software is very special. Version
27 * control is crucial - bugs must be traceable. We will be happy to
28 * consider code for inclusion in the official distribution, but
29 * derived work must not be called official GROMACS. Details are found
30 * in the README & COPYING files - if they are missing, get the
31 * official version at http://www.gromacs.org.
32 *
33 * To help us fund GROMACS development, we humbly ask that you cite
34 * the research papers on the package. Check out http://www.gromacs.org.
35 */
36 #include "gmxpre.h"
37
38 #include "vmdio.h"
39
40 #include "config.h"
41
42 #include "gromacs/utility/path.h"
43 #include "gromacs/utility/stringutil.h"
44
45 /* Derived from PluginMgr.C and catdcd.c */
46
47 /* PluginMgr.C: Copyright: */
48 /***************************************************************************
49 * cr
50 * cr (C) Copyright 1995-2009 The Board of Trustees of the
51 * cr University of Illinois
52 * cr All Rights Reserved
53 * cr
54 Developed by: Theoretical and Computational Biophysics Group
55 University of Illinois at Urbana-Champaign
56 http://www.ks.uiuc.edu/
57
58 Permission is hereby granted, free of charge, to any person obtaining a copy of
59 this software and associated documentation files (the Software), to deal with
60 the Software without restriction, including without limitation the rights to
61 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
62 of the Software, and to permit persons to whom the Software is furnished to
63 do so, subject to the following conditions:
64
65 Redistributions of source code must retain the above copyright notice,
66 this list of conditions and the following disclaimers.
67
68 Redistributions in binary form must reproduce the above copyright notice,
69 this list of conditions and the following disclaimers in the documentation
70 and/or other materials provided with the distribution.
71
72 Neither the names of Theoretical and Computational Biophysics Group,
73 University of Illinois at Urbana-Champaign, nor the names of its contributors
74 may be used to endorse or promote products derived from this Software without
75 specific prior written permission.
76
77 THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
78 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
79 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
80 THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
81 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
82 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
83 OTHER DEALINGS WITH THE SOFTWARE.
84 ***************************************************************************/
85
86 /* catdcd.c: Copyright: */
87 /*****************************************************************************/
88 /* */
89 /* (C) Copyright 2001-2005 Justin Gullingsrud and the University of Illinois.*/
90 /* */
91 /*****************************************************************************/
92
93 #include <cassert>
94 #include <cstdio>
95 #include <cstdlib>
96 #include <cstring>
97
98 /*
99 * Plugin header files; get plugin source from www.ks.uiuc.edu/Research/vmd"
100 */
101 #include "external/vmd_molfile/molfile_plugin.h"
102 #include "external/vmd_molfile/vmddlopen.h"
103 #if !GMX_NATIVE_WINDOWS
104 # include <glob.h>
105 #else
106 # ifndef _WIN32_IE
107 # define _WIN32_IE 0x0500 /* SHGetFolderPath is available since WinXP/IE5 */
108 # endif
109 # include <shlobj.h>
110 # include <windows.h>
111 #endif
112
113 #include "gromacs/fileio/gmxfio.h"
114 #include "gromacs/math/vec.h"
115 #include "gromacs/pbcutil/pbc.h"
116 #include "gromacs/trajectory/trajectoryframe.h"
117 #include "gromacs/utility/basedefinitions.h"
118 #include "gromacs/utility/fatalerror.h"
119 #include "gromacs/utility/futil.h"
120 #include "gromacs/utility/smalloc.h"
121
122
123 typedef int (*initfunc)();
124 typedef int (*regfunc)(void*, vmdplugin_register_cb);
125 typedef int (*finifunc)();
126
127
register_cb(void * v,vmdplugin_t * p)128 static int register_cb(void* v, vmdplugin_t* p)
129 {
130 const char* key = p->name;
131 gmx_vmdplugin_t* vmdplugin = static_cast<gmx_vmdplugin_t*>(v);
132
133 if (strcmp(key, vmdplugin->filetype) == 0)
134 {
135 vmdplugin->api = reinterpret_cast<molfile_plugin_t*>(p);
136 }
137 return VMDPLUGIN_SUCCESS;
138 }
139
load_sharedlibrary_plugins(const char * fullpath,gmx_vmdplugin_t * vmdplugin)140 static int load_sharedlibrary_plugins(const char* fullpath, gmx_vmdplugin_t* vmdplugin)
141 {
142 /* Open the dll; try to execute the init function. */
143 void *handle, *ifunc, *registerfunc;
144 handle = vmddlopen(fullpath);
145 if (!handle)
146 {
147 if (debug)
148 {
149 fprintf(debug, "\nUnable to open dynamic library %s.\n%s\n", fullpath,
150 vmddlerror()); /*only to debug because of stdc++ erros */
151 }
152 return 0;
153 }
154
155 ifunc = vmddlsym(handle, "vmdplugin_init");
156 if (!ifunc || (reinterpret_cast<initfunc>(ifunc))())
157 {
158 printf("\nvmdplugin_init() for %s returned an error; plugin(s) not loaded.\n", fullpath);
159 vmddlclose(handle);
160 return 0;
161 }
162
163 registerfunc = vmddlsym(handle, "vmdplugin_register");
164 if (!registerfunc)
165 {
166 printf("\nDidn't find the register function in %s; plugin(s) not loaded.\n", fullpath);
167 vmddlclose(handle);
168 return 0;
169 }
170 else
171 {
172 /* Load plugins from the library.*/
173 (reinterpret_cast<regfunc>(registerfunc))(vmdplugin, register_cb);
174 }
175
176 /* in case this library does not support the filetype, close it */
177 if (vmdplugin->api == nullptr)
178 {
179 vmddlclose(handle);
180 }
181
182 return 1;
183 }
184
185 /*return: 1: success, 0: last frame, -1: error*/
read_next_vmd_frame(gmx_vmdplugin_t * vmdplugin,t_trxframe * fr)186 gmx_bool read_next_vmd_frame(gmx_vmdplugin_t* vmdplugin, t_trxframe* fr)
187 {
188 int rc, i;
189 rvec vec, angle;
190 molfile_timestep_t ts;
191
192
193 fr->bV = vmdplugin->bV;
194
195 #if GMX_DOUBLE
196 snew(ts.coords, fr->natoms * 3);
197 if (fr->bV)
198 {
199 snew(ts.velocities, fr->natoms * 3);
200 }
201 #else
202 ts.coords = reinterpret_cast<float*>(fr->x);
203 if (fr->bV)
204 {
205 ts.velocities = reinterpret_cast<float*>(fr->v);
206 }
207 #endif
208
209 rc = vmdplugin->api->read_next_timestep(vmdplugin->handle, fr->natoms, &ts);
210
211 if (rc < -1)
212 {
213 fprintf(stderr, "\nError reading input file (error code %d)\n", rc);
214 }
215 if (rc < 0)
216 {
217 vmdplugin->api->close_file_read(vmdplugin->handle);
218 return false;
219 }
220
221 #if GMX_DOUBLE
222 for (i = 0; i < fr->natoms; i++)
223 {
224 fr->x[i][0] = .1 * ts.coords[i * 3];
225 fr->x[i][1] = .1 * ts.coords[i * 3 + 1];
226 fr->x[i][2] = .1 * ts.coords[i * 3 + 2];
227 if (fr->bV)
228 {
229 fr->v[i][0] = .1 * ts.velocities[i * 3];
230 fr->v[i][1] = .1 * ts.velocities[i * 3 + 1];
231 fr->v[i][2] = .1 * ts.velocities[i * 3 + 2];
232 }
233 }
234 sfree(ts.coords);
235 if (fr->bV)
236 {
237 sfree(ts.velocities);
238 }
239 #else
240 for (i = 0; i < fr->natoms; i++)
241 {
242 svmul(.1, fr->x[i], fr->x[i]);
243 if (fr->bV)
244 {
245 svmul(.1, fr->v[i], fr->v[i]);
246 }
247 }
248 #endif
249
250 fr->bX = true;
251 fr->bBox = true;
252 vec[0] = .1 * ts.A;
253 vec[1] = .1 * ts.B;
254 vec[2] = .1 * ts.C;
255 angle[0] = ts.alpha;
256 angle[1] = ts.beta;
257 angle[2] = ts.gamma;
258 matrix_convert(fr->box, vec, angle);
259 if (vmdplugin->api->abiversion > 10)
260 {
261 fr->bTime = TRUE;
262 fr->time = ts.physical_time;
263 }
264 else
265 {
266 fr->bTime = FALSE;
267 }
268
269
270 return true;
271 }
272
load_vmd_library(const char * fn,gmx_vmdplugin_t * vmdplugin)273 static int load_vmd_library(const char* fn, gmx_vmdplugin_t* vmdplugin)
274 {
275 const char* err;
276 int ret = 0;
277 #if !GMX_NATIVE_WINDOWS
278 glob_t globbuf;
279 const std::string defpath_suffix = "/plugins/*/molfile";
280 const std::string defpathenv = GMX_VMD_PLUGIN_PATH;
281 #else
282 WIN32_FIND_DATA ffd;
283 HANDLE hFind = INVALID_HANDLE_VALUE;
284 char progfolder[GMX_PATH_MAX];
285 std::string defpath_suffix = "\\plugins\\WIN32\\molfile";
286 SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, progfolder);
287 std::string defpathenv =
288 gmx::formatString("%s\\University of Illinois\\VMD\\plugins\\WIN32\\molfile", progfolder);
289 #endif
290
291 vmdplugin->api = nullptr;
292 vmdplugin->filetype = strrchr(fn, '.');
293 if (!vmdplugin->filetype)
294 {
295 return 0;
296 }
297 vmdplugin->filetype++;
298
299 /* First look for an explicit path given at run time for the
300 * plugins, then an implicit run-time path, and finally for one
301 * given at configure time. This last might be hard-coded to the
302 * default for VMD installs. */
303 const char* pathenv = getenv("VMD_PLUGIN_PATH");
304 std::string fallBackPathEnv;
305 if (!pathenv)
306 {
307 pathenv = getenv("VMDDIR");
308 if (!pathenv)
309 {
310 printf("\nNeither VMD_PLUGIN_PATH or VMDDIR set. ");
311 printf("Using default location:\n%s\n", defpathenv.c_str());
312 pathenv = defpathenv.c_str();
313 }
314 else
315 {
316 printf("\nVMD_PLUGIN_PATH no set, but VMDDIR is set. ");
317 fallBackPathEnv = gmx::Path::join(pathenv, defpath_suffix);
318 pathenv = fallBackPathEnv.c_str();
319 printf("Using semi-default location:\n%s\n", pathenv);
320 }
321 }
322 #if !GMX_NATIVE_WINDOWS
323 std::string pathname = gmx::Path::join(pathenv, "/*.so");
324 glob(pathname.c_str(), 0, nullptr, &globbuf);
325 if (globbuf.gl_pathc == 0)
326 {
327 printf("\nNo VMD Plugins found\n"
328 "Set the environment variable VMD_PLUGIN_PATH to the molfile folder within the\n"
329 "VMD installation.\n"
330 "The architecture (e.g. 32bit versus 64bit) of GROMACS and VMD has to match.\n");
331 return 0;
332 }
333 for (size_t i = 0; i < globbuf.gl_pathc && vmdplugin->api == nullptr; i++)
334 {
335 /* FIXME: Undefined which plugin is chosen if more than one plugin
336 can read a certain file ending. Requires some additional command
337 line option or enviroment variable to specify which plugin should
338 be picked.
339 */
340 ret |= load_sharedlibrary_plugins(globbuf.gl_pathv[i], vmdplugin);
341 }
342 globfree(&globbuf);
343 #else
344 std::string pathname = gmx::Path::join(pathenv, "\\*.so");
345 hFind = FindFirstFile(pathname.c_str(), &ffd);
346 if (INVALID_HANDLE_VALUE == hFind)
347 {
348 printf("\nNo VMD Plugins found\n");
349 return 0;
350 }
351 do
352 {
353 std::string filename = gmx::Path::join(pathenv, ffd.cFileName);
354 ret |= load_sharedlibrary_plugins(filename.c_str(), vmdplugin);
355 } while (FindNextFile(hFind, &ffd) != 0 && vmdplugin->api == NULL);
356 FindClose(hFind);
357 #endif
358
359 if (!ret)
360 {
361 printf("\nCould not open any VMD library.\n");
362 err = vmddlerror();
363 if (!err)
364 {
365 printf("Compiled with dlopen?\n");
366 }
367 else
368 {
369 printf("Last error:\n%s\n", err);
370 }
371 return 0;
372 }
373
374 if (vmdplugin->api == nullptr)
375 {
376 printf("\nNo plugin for %s found\n", vmdplugin->filetype);
377 return 0;
378 }
379
380 if (vmdplugin->api->abiversion < 10)
381 {
382 printf("\nPlugin and/or VMD is too old. At least VMD 1.8.6 is required.\n");
383 return 0;
384 }
385
386 printf("\nUsing VMD plugin: %s (%s)\n", vmdplugin->api->name, vmdplugin->api->prettyname);
387
388 return 1;
389 }
390
read_first_vmd_frame(const char * fn,gmx_vmdplugin_t ** vmdpluginp,t_trxframe * fr)391 int read_first_vmd_frame(const char* fn, gmx_vmdplugin_t** vmdpluginp, t_trxframe* fr)
392 {
393 molfile_timestep_metadata_t* metadata = nullptr;
394 gmx_vmdplugin_t* vmdplugin;
395
396 snew(vmdplugin, 1);
397 *vmdpluginp = vmdplugin;
398 if (!load_vmd_library(fn, vmdplugin))
399 {
400 return 0;
401 }
402
403 vmdplugin->handle = vmdplugin->api->open_file_read(fn, vmdplugin->filetype, &fr->natoms);
404
405 if (!vmdplugin->handle)
406 {
407 fprintf(stderr, "\nError: could not open file '%s' for reading.\n", fn);
408 return 0;
409 }
410
411 if (fr->natoms == MOLFILE_NUMATOMS_UNKNOWN)
412 {
413 fprintf(stderr, "\nFormat of file %s does not record number of atoms.\n", fn);
414 return 0;
415 }
416 else if (fr->natoms == MOLFILE_NUMATOMS_NONE)
417 {
418 fprintf(stderr, "\nNo atoms found by VMD plugin in file %s.\n", fn);
419 return 0;
420 }
421 else if (fr->natoms < 1) /*should not be reached*/
422 {
423 fprintf(stderr, "\nUnknown number of atoms %d for VMD plugin opening file %s.\n", fr->natoms, fn);
424 return 0;
425 }
426
427 snew(fr->x, fr->natoms);
428
429 vmdplugin->bV = false;
430 if (vmdplugin->api->abiversion > 10 && vmdplugin->api->read_timestep_metadata)
431 {
432 vmdplugin->api->read_timestep_metadata(vmdplugin->handle, metadata);
433 assert(metadata);
434 vmdplugin->bV = (metadata->has_velocities != 0);
435 if (vmdplugin->bV)
436 {
437 snew(fr->v, fr->natoms);
438 }
439 }
440 else
441 {
442 fprintf(stderr,
443 "\nThis trajectory is being read with a VMD plug-in from before VMD"
444 "\nversion 1.8, or from a trajectory that lacks time step metadata."
445 "\nEither way, GROMACS cannot tell whether the trajectory has velocities.\n");
446 }
447 return 1;
448 }
449