Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : University of Illinois Open Source License
3 : Copyright 2003 Theoretical and Computational Biophysics Group,
4 : All rights reserved.
5 :
6 : Developed by: Theoretical and Computational Biophysics Group
7 : University of Illinois at Urbana-Champaign
8 : http://www.ks.uiuc.edu/
9 :
10 : Permission is hereby granted, free of charge, to any person obtaining a copy of
11 : this software and associated documentation files (the Software), to deal with
12 : the Software without restriction, including without limitation the rights to
13 : use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
14 : of the Software, and to permit persons to whom the Software is furnished to
15 : do so, subject to the following conditions:
16 :
17 : Redistributions of source code must retain the above copyright notice,
18 : this list of conditions and the following disclaimers.
19 :
20 : Redistributions in binary form must reproduce the above copyright notice,
21 : this list of conditions and the following disclaimers in the documentation
22 : and/or other materials provided with the distribution.
23 :
24 : Neither the names of Theoretical and Computational Biophysics Group,
25 : University of Illinois at Urbana-Champaign, nor the names of its contributors
26 : may be used to endorse or promote products derived from this Software without
27 : specific prior written permission.
28 :
29 : THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
32 : THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
33 : OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
34 : ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
35 : OTHER DEALINGS WITH THE SOFTWARE.
36 : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
37 : #if defined(__PLUMED_HAS_MOLFILE_PLUGINS) && ! defined(__PLUMED_HAS_EXTERNAL_MOLFILE_PLUGINS)
38 : /***************************************************************************
39 : *cr
40 : *cr (C) Copyright 1995-2016 The Board of Trustees of the
41 : *cr University of Illinois
42 : *cr All Rights Reserved
43 : *cr
44 : ***************************************************************************/
45 :
46 : /***************************************************************************
47 : * RCS INFORMATION:
48 : *
49 : * $RCSfile: dcdplugin.c,v $
50 : * $Author: johns $ $Locker: $ $State: Exp $
51 : * $Revision: 1.88 $ $Date: 2020/12/17 17:14:07 $
52 : *
53 : ***************************************************************************
54 : * DESCRIPTION:
55 : * Code for reading and writing CHARMM, NAMD, and X-PLOR format
56 : * molecular dynamic trajectory files.
57 : *
58 : * TODO:
59 : * Integrate improvements from the NAMD source tree
60 : * - NAMD's writer code has better type-correctness for the sizes
61 : * of "int". NAMD uses "int32" explicitly, which is required on
62 : * machines like the T3E. VMD's version of the code doesn't do that
63 : * presently.
64 : *
65 : * Try various alternative I/O API options:
66 : * - use mmap(), with read-once flags
67 : * - use O_DIRECT open mode on new revs of Linux kernel
68 : * - use directio() call on a file descriptor to enable on Solaris
69 : * - use aio_open()/read()/write()
70 : * - use readv/writev() etc.
71 : *
72 : * Standalone test binary compilation flags:
73 : * cc -fast -xarch=v9a -I../../include -DTEST_DCDPLUGIN dcdplugin.c \
74 : * -o ~/bin/readdcd -lm
75 : *
76 : * Profiling flags:
77 : * cc -xpg -fast -xarch=v9a -g -I../../include -DTEST_DCDPLUGIN dcdplugin.c \
78 : * -o ~/bin/readdcd -lm
79 : *
80 : ***************************************************************************/
81 :
82 : #include "largefiles.h" /* platform dependent 64-bit file I/O defines */
83 : #include "fastio.h" /* must come before others, for O_DIRECT... */
84 :
85 : #include <stdio.h>
86 : #include <sys/stat.h>
87 : #include <sys/types.h>
88 : #include <stdlib.h>
89 : #include <stddef.h>
90 : #include <string.h>
91 : #include <math.h>
92 : #include <time.h>
93 : #include "endianswap.h"
94 : #include "molfile_plugin.h"
95 :
96 : namespace PLMD{
97 : namespace molfile{
98 :
99 : #ifndef M_PI_2
100 : #define M_PI_2 1.57079632679489661922
101 : #endif
102 :
103 : #define RECSCALE32BIT 1
104 : #define RECSCALE64BIT 2
105 : #define RECSCALEMAX 2
106 :
107 : typedef struct {
108 : fio_fd fd;
109 : int natoms;
110 : int nsets;
111 : int setsread;
112 : int istart;
113 : int nsavc;
114 : double delta;
115 : int nfixed;
116 : float *x, *y, *z;
117 : int *freeind;
118 : float *fixedcoords;
119 : int reverse;
120 : int charmm;
121 : int first;
122 : int with_unitcell;
123 : } dcdhandle;
124 :
125 : /* Define error codes that may be returned by the DCD routines */
126 : #define DCD_SUCCESS 0 /* No problems */
127 : #define DCD_EOF -1 /* Normal EOF */
128 : #define DCD_DNE -2 /* DCD file does not exist */
129 : #define DCD_OPENFAILED -3 /* Open of DCD file failed */
130 : #define DCD_BADREAD -4 /* read call on DCD file failed */
131 : #define DCD_BADEOF -5 /* premature EOF found in DCD file */
132 : #define DCD_BADFORMAT -6 /* format of DCD file is wrong */
133 : #define DCD_FILEEXISTS -7 /* output file already exists */
134 : #define DCD_BADMALLOC -8 /* malloc failed */
135 : #define DCD_BADWRITE -9 /* write call on DCD file failed */
136 :
137 : /* Define feature flags for this DCD file */
138 : #define DCD_IS_XPLOR 0x00
139 : #define DCD_IS_CHARMM 0x01
140 : #define DCD_HAS_4DIMS 0x02
141 : #define DCD_HAS_EXTRA_BLOCK 0x04
142 : #define DCD_HAS_64BIT_REC 0x08
143 :
144 : /* defines used by write_dcdstep */
145 : #define NFILE_POS 8L
146 : #define NSTEP_POS 20L
147 :
148 : /* READ Macro to make porting easier */
149 : #define READ(fd, buf, size) fio_fread(((void *) buf), (size), 1, (fd))
150 :
151 : /* WRITE Macro to make porting easier */
152 : #define WRITE(fd, buf, size) fio_fwrite(((void *) buf), (size), 1, (fd))
153 :
154 : /* XXX This is broken - fread never returns -1 */
155 : #define CHECK_FREAD(X, msg) if (X==-1) { return(DCD_BADREAD); }
156 : #define CHECK_FEOF(X, msg) if (X==0) { return(DCD_BADEOF); }
157 :
158 :
159 : /* print DCD error in a human readable way */
160 0 : static void print_dcderror(const char *func, int errcode) {
161 : const char *errstr;
162 :
163 0 : switch (errcode) {
164 : case DCD_EOF: errstr = "end of file"; break;
165 0 : case DCD_DNE: errstr = "file not found"; break;
166 0 : case DCD_OPENFAILED: errstr = "file open failed"; break;
167 0 : case DCD_BADREAD: errstr = "error during read"; break;
168 0 : case DCD_BADEOF: errstr = "premature end of file"; break;
169 0 : case DCD_BADFORMAT: errstr = "corruption or unrecognized file structure"; break;
170 0 : case DCD_FILEEXISTS: errstr = "output file already exists"; break;
171 0 : case DCD_BADMALLOC: errstr = "memory allocation failed"; break;
172 0 : case DCD_BADWRITE: errstr = "error during write"; break;
173 0 : case DCD_SUCCESS:
174 : default:
175 : errstr = "no error";
176 0 : break;
177 : }
178 : printf("dcdplugin) %s: %s\n", func, errstr);
179 0 : }
180 :
181 :
182 : /*
183 : * Read the header information from a dcd file.
184 : * Input: fd - a file struct opened for binary reading.
185 : * Output: 0 on success, negative error code on failure.
186 : * Side effects: *natoms set to number of atoms per frame
187 : * *nsets set to number of frames in dcd file
188 : * *istart set to starting timestep of dcd file
189 : * *nsavc set to timesteps between dcd saves
190 : * *delta set to value of trajectory timestep
191 : * *nfixed set to number of fixed atoms
192 : * *freeind may be set to heap-allocated space
193 : * *reverse set to one if reverse-endian, zero if not.
194 : * *charmm set to internal code for handling charmm data.
195 : */
196 1 : static int read_dcdheader(fio_fd fd, int *N, int *NSET, int *ISTART,
197 : int *NSAVC, double *DELTA, int *NAMNF,
198 : int **FREEINDEXES, float **fixedcoords, int *reverseEndian,
199 : int *charmm)
200 : {
201 : unsigned int input_integer[2]; /* buffer space */
202 : int i, ret_val, rec_scale;
203 : char hdrbuf[84]; /* char buffer used to store header */
204 : int NTITLE;
205 : int dcdcordmagic;
206 : int hugefile = 0;
207 : char *corp = (char *) &dcdcordmagic;
208 :
209 : /* coordinate dcd file magic string 'CORD' */
210 : corp[0] = 'C';
211 : corp[1] = 'O';
212 : corp[2] = 'R';
213 : corp[3] = 'D';
214 :
215 : /* First thing in the file should be an 84.
216 : * some 64-bit compiles have a 64-bit record length indicator,
217 : * so we have to read two ints and check in a more complicated
218 : * way. :-( */
219 1 : ret_val = READ(fd, input_integer, 2*sizeof(unsigned int));
220 1 : CHECK_FREAD(ret_val, "reading first int from dcd file");
221 1 : CHECK_FEOF(ret_val, "reading first int from dcd file");
222 :
223 : /* Check magic number in file header and determine byte order*/
224 1 : if ((input_integer[0]+input_integer[1]) == 84) {
225 0 : *reverseEndian=0;
226 : rec_scale=RECSCALE64BIT;
227 : printf("dcdplugin) detected CHARMM -i8 64-bit DCD file of native endianness\n");
228 1 : } else if (input_integer[0] == 84 && input_integer[1] == dcdcordmagic) {
229 1 : *reverseEndian=0;
230 : rec_scale=RECSCALE32BIT;
231 : printf("dcdplugin) detected standard 32-bit DCD file of native endianness\n");
232 : } else {
233 : /* now try reverse endian */
234 0 : swap4_aligned(input_integer, 2); /* will have to unswap magic if 32-bit */
235 0 : if ((input_integer[0]+input_integer[1]) == 84) {
236 0 : *reverseEndian=1;
237 : rec_scale=RECSCALE64BIT;
238 : printf("dcdplugin) detected CHARMM -i8 64-bit DCD file of opposite endianness\n");
239 : } else {
240 0 : swap4_aligned(&input_integer[1], 1); /* unswap magic (see above) */
241 0 : if (input_integer[0] == 84 && input_integer[1] == dcdcordmagic) {
242 0 : *reverseEndian=1;
243 : rec_scale=RECSCALE32BIT;
244 : printf("dcdplugin) detected standard 32-bit DCD file of opposite endianness\n");
245 : } else {
246 : /* not simply reversed endianism or -i8, something rather more evil */
247 : printf("dcdplugin) unrecognized DCD header:\n");
248 0 : printf("dcdplugin) [0]: %10d [1]: %10d\n", input_integer[0], input_integer[1]);
249 0 : printf("dcdplugin) [0]: 0x%08x [1]: 0x%08x\n", input_integer[0], input_integer[1]);
250 0 : return DCD_BADFORMAT;
251 :
252 : }
253 : }
254 : }
255 :
256 : /* check for magic string, in case of long record markers */
257 : if (rec_scale == RECSCALE64BIT) {
258 0 : ret_val = READ(fd, input_integer, sizeof(unsigned int));
259 0 : if (input_integer[0] != dcdcordmagic) {
260 : printf("dcdplugin) failed to find CORD magic in CHARMM -i8 64-bit DCD file\n");
261 0 : return DCD_BADFORMAT;
262 : }
263 : }
264 :
265 : /* Buffer the entire header for random access */
266 1 : ret_val = READ(fd, hdrbuf, 80);
267 1 : CHECK_FREAD(ret_val, "buffering header");
268 1 : CHECK_FEOF(ret_val, "buffering header");
269 :
270 : /* CHARMm-genereate DCD files set the last integer in the */
271 : /* header, which is unused by X-PLOR, to its version number. */
272 : /* Checking if this is nonzero tells us this is a CHARMm file */
273 : /* and to look for other CHARMm flags. */
274 1 : if (*((int *) (hdrbuf + 76)) != 0) {
275 1 : (*charmm) = DCD_IS_CHARMM;
276 1 : if (*((int *) (hdrbuf + 40)) != 0)
277 1 : (*charmm) |= DCD_HAS_EXTRA_BLOCK;
278 :
279 1 : if (*((int *) (hdrbuf + 44)) == 1)
280 0 : (*charmm) |= DCD_HAS_4DIMS;
281 :
282 1 : if (rec_scale == RECSCALE64BIT)
283 0 : (*charmm) |= DCD_HAS_64BIT_REC;
284 :
285 : } else {
286 0 : (*charmm) = DCD_IS_XPLOR; /* must be an X-PLOR format DCD file */
287 : }
288 :
289 1 : if (*charmm & DCD_IS_CHARMM) {
290 : /* CHARMM and NAMD versions 2.1b1 and later */
291 : printf("dcdplugin) CHARMM format DCD file (also NAMD 2.1 and later)\n");
292 : } else {
293 : /* CHARMM and NAMD versions prior to 2.1b1 */
294 : printf("dcdplugin) X-PLOR format DCD file (also NAMD 2.0 and earlier)\n");
295 : }
296 :
297 : /* Store the number of sets of coordinates (NSET) */
298 1 : (*NSET) = *((int *) (hdrbuf));
299 1 : if (*reverseEndian) swap4_unaligned(NSET, 1);
300 :
301 : /* Store ISTART, the starting timestep */
302 1 : (*ISTART) = *((int *) (hdrbuf + 4));
303 1 : if (*reverseEndian) swap4_unaligned(ISTART, 1);
304 :
305 : /* Store NSAVC, the number of timesteps between dcd saves */
306 1 : (*NSAVC) = *((int *) (hdrbuf + 8));
307 1 : if (*reverseEndian) swap4_unaligned(NSAVC, 1);
308 :
309 : /* Store NAMNF, the number of fixed atoms */
310 1 : (*NAMNF) = *((int *) (hdrbuf + 32));
311 1 : if (*reverseEndian) swap4_unaligned(NAMNF, 1);
312 :
313 : /* Read in the timestep, DELTA */
314 : /* Note: DELTA is stored as a double with X-PLOR but as a float with CHARMm */
315 1 : if ((*charmm) & DCD_IS_CHARMM) {
316 : float ftmp;
317 1 : ftmp = *((float *)(hdrbuf+36)); /* is this safe on Alpha? */
318 1 : if (*reverseEndian)
319 0 : swap4_aligned(&ftmp, 1);
320 :
321 1 : *DELTA = (double)ftmp;
322 : } else {
323 0 : (*DELTA) = *((double *)(hdrbuf + 36));
324 0 : if (*reverseEndian) swap8_unaligned(DELTA, 1);
325 : }
326 :
327 : /* Get the end size of the first block */
328 1 : ret_val = READ(fd, input_integer, rec_scale*sizeof(int));
329 1 : CHECK_FREAD(ret_val, "reading second 84 from dcd file");
330 1 : CHECK_FEOF(ret_val, "reading second 84 from dcd file");
331 1 : if (*reverseEndian) swap4_aligned(input_integer, rec_scale);
332 :
333 1 : if (rec_scale == RECSCALE64BIT) {
334 0 : if ((input_integer[0]+input_integer[1]) != 84) {
335 : return DCD_BADFORMAT;
336 : }
337 : } else {
338 1 : if (input_integer[0] != 84) {
339 : return DCD_BADFORMAT;
340 : }
341 : }
342 :
343 : /* Read in the size of the next block */
344 1 : input_integer[1] = 0;
345 1 : ret_val = READ(fd, input_integer, rec_scale*sizeof(int));
346 1 : CHECK_FREAD(ret_val, "reading size of title block");
347 1 : CHECK_FEOF(ret_val, "reading size of title block");
348 1 : if (*reverseEndian) swap4_aligned(input_integer, rec_scale);
349 :
350 1 : if ((((input_integer[0]+input_integer[1])-4) % 80) == 0) {
351 : /* Read NTITLE, the number of 80 character title strings there are */
352 1 : ret_val = READ(fd, &NTITLE, sizeof(int));
353 1 : CHECK_FREAD(ret_val, "reading NTITLE");
354 1 : CHECK_FEOF(ret_val, "reading NTITLE");
355 1 : if (*reverseEndian) swap4_aligned(&NTITLE, 1);
356 :
357 1 : if (NTITLE < 0) {
358 : printf("dcdplugin) WARNING: Bogus NTITLE value: %d (hex: %08x)\n",
359 : NTITLE, NTITLE);
360 0 : return DCD_BADFORMAT;
361 : }
362 :
363 1 : if (NTITLE > 1000) {
364 : printf("dcdplugin) WARNING: Bogus NTITLE value: %d (hex: %08x)\n",
365 : NTITLE, NTITLE);
366 0 : if (NTITLE == 1095062083) {
367 : printf("dcdplugin) WARNING: Broken Vega ZZ 2.4.0 DCD file detected\n");
368 : printf("dcdplugin) Assuming 2 title lines, good luck...\n");
369 0 : NTITLE = 2;
370 : } else {
371 : printf("dcdplugin) Assuming zero title lines, good luck...\n");
372 0 : NTITLE = 0;
373 : }
374 : }
375 :
376 3 : for (i=0; i<NTITLE; i++) {
377 2 : fio_fseek(fd, 80, FIO_SEEK_CUR);
378 : CHECK_FEOF(ret_val, "reading TITLE");
379 : }
380 :
381 : /* Get the ending size for this block */
382 1 : ret_val = READ(fd, input_integer, rec_scale*sizeof(int));
383 1 : CHECK_FREAD(ret_val, "reading size of title block");
384 1 : CHECK_FEOF(ret_val, "reading size of title block");
385 : } else {
386 : return DCD_BADFORMAT;
387 : }
388 :
389 : /* Read in an integer '4' */
390 1 : input_integer[1] = 0;
391 1 : ret_val = READ(fd, input_integer, rec_scale*sizeof(int));
392 :
393 1 : CHECK_FREAD(ret_val, "reading a '4'");
394 1 : CHECK_FEOF(ret_val, "reading a '4'");
395 1 : if (*reverseEndian) swap4_aligned(input_integer, rec_scale);
396 :
397 1 : if ((input_integer[0]+input_integer[1]) != 4) {
398 : return DCD_BADFORMAT;
399 : }
400 :
401 : /* Read in the number of atoms */
402 1 : ret_val = READ(fd, N, sizeof(int));
403 1 : CHECK_FREAD(ret_val, "reading number of atoms");
404 1 : CHECK_FEOF(ret_val, "reading number of atoms");
405 1 : if (*reverseEndian) swap4_aligned(N, 1);
406 :
407 1 : if (*N > (1L<<30)) {
408 : hugefile=1;
409 : printf("dcdplugin) ***\n");
410 : printf("dcdplugin) *** Trajectory contains over 2^30 atoms.\n");
411 : printf("dcdplugin) *** Huge file integer wraparound handling enabled.\n");
412 : printf("dcdplugin) ***\n");
413 : }
414 :
415 : /* Read in an integer '4' */
416 1 : input_integer[1] = 0;
417 1 : ret_val = READ(fd, input_integer, rec_scale*sizeof(int));
418 1 : CHECK_FREAD(ret_val, "reading a '4'");
419 1 : CHECK_FEOF(ret_val, "reading a '4'");
420 1 : if (*reverseEndian) swap4_aligned(input_integer, rec_scale);
421 :
422 1 : if ((input_integer[0]+input_integer[1]) != 4) {
423 : return DCD_BADFORMAT;
424 : }
425 :
426 1 : *FREEINDEXES = NULL;
427 1 : *fixedcoords = NULL;
428 1 : if (*NAMNF != 0) {
429 0 : (*FREEINDEXES) = (int *) calloc((((ptrdiff_t)(*N))-(*NAMNF)), sizeof(int));
430 0 : if (*FREEINDEXES == NULL)
431 : return DCD_BADMALLOC;
432 :
433 0 : *fixedcoords = (float *) calloc(((ptrdiff_t)(*N))*4L - (*NAMNF), sizeof(float));
434 0 : if (*fixedcoords == NULL)
435 : return DCD_BADMALLOC;
436 :
437 : /* Read in index array size */
438 0 : input_integer[1]=0;
439 0 : ret_val = READ(fd, input_integer, rec_scale*sizeof(int));
440 0 : CHECK_FREAD(ret_val, "reading size of index array");
441 0 : CHECK_FEOF(ret_val, "reading size of index array");
442 0 : if (*reverseEndian) swap4_aligned(input_integer, rec_scale);
443 :
444 : /* when we have more then 2^30 atoms, tests like this one */
445 : /* are no longer meaningful and have to be bypassed... */
446 0 : if (!hugefile && ((input_integer[0]+input_integer[1]) != ((*N)-(*NAMNF))*4L)) {
447 : return DCD_BADFORMAT;
448 : }
449 :
450 0 : ret_val = READ(fd, (*FREEINDEXES), ((ptrdiff_t) ((*N)-(*NAMNF)))*sizeof(int));
451 0 : CHECK_FREAD(ret_val, "reading size of index array");
452 0 : CHECK_FEOF(ret_val, "reading size of index array");
453 :
454 0 : if (*reverseEndian)
455 0 : swap4_aligned((*FREEINDEXES), ((*N)-(*NAMNF)));
456 :
457 0 : input_integer[1]=0;
458 0 : ret_val = READ(fd, input_integer, rec_scale*sizeof(int));
459 0 : CHECK_FREAD(ret_val, "reading size of index array");
460 0 : CHECK_FEOF(ret_val, "reading size of index array");
461 0 : if (*reverseEndian) swap4_aligned(input_integer, rec_scale);
462 :
463 : /* when we have more then 2^30 atoms, tests like this one */
464 : /* are no longer meaningful and have to be bypassed... */
465 0 : if (!hugefile && ((input_integer[0]+input_integer[1]) != ((*N)-(*NAMNF))*4L)) {
466 0 : return DCD_BADFORMAT;
467 : }
468 : }
469 :
470 : return DCD_SUCCESS;
471 : }
472 :
473 :
474 21 : static int read_charmm_extrablock(fio_fd fd, int charmm, int reverseEndian,
475 : float *unitcell) {
476 : int i, input_integer[2], rec_scale;
477 :
478 21 : if (charmm & DCD_HAS_64BIT_REC) {
479 : rec_scale = RECSCALE64BIT;
480 : } else {
481 : rec_scale = RECSCALE32BIT;
482 : }
483 :
484 21 : if ((charmm & DCD_IS_CHARMM) && (charmm & DCD_HAS_EXTRA_BLOCK)) {
485 : /* Leading integer must be 48 */
486 21 : input_integer[1] = 0;
487 21 : if (fio_fread(input_integer, sizeof(int), rec_scale, fd) != rec_scale)
488 : return DCD_BADREAD;
489 21 : if (reverseEndian) swap4_aligned(input_integer, rec_scale);
490 21 : if ((input_integer[0]+input_integer[1]) == 48) {
491 : double tmp[6];
492 21 : if (fio_fread(tmp, 48, 1, fd) != 1) return DCD_BADREAD;
493 21 : if (reverseEndian)
494 0 : swap8_aligned(tmp, 6);
495 147 : for (i=0; i<6; i++) unitcell[i] = (float)tmp[i];
496 : } else {
497 : /* unrecognized block, just skip it */
498 0 : if (fio_fseek(fd, (input_integer[0]+input_integer[1]), FIO_SEEK_CUR)) return DCD_BADREAD;
499 : }
500 21 : if (fio_fread(input_integer, sizeof(int), rec_scale, fd) != rec_scale) return DCD_BADREAD;
501 : }
502 :
503 : return DCD_SUCCESS;
504 : }
505 :
506 :
507 0 : static int read_fixed_atoms(fio_fd fd, int N, int num_free, const int *indexes,
508 : int reverseEndian, const float *fixedcoords,
509 : float *freeatoms, float *pos, int charmm) {
510 : int i, input_integer[2], rec_scale;
511 :
512 0 : if(charmm & DCD_HAS_64BIT_REC) {
513 : rec_scale=RECSCALE64BIT;
514 : } else {
515 : rec_scale=RECSCALE32BIT;
516 : }
517 :
518 : /* Read leading integer */
519 0 : input_integer[1]=0;
520 0 : if (fio_fread(input_integer, sizeof(int), rec_scale, fd) != rec_scale) return DCD_BADREAD;
521 0 : if (reverseEndian) swap4_aligned(input_integer, rec_scale);
522 0 : if ((input_integer[0]+input_integer[1]) != 4L*num_free) return DCD_BADFORMAT;
523 :
524 : /* Read free atom coordinates */
525 0 : if (fio_fread(freeatoms, 4L*num_free, 1, fd) != 1) return DCD_BADREAD;
526 0 : if (reverseEndian)
527 0 : swap4_aligned(freeatoms, num_free);
528 :
529 : /* Copy fixed and free atom coordinates into position buffer */
530 0 : memcpy(pos, fixedcoords, 4L*N);
531 0 : for (i=0; i<num_free; i++)
532 0 : pos[indexes[i]-1] = freeatoms[i];
533 :
534 : /* Read trailing integer */
535 0 : input_integer[1]=0;
536 0 : if (fio_fread(input_integer, sizeof(int), rec_scale, fd) != rec_scale) return DCD_BADREAD;
537 0 : if (reverseEndian) swap4_aligned(input_integer, rec_scale);
538 0 : if ((input_integer[0]+input_integer[1]) != 4L*num_free) return DCD_BADFORMAT;
539 :
540 : return DCD_SUCCESS;
541 : }
542 :
543 :
544 21 : static int read_charmm_4dim(fio_fd fd, int charmm, int reverseEndian) {
545 : int input_integer[2], rec_scale;
546 :
547 21 : if (charmm & DCD_HAS_64BIT_REC) {
548 : rec_scale=RECSCALE64BIT;
549 : } else {
550 : rec_scale=RECSCALE32BIT;
551 : }
552 :
553 : /* If this is a CHARMm file and contains a 4th dimension block, */
554 : /* we must skip past it to avoid problems */
555 21 : if ((charmm & DCD_IS_CHARMM) && (charmm & DCD_HAS_4DIMS)) {
556 0 : input_integer[1]=0;
557 0 : if (fio_fread(input_integer, sizeof(int), rec_scale, fd) != rec_scale) return DCD_BADREAD;
558 0 : if (reverseEndian) swap4_aligned(input_integer, rec_scale);
559 0 : if (fio_fseek(fd, (input_integer[0]+input_integer[1]), FIO_SEEK_CUR)) return DCD_BADREAD;
560 0 : if (fio_fread(input_integer, sizeof(int), rec_scale, fd) != rec_scale) return DCD_BADREAD;
561 : }
562 :
563 : return DCD_SUCCESS;
564 : }
565 :
566 :
567 : /*
568 : * Read a dcd timestep from a dcd file
569 : * Input: fd - a file struct opened for binary reading, from which the
570 : * header information has already been read.
571 : * natoms, nfixed, first, *freeind, reverse, charmm - the corresponding
572 : * items as set by read_dcdheader
573 : * first - true if this is the first frame we are reading.
574 : * x, y, z: space for natoms each of floats.
575 : * unitcell - space for six floats to hold the unit cell data.
576 : * Not set if no unit cell data is present.
577 : * Output: 0 on success, negative error code on failure.
578 : * Side effects: x, y, z contain the coordinates for the timestep read.
579 : * unitcell holds unit cell data if present.
580 : */
581 21 : static int read_dcdstep(fio_fd fd, int N, float *X, float *Y, float *Z,
582 : float *unitcell, int num_fixed,
583 : int first, int *indexes, float *fixedcoords,
584 : int reverseEndian, int charmm) {
585 : int ret_val; /* Return value from read */
586 : ptrdiff_t rec_scale;
587 21 : int hugefile = (N > (1L<<30)) ? 1 : 0;
588 : int check_reclen = 1; /* Enable Fortran record length value safety checks */
589 :
590 : /* Fortran record length checks disabled for huge files or user request */
591 21 : if (hugefile || (getenv("VMDDCDNOCHECKRECLEN") != NULL))
592 : check_reclen = 0;
593 :
594 21 : if (charmm & DCD_HAS_64BIT_REC) {
595 : rec_scale=RECSCALE64BIT;
596 : } else {
597 : rec_scale=RECSCALE32BIT;
598 : }
599 :
600 21 : if ((num_fixed==0) || first) {
601 : /* temp storage for reading formatting info */
602 : /* note: has to be max size we'll ever use */
603 : int tmpbuf[6L*RECSCALEMAX];
604 :
605 : fio_iovec iov[7]; /* I/O vector for fio_readv() call */
606 : int i;
607 :
608 : /* if there are no fixed atoms or this is the first timestep read */
609 : /* then we read all coordinates normally. */
610 :
611 : /* read the charmm periodic cell information */
612 : /* XXX this too should be read together with the other items in a */
613 : /* single fio_readv() call in order to prevent lots of extra */
614 : /* kernel/user context switches. */
615 21 : ret_val = read_charmm_extrablock(fd, charmm, reverseEndian, unitcell);
616 21 : if (ret_val) return ret_val;
617 :
618 : /* setup the I/O vector for the call to fio_readv() */
619 : iov[0].iov_base = (fio_caddr_t) &tmpbuf[0]; /* read format integer */
620 21 : iov[0].iov_len = rec_scale*sizeof(int);
621 :
622 : iov[1].iov_base = (fio_caddr_t) X; /* read X coordinates */
623 21 : iov[1].iov_len = sizeof(float)*N;
624 :
625 21 : iov[2].iov_base = (fio_caddr_t) &tmpbuf[1*rec_scale]; /* read 2 format integers */
626 21 : iov[2].iov_len = rec_scale*sizeof(int) * 2L;
627 :
628 : iov[3].iov_base = (fio_caddr_t) Y; /* read Y coordinates */
629 : iov[3].iov_len = sizeof(float)*N;
630 :
631 21 : iov[4].iov_base = (fio_caddr_t) &tmpbuf[3L*rec_scale]; /* read 2 format integers */
632 : iov[4].iov_len = rec_scale*sizeof(int) * 2L;
633 :
634 : iov[5].iov_base = (fio_caddr_t) Z; /* read Y coordinates */
635 : iov[5].iov_len = sizeof(float)*N;
636 :
637 21 : iov[6].iov_base = (fio_caddr_t) &tmpbuf[5L*rec_scale]; /* read format integer */
638 : iov[6].iov_len = rec_scale*sizeof(int);
639 :
640 : #if 1
641 : /* Use fall-back code instead of readv(): */
642 : /* Some platforms implement readv() as user level code in libc, */
643 : /* and due to POSIX atomicity requirements for readv()/writev(), */
644 : /* they may copy data to internal temp buffers, which can kill */
645 : /* performance, and in cases when doing single I/O ops on large, */
646 : /* buffers, e.g. > 2GB, can fail with shorts reads or writes... */
647 : /* On such platforms it is best to avoid using readv()/writev()... */
648 : {
649 : int readcnt = 0;
650 21 : readcnt = fio_fread(iov[0].iov_base, iov[0].iov_len, 1, fd);
651 21 : readcnt += fio_fread(iov[1].iov_base, iov[1].iov_len, 1, fd);
652 21 : readcnt += fio_fread(iov[2].iov_base, iov[2].iov_len, 1, fd);
653 21 : readcnt += fio_fread(iov[3].iov_base, iov[3].iov_len, 1, fd);
654 21 : readcnt += fio_fread(iov[4].iov_base, iov[4].iov_len, 1, fd);
655 21 : readcnt += fio_fread(iov[5].iov_base, iov[5].iov_len, 1, fd);
656 21 : readcnt += fio_fread(iov[6].iov_base, iov[6].iov_len, 1, fd);
657 :
658 : /* if all records read correctly, then the reads are okay */
659 21 : if (readcnt != 7)
660 : return DCD_BADREAD;
661 : }
662 : #else
663 : /* check number of bytes actually read */
664 : if (fio_readv(fd, &iov[0], 7) != ((fio_size_t) (rec_scale*6L*sizeof(int) + 3L*N*sizeof(float))))
665 : return DCD_BADREAD;
666 : #endif
667 :
668 : /* convert endianism if necessary */
669 21 : if (reverseEndian) {
670 0 : swap4_aligned(&tmpbuf[0], rec_scale*6L);
671 0 : swap4_aligned(X, N);
672 0 : swap4_aligned(Y, N);
673 0 : swap4_aligned(Z, N);
674 : }
675 :
676 : /* when we have more then 2^30 atoms, tests like this one */
677 : /* are no longer meaningful and have to be bypassed... */
678 21 : if (check_reclen) {
679 : /* double-check the fortran format size values for safety */
680 21 : if (rec_scale == 1) {
681 147 : for (i=0; i<6; i++) {
682 126 : if (tmpbuf[i] != sizeof(float)*N) return DCD_BADFORMAT;
683 : }
684 : } else {
685 0 : for (i=0; i<6; i++) {
686 0 : if ((tmpbuf[2L*i]+tmpbuf[2L*i+1L]) != sizeof(float)*N) return DCD_BADFORMAT;
687 : }
688 : }
689 : }
690 :
691 : /* copy fixed atom coordinates into fixedcoords array if this was the */
692 : /* first timestep, to be used from now on. We just copy all atoms. */
693 21 : if (num_fixed && first) {
694 : memcpy(fixedcoords, X, N*sizeof(float));
695 0 : memcpy(fixedcoords+N, Y, N*sizeof(float));
696 0 : memcpy(fixedcoords+2L*N, Z, N*sizeof(float));
697 : }
698 :
699 : /* read in the optional charmm 4th array */
700 : /* XXX this too should be read together with the other items in a */
701 : /* single fio_readv() call in order to prevent lots of extra */
702 : /* kernel/user context switches. */
703 21 : ret_val = read_charmm_4dim(fd, charmm, reverseEndian);
704 21 : if (ret_val) return ret_val;
705 : } else {
706 : /* if there are fixed atoms, and this isn't the first frame, then we */
707 : /* only read in the non-fixed atoms for all subsequent timesteps. */
708 0 : ret_val = read_charmm_extrablock(fd, charmm, reverseEndian, unitcell);
709 0 : if (ret_val) return ret_val;
710 0 : ret_val = read_fixed_atoms(fd, N, N-num_fixed, indexes, reverseEndian,
711 0 : fixedcoords, fixedcoords+3L*N, X, charmm);
712 0 : if (ret_val) return ret_val;
713 0 : ret_val = read_fixed_atoms(fd, N, N-num_fixed, indexes, reverseEndian,
714 0 : fixedcoords+N, fixedcoords+3L*N, Y, charmm);
715 0 : if (ret_val) return ret_val;
716 0 : ret_val = read_fixed_atoms(fd, N, N-num_fixed, indexes, reverseEndian,
717 0 : fixedcoords+2*N, fixedcoords+3L*N, Z, charmm);
718 0 : if (ret_val) return ret_val;
719 0 : ret_val = read_charmm_4dim(fd, charmm, reverseEndian);
720 0 : if (ret_val) return ret_val;
721 : }
722 :
723 : return DCD_SUCCESS;
724 : }
725 :
726 :
727 : /*
728 : * Skip past a timestep. If there are fixed atoms, this cannot be used with
729 : * the first timestep.
730 : * Input: fd - a file struct from which the header has already been read
731 : * natoms - number of atoms per timestep
732 : * nfixed - number of fixed atoms
733 : * charmm - charmm flags as returned by read_dcdheader
734 : * Output: 0 on success, negative error code on failure.
735 : * Side effects: One timestep will be skipped; fd will be positioned at the
736 : * next timestep.
737 : */
738 0 : static int skip_dcdstep(fio_fd fd, int natoms, int nfixed, int charmm) {
739 : ptrdiff_t seekoffset = 0;
740 : ptrdiff_t rec_scale;
741 :
742 0 : if (charmm & DCD_HAS_64BIT_REC) {
743 : rec_scale=RECSCALE64BIT;
744 : } else {
745 : rec_scale=RECSCALE32BIT;
746 : }
747 :
748 : /* Skip charmm extra block */
749 0 : if ((charmm & DCD_IS_CHARMM) && (charmm & DCD_HAS_EXTRA_BLOCK)) {
750 0 : seekoffset += 4L*rec_scale + 48L + 4L*rec_scale;
751 : }
752 :
753 : /* For each atom set, seek past an int, the free atoms, and another int. */
754 0 : seekoffset += 3L * (2L*rec_scale + natoms - nfixed) * 4L;
755 :
756 : /* Assume that charmm 4th dim is the same size as the other three. */
757 0 : if ((charmm & DCD_IS_CHARMM) && (charmm & DCD_HAS_4DIMS)) {
758 0 : seekoffset += (2L*rec_scale + natoms - nfixed) * 4L;
759 : }
760 :
761 0 : if (fio_fseek(fd, seekoffset, FIO_SEEK_CUR)) return DCD_BADEOF;
762 :
763 : return DCD_SUCCESS;
764 : }
765 :
766 :
767 : /*
768 : * Write a timestep to a dcd file
769 : * Input: fd - a file struct for which a dcd header has already been written
770 : * curframe: Count of frames written to this file, starting with 1.
771 : * curstep: Count of timesteps elapsed = istart + curframe * nsavc.
772 : * natoms: number of elements in x, y, z arrays
773 : * x, y, z: pointers to atom coordinates
774 : * Output: 0 on success, negative error code on failure.
775 : * Side effects: coordinates are written to the dcd file.
776 : */
777 0 : static int write_dcdstep(fio_fd fd, int curframe, int curstep, int N,
778 : const float *X, const float *Y, const float *Z,
779 : const double *unitcell, int charmm) {
780 : int out_integer;
781 :
782 0 : if (charmm) {
783 : /* write out optional unit cell */
784 0 : if (unitcell != NULL) {
785 : out_integer = 48; /* 48 bytes (6 floats) */
786 0 : fio_write_int32(fd, out_integer);
787 0 : WRITE(fd, unitcell, out_integer);
788 0 : fio_write_int32(fd, out_integer);
789 : }
790 : }
791 :
792 : /* write out coordinates */
793 0 : out_integer = N*4; /* N*4 bytes per X/Y/Z array (N floats per array) */
794 0 : fio_write_int32(fd, out_integer);
795 0 : if (fio_fwrite((void *) X, out_integer, 1, fd) != 1) return DCD_BADWRITE;
796 0 : fio_write_int32(fd, out_integer);
797 0 : fio_write_int32(fd, out_integer);
798 0 : if (fio_fwrite((void *) Y, out_integer, 1, fd) != 1) return DCD_BADWRITE;
799 0 : fio_write_int32(fd, out_integer);
800 0 : fio_write_int32(fd, out_integer);
801 0 : if (fio_fwrite((void *) Z, out_integer, 1, fd) != 1) return DCD_BADWRITE;
802 0 : fio_write_int32(fd, out_integer);
803 :
804 : /* update the DCD header information */
805 0 : fio_fseek(fd, NFILE_POS, FIO_SEEK_SET);
806 0 : fio_write_int32(fd, curframe);
807 0 : fio_fseek(fd, NSTEP_POS, FIO_SEEK_SET);
808 0 : fio_write_int32(fd, curstep);
809 0 : fio_fseek(fd, 0, FIO_SEEK_END);
810 :
811 0 : return DCD_SUCCESS;
812 : }
813 :
814 :
815 : /*
816 : * Write a header for a new dcd file
817 : * Input: fd - file struct opened for binary writing
818 : * remarks - string to be put in the remarks section of the header.
819 : * The string will be truncated to 70 characters.
820 : * natoms, istart, nsavc, delta - see comments in read_dcdheader
821 : * Output: 0 on success, negative error code on failure.
822 : * Side effects: Header information is written to the dcd file.
823 : */
824 0 : static int write_dcdheader(fio_fd fd, const char *remarks, int N,
825 : int ISTART, int NSAVC, double DELTA, int with_unitcell,
826 : int charmm) {
827 : int out_integer;
828 : float out_float;
829 : char title_string[200];
830 : time_t cur_time;
831 : struct tm *tmbuf;
832 : char time_str[81];
833 :
834 0 : out_integer = 84;
835 0 : WRITE(fd, (char *) & out_integer, sizeof(int));
836 : strcpy(title_string, "CORD");
837 0 : WRITE(fd, title_string, 4);
838 0 : fio_write_int32(fd, 0); /* Number of frames in file, none written yet */
839 0 : fio_write_int32(fd, ISTART); /* Starting timestep */
840 0 : fio_write_int32(fd, NSAVC); /* Timesteps between frames written to the file */
841 0 : fio_write_int32(fd, 0); /* Number of timesteps in simulation */
842 0 : fio_write_int32(fd, 0); /* NAMD writes NSTEP or ISTART - NSAVC here? */
843 0 : fio_write_int32(fd, 0);
844 0 : fio_write_int32(fd, 0);
845 0 : fio_write_int32(fd, 0);
846 0 : fio_write_int32(fd, 0);
847 0 : if (charmm) {
848 0 : out_float = DELTA;
849 0 : WRITE(fd, (char *) &out_float, sizeof(float));
850 0 : if (with_unitcell) {
851 0 : fio_write_int32(fd, 1);
852 : } else {
853 0 : fio_write_int32(fd, 0);
854 : }
855 : } else {
856 0 : WRITE(fd, (char *) &DELTA, sizeof(double));
857 : }
858 0 : fio_write_int32(fd, 0);
859 0 : fio_write_int32(fd, 0);
860 0 : fio_write_int32(fd, 0);
861 0 : fio_write_int32(fd, 0);
862 0 : fio_write_int32(fd, 0);
863 0 : fio_write_int32(fd, 0);
864 0 : fio_write_int32(fd, 0);
865 0 : fio_write_int32(fd, 0);
866 0 : if (charmm) {
867 0 : fio_write_int32(fd, 24); /* Pretend to be CHARMM version 24 */
868 : } else {
869 0 : fio_write_int32(fd, 0);
870 : }
871 0 : fio_write_int32(fd, 84);
872 0 : fio_write_int32(fd, 164);
873 0 : fio_write_int32(fd, 2);
874 :
875 : strncpy(title_string, remarks, 80);
876 0 : title_string[79] = '\0';
877 0 : WRITE(fd, title_string, 80);
878 :
879 0 : cur_time=time(NULL);
880 0 : tmbuf=localtime(&cur_time);
881 0 : strftime(time_str, 80, "REMARKS Created %d %B, %Y at %R", tmbuf);
882 0 : WRITE(fd, time_str, 80);
883 :
884 0 : fio_write_int32(fd, 164);
885 0 : fio_write_int32(fd, 4);
886 0 : fio_write_int32(fd, N);
887 0 : fio_write_int32(fd, 4);
888 :
889 0 : return DCD_SUCCESS;
890 : }
891 :
892 :
893 : /*
894 : * clean up dcd data
895 : * Input: nfixed, freeind - elements as returned by read_dcdheader
896 : * Output: None
897 : * Side effects: Space pointed to by freeind is freed if necessary.
898 : */
899 1 : static void close_dcd_read(int *indexes, float *fixedcoords) {
900 1 : free(indexes);
901 1 : free(fixedcoords);
902 1 : }
903 :
904 :
905 1 : static void *open_dcd_read(const char *path, const char *filetype,
906 : int *natoms) {
907 : dcdhandle *dcd;
908 : fio_fd fd;
909 : int rc;
910 : struct stat stbuf;
911 :
912 1 : if (!path) return NULL;
913 :
914 : #if !(defined(_MSC_VER) && defined(FASTIO_NATIVEWIN32))
915 : /* See if the file exists, and get its size */
916 : memset(&stbuf, 0, sizeof(struct stat));
917 1 : if (stat(path, &stbuf)) {
918 : printf("dcdplugin) Could not access file '%s'.\n", path);
919 0 : return NULL;
920 : }
921 : #endif
922 :
923 1 : if (fio_open(path, FIO_READ, &fd) < 0) {
924 : printf("dcdplugin) Could not open file '%s' for reading.\n", path);
925 0 : return NULL;
926 : }
927 :
928 1 : dcd = (dcdhandle *)malloc(sizeof(dcdhandle));
929 : memset(dcd, 0, sizeof(dcdhandle));
930 1 : dcd->fd = fd;
931 :
932 1 : if ((rc = read_dcdheader(dcd->fd, &dcd->natoms, &dcd->nsets, &dcd->istart,
933 : &dcd->nsavc, &dcd->delta, &dcd->nfixed, &dcd->freeind,
934 : &dcd->fixedcoords, &dcd->reverse, &dcd->charmm))) {
935 0 : print_dcderror("read_dcdheader", rc);
936 0 : fio_fclose(dcd->fd);
937 0 : free(dcd);
938 0 : return NULL;
939 : }
940 :
941 : /*
942 : * Check that the file is big enough to really hold the number of sets
943 : * it claims to have. Then we'll use nsets to keep track of where EOF
944 : * should be.
945 : */
946 : {
947 : fio_size_t ndims, firstframesize, framesize, extrablocksize;
948 : fio_size_t trjsize, filesize, curpos;
949 : int newnsets;
950 :
951 1 : extrablocksize = dcd->charmm & DCD_HAS_EXTRA_BLOCK ? 48 + 8 : 0;
952 1 : ndims = dcd->charmm & DCD_HAS_4DIMS ? 4 : 3;
953 1 : firstframesize = (dcd->natoms+2) * ndims * sizeof(float) + extrablocksize;
954 1 : framesize = (dcd->natoms-dcd->nfixed+2) * ndims * sizeof(float)
955 1 : + extrablocksize;
956 :
957 : /*
958 : * It's safe to use ftell, even though ftell returns a long, because the
959 : * header size is < 4GB.
960 : */
961 :
962 1 : curpos = fio_ftell(dcd->fd); /* save current offset (end of header) */
963 :
964 : #if defined(_MSC_VER) && defined(FASTIO_NATIVEWIN32)
965 : /* the stat() call is not 64-bit savvy on Windows */
966 : /* so we have to use the fastio fseek/ftell routines for this */
967 : /* until we add a portable filesize routine for this purpose */
968 : fio_fseek(dcd->fd, 0, FIO_SEEK_END); /* seek to end of file */
969 : filesize = fio_ftell(dcd->fd);
970 : fio_fseek(dcd->fd, curpos, FIO_SEEK_SET); /* return to end of header */
971 : #else
972 1 : filesize = stbuf.st_size; /* this works ok on Unix machines */
973 : #endif
974 1 : trjsize = filesize - curpos - firstframesize;
975 1 : if (trjsize < 0) {
976 : printf("dcdplugin) file '%s' appears to contain no timesteps.\n", path);
977 0 : fio_fclose(dcd->fd);
978 0 : free(dcd);
979 0 : return NULL;
980 : }
981 :
982 1 : newnsets = trjsize / framesize + 1;
983 :
984 1 : if (dcd->nsets > 0 && newnsets != dcd->nsets) {
985 : printf("dcdplugin) Warning: DCD header claims %d frames, but \n"
986 : "dcdplugin) file size (%ld) indicates there are actually \n"
987 : "%d frames of size (%ld)\n",
988 : dcd->nsets, trjsize, newnsets, framesize);
989 : }
990 :
991 1 : dcd->nsets = newnsets;
992 1 : dcd->setsread = 0;
993 : }
994 :
995 1 : dcd->first = 1;
996 1 : dcd->x = (float *)malloc(dcd->natoms * sizeof(float));
997 1 : dcd->y = (float *)malloc(dcd->natoms * sizeof(float));
998 1 : dcd->z = (float *)malloc(dcd->natoms * sizeof(float));
999 1 : if (!dcd->x || !dcd->y || !dcd->z) {
1000 : printf("dcdplugin) Unable to allocate space for %d atoms.\n", dcd->natoms);
1001 0 : if (dcd->x)
1002 0 : free(dcd->x);
1003 0 : if (dcd->y)
1004 0 : free(dcd->y);
1005 0 : if (dcd->z)
1006 0 : free(dcd->z);
1007 0 : fio_fclose(dcd->fd);
1008 0 : free(dcd);
1009 0 : return NULL;
1010 : }
1011 1 : *natoms = dcd->natoms;
1012 1 : return dcd;
1013 : }
1014 :
1015 :
1016 22 : static int read_next_timestep(void *v, int natoms, molfile_timestep_t *ts) {
1017 : dcdhandle *dcd;
1018 : int i, j, rc;
1019 : float unitcell[6];
1020 22 : unitcell[0] = unitcell[2] = unitcell[5] = 0.0f;
1021 22 : unitcell[1] = unitcell[3] = unitcell[4] = 90.0f;
1022 : dcd = (dcdhandle *)v;
1023 :
1024 : /* Check for EOF here; that way all EOF's encountered later must be errors */
1025 22 : if (dcd->setsread == dcd->nsets) return MOLFILE_EOF;
1026 21 : dcd->setsread++;
1027 21 : if (!ts) {
1028 0 : if (dcd->first && dcd->nfixed) {
1029 : /* We can't just skip it because we need the fixed atom coordinates */
1030 0 : rc = read_dcdstep(dcd->fd, dcd->natoms, dcd->x, dcd->y, dcd->z,
1031 : unitcell, dcd->nfixed, dcd->first, dcd->freeind, dcd->fixedcoords,
1032 : dcd->reverse, dcd->charmm);
1033 0 : dcd->first = 0;
1034 0 : return rc; /* XXX this needs to be updated */
1035 : }
1036 0 : dcd->first = 0;
1037 : /* XXX this needs to be changed */
1038 0 : return skip_dcdstep(dcd->fd, dcd->natoms, dcd->nfixed, dcd->charmm);
1039 : }
1040 21 : rc = read_dcdstep(dcd->fd, dcd->natoms, dcd->x, dcd->y, dcd->z, unitcell,
1041 : dcd->nfixed, dcd->first, dcd->freeind, dcd->fixedcoords,
1042 : dcd->reverse, dcd->charmm);
1043 21 : dcd->first = 0;
1044 21 : if (rc < 0) {
1045 0 : print_dcderror("read_dcdstep", rc);
1046 0 : return MOLFILE_ERROR;
1047 : }
1048 :
1049 : /* copy timestep data from plugin-local buffers to VMD's buffer */
1050 : /* XXX
1051 : * This code is still the root of all evil. Just doing this extra copy
1052 : * cuts the I/O rate of the DCD reader from 728 MB/sec down to
1053 : * 394 MB/sec when reading from a ram filesystem.
1054 : * For a physical disk filesystem, the I/O rate goes from
1055 : * 187 MB/sec down to 122 MB/sec. Clearly this extra copy has to go.
1056 : */
1057 : {
1058 21 : int natoms = dcd->natoms;
1059 21 : float *nts = ts->coords;
1060 21 : const float *bufx = dcd->x;
1061 21 : const float *bufy = dcd->y;
1062 21 : const float *bufz = dcd->z;
1063 :
1064 483 : for (i=0, j=0; i<natoms; i++, j+=3) {
1065 462 : nts[j ] = bufx[i];
1066 462 : nts[j + 1] = bufy[i];
1067 462 : nts[j + 2] = bufz[i];
1068 : }
1069 : }
1070 :
1071 21 : ts->A = unitcell[0];
1072 21 : ts->B = unitcell[2];
1073 21 : ts->C = unitcell[5];
1074 :
1075 21 : if (unitcell[1] >= -1.0 && unitcell[1] <= 1.0 &&
1076 21 : unitcell[3] >= -1.0 && unitcell[3] <= 1.0 &&
1077 21 : unitcell[4] >= -1.0 && unitcell[4] <= 1.0) {
1078 : /* This file was generated by CHARMM, or by NAMD > 2.5, with the angle */
1079 : /* cosines of the periodic cell angles written to the DCD file. */
1080 : /* This formulation improves rounding behavior for orthogonal cells */
1081 : /* so that the angles end up at precisely 90 degrees, unlike acos(). */
1082 21 : ts->alpha = 90.0 - asin(unitcell[4]) * 90.0 / M_PI_2; /* cosBC */
1083 21 : ts->beta = 90.0 - asin(unitcell[3]) * 90.0 / M_PI_2; /* cosAC */
1084 21 : ts->gamma = 90.0 - asin(unitcell[1]) * 90.0 / M_PI_2; /* cosAB */
1085 : } else {
1086 : /* This file was likely generated by NAMD 2.5 and the periodic cell */
1087 : /* angles are specified in degrees rather than angle cosines. */
1088 0 : ts->alpha = unitcell[4]; /* angle between B and C */
1089 0 : ts->beta = unitcell[3]; /* angle between A and C */
1090 0 : ts->gamma = unitcell[1]; /* angle between A and B */
1091 : }
1092 :
1093 : return MOLFILE_SUCCESS;
1094 : }
1095 :
1096 :
1097 1 : static void close_file_read(void *v) {
1098 : dcdhandle *dcd = (dcdhandle *)v;
1099 1 : close_dcd_read(dcd->freeind, dcd->fixedcoords);
1100 1 : fio_fclose(dcd->fd);
1101 1 : free(dcd->x);
1102 1 : free(dcd->y);
1103 1 : free(dcd->z);
1104 1 : free(dcd);
1105 1 : }
1106 :
1107 :
1108 0 : static void *open_dcd_write(const char *path, const char *filetype,
1109 : int natoms) {
1110 : dcdhandle *dcd;
1111 : fio_fd fd;
1112 : int rc;
1113 : int istart, nsavc;
1114 : double delta;
1115 : int with_unitcell;
1116 : int charmm;
1117 :
1118 0 : if (fio_open(path, FIO_WRITE, &fd) < 0) {
1119 : printf("dcdplugin) Could not open file '%s' for writing\n", path);
1120 0 : return NULL;
1121 : }
1122 :
1123 0 : dcd = (dcdhandle *)malloc(sizeof(dcdhandle));
1124 : memset(dcd, 0, sizeof(dcdhandle));
1125 0 : dcd->fd = fd;
1126 :
1127 : istart = 0; /* starting timestep of DCD file */
1128 : nsavc = 1; /* number of timesteps between written DCD frames */
1129 : delta = 1.0; /* length of a timestep */
1130 :
1131 0 : if (getenv("VMDDCDWRITEXPLORFORMAT") != NULL) {
1132 : with_unitcell = 0; /* no unit cell info */
1133 : charmm = DCD_IS_XPLOR; /* X-PLOR format */
1134 : printf("dcdplugin) WARNING: Writing DCD file in X-PLOR format, \n");
1135 : printf("dcdplugin) WARNING: unit cell information will be lost!\n");
1136 : } else {
1137 : with_unitcell = 1; /* contains unit cell infor (Charmm format) */
1138 : charmm = DCD_IS_CHARMM; /* charmm-formatted DCD file */
1139 : if (with_unitcell)
1140 : charmm |= DCD_HAS_EXTRA_BLOCK;
1141 : }
1142 :
1143 0 : rc = write_dcdheader(dcd->fd, "Created by DCD plugin", natoms,
1144 : istart, nsavc, delta, with_unitcell, charmm);
1145 :
1146 0 : if (rc < 0) {
1147 0 : print_dcderror("write_dcdheader", rc);
1148 0 : fio_fclose(dcd->fd);
1149 0 : free(dcd);
1150 0 : return NULL;
1151 : }
1152 :
1153 0 : dcd->natoms = natoms;
1154 0 : dcd->nsets = 0;
1155 0 : dcd->istart = istart;
1156 0 : dcd->nsavc = nsavc;
1157 0 : dcd->with_unitcell = with_unitcell;
1158 0 : dcd->charmm = charmm;
1159 0 : dcd->x = (float *)malloc(natoms * sizeof(float));
1160 0 : dcd->y = (float *)malloc(natoms * sizeof(float));
1161 0 : dcd->z = (float *)malloc(natoms * sizeof(float));
1162 0 : return dcd;
1163 : }
1164 :
1165 :
1166 0 : static int write_timestep(void *v, const molfile_timestep_t *ts) {
1167 : dcdhandle *dcd = (dcdhandle *)v;
1168 : int i, rc, curstep;
1169 0 : float *pos = ts->coords;
1170 : double unitcell[6];
1171 0 : unitcell[0] = unitcell[2] = unitcell[5] = 0.0f;
1172 0 : unitcell[1] = unitcell[3] = unitcell[4] = 90.0f;
1173 :
1174 : /* copy atom coords into separate X/Y/Z arrays for writing */
1175 0 : for (i=0; i<dcd->natoms; i++) {
1176 0 : dcd->x[i] = *(pos++);
1177 0 : dcd->y[i] = *(pos++);
1178 0 : dcd->z[i] = *(pos++);
1179 : }
1180 0 : dcd->nsets++;
1181 0 : curstep = dcd->istart + dcd->nsets * dcd->nsavc;
1182 :
1183 0 : unitcell[0] = ts->A;
1184 0 : unitcell[2] = ts->B;
1185 0 : unitcell[5] = ts->C;
1186 0 : unitcell[1] = sin((M_PI_2 / 90.0) * (90.0 - ts->gamma)); /* cosAB */
1187 0 : unitcell[3] = sin((M_PI_2 / 90.0) * (90.0 - ts->beta)); /* cosAC */
1188 0 : unitcell[4] = sin((M_PI_2 / 90.0) * (90.0 - ts->alpha)); /* cosBC */
1189 :
1190 0 : rc = write_dcdstep(dcd->fd, dcd->nsets, curstep, dcd->natoms,
1191 0 : dcd->x, dcd->y, dcd->z,
1192 0 : dcd->with_unitcell ? unitcell : NULL,
1193 : dcd->charmm);
1194 0 : if (rc < 0) {
1195 0 : print_dcderror("write_dcdstep", rc);
1196 0 : return MOLFILE_ERROR;
1197 : }
1198 :
1199 : return MOLFILE_SUCCESS;
1200 : }
1201 :
1202 0 : static void close_file_write(void *v) {
1203 : dcdhandle *dcd = (dcdhandle *)v;
1204 0 : fio_fclose(dcd->fd);
1205 0 : free(dcd->x);
1206 0 : free(dcd->y);
1207 0 : free(dcd->z);
1208 0 : free(dcd);
1209 0 : }
1210 :
1211 :
1212 : /*
1213 : * Initialization stuff here
1214 : */
1215 : static molfile_plugin_t plugin;
1216 :
1217 10632 : VMDPLUGIN_API int VMDPLUGIN_init() {
1218 : memset(&plugin, 0, sizeof(molfile_plugin_t));
1219 10632 : plugin.abiversion = vmdplugin_ABIVERSION;
1220 10632 : plugin.type = MOLFILE_PLUGIN_TYPE;
1221 10632 : plugin.name = "dcd";
1222 10632 : plugin.prettyname = "CHARMM,NAMD,XPLOR DCD Trajectory";
1223 10632 : plugin.author = "Axel Kohlmeyer, Justin Gullingsrud, John Stone";
1224 10632 : plugin.majorv = 1;
1225 10632 : plugin.minorv = 18;
1226 10632 : plugin.is_reentrant = VMDPLUGIN_THREADSAFE;
1227 10632 : plugin.filename_extension = "dcd";
1228 10632 : plugin.open_file_read = open_dcd_read;
1229 10632 : plugin.read_next_timestep = read_next_timestep;
1230 10632 : plugin.close_file_read = close_file_read;
1231 10632 : plugin.open_file_write = open_dcd_write;
1232 10632 : plugin.write_timestep = write_timestep;
1233 10632 : plugin.close_file_write = close_file_write;
1234 10632 : return VMDPLUGIN_SUCCESS;
1235 : }
1236 :
1237 10632 : VMDPLUGIN_API int VMDPLUGIN_register(void *v, vmdplugin_register_cb cb) {
1238 10632 : (*cb)(v, (vmdplugin_t *)&plugin);
1239 10632 : return VMDPLUGIN_SUCCESS;
1240 : }
1241 :
1242 0 : VMDPLUGIN_API int VMDPLUGIN_fini() {
1243 0 : return VMDPLUGIN_SUCCESS;
1244 : }
1245 :
1246 : }
1247 : }
1248 :
1249 :
1250 : #ifdef TEST_DCDPLUGIN
1251 :
1252 : #include <sys/time.h>
1253 :
1254 : /* get the time of day from the system clock, and store it (in seconds) */
1255 : double time_of_day(void) {
1256 : #if defined(_MSC_VER)
1257 : double t;
1258 :
1259 : t = GetTickCount();
1260 : t = t / 1000.0;
1261 :
1262 : return t;
1263 : #else
1264 : struct timeval tm;
1265 : struct timezone tz;
1266 :
1267 : gettimeofday(&tm, &tz);
1268 : return((double)(tm.tv_sec) + (double)(tm.tv_usec)/1000000.0);
1269 : #endif
1270 : }
1271 :
1272 : int main(int argc, char *argv[]) {
1273 : molfile_timestep_t timestep;
1274 : void *v;
1275 : dcdhandle *dcd;
1276 : int i, natoms;
1277 : float sizeMB =0.0, totalMB = 0.0;
1278 : double starttime, endtime, totaltime = 0.0;
1279 :
1280 : while (--argc) {
1281 : ++argv;
1282 : natoms = 0;
1283 : v = open_dcd_read(*argv, "dcd", &natoms);
1284 : if (!v) {
1285 : fprintf(stderr, "main) open_dcd_read failed for file %s\n", *argv);
1286 : return 1;
1287 : }
1288 : dcd = (dcdhandle *)v;
1289 : sizeMB = ((natoms * 3.0) * dcd->nsets * 4.0) / (1024.0 * 1024.0);
1290 : totalMB += sizeMB;
1291 : printf("main) file: %s\n", *argv);
1292 : printf(" %d atoms, %d frames, size: %6.1fMB\n", natoms, dcd->nsets, sizeMB);
1293 :
1294 : starttime = time_of_day();
1295 : timestep.coords = (float *)malloc(3*sizeof(float)*natoms);
1296 : for (i=0; i<dcd->nsets; i++) {
1297 : int rc = read_next_timestep(v, natoms, ×tep);
1298 : if (rc) {
1299 : fprintf(stderr, "error in read_next_timestep on frame %d\n", i);
1300 : return 1;
1301 : }
1302 : }
1303 : endtime = time_of_day();
1304 : close_file_read(v);
1305 : totaltime += endtime - starttime;
1306 : printf(" Time: %5.1f seconds\n", endtime - starttime);
1307 : printf(" Speed: %5.1f MB/sec, %5.1f timesteps/sec\n", sizeMB / (endtime - starttime), (dcd->nsets / (endtime - starttime)));
1308 : }
1309 : printf("Overall Size: %6.1f MB\n", totalMB);
1310 : printf("Overall Time: %6.1f seconds\n", totaltime);
1311 : printf("Overall Speed: %5.1f MB/sec\n", totalMB / totaltime);
1312 : return 0;
1313 : }
1314 :
1315 : #endif
1316 :
1317 : #endif
|