OSDN Git Service

016c4a282b92ea029264dacaa6599c5e0f0c00db
[molby/Molby.git] / MolLib / Molecule.c
1 /*
2  *  Molecule.c
3  *
4  *  Created by Toshi Nagata on 06/03/11.
5  *  Copyright 2006 Toshi Nagata. All rights reserved.
6  *
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation version 2 of the License.
10  
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15  */
16
17 #include "MolLib.h"
18 #include <string.h>
19 #include <stddef.h>
20 #include <ctype.h>
21 #include <math.h>
22
23 #include "Missing.h"
24 #include "Dcd.h"
25 #include "MD/MDCore.h"
26 #include "MD/MDPressure.h"
27
28 static Molecule *sMoleculeRoot = NULL;
29 static int sMoleculeUntitledCount = 0;
30
31 Int gSizeOfAtomRecord = sizeof(Atom);
32
33 #pragma mark ====== Utility function ======
34
35 int
36 strlen_limit(const char *s, int limit)
37 {
38         int len;
39         for (len = 0; *s != 0 && (limit < 0 || len < limit); s++, len++);
40         return len;
41 }
42
43 #pragma mark ======  Atom handling  ======
44
45 Atom *
46 AtomDuplicate(Atom *dst, const Atom *src)
47 {
48         if (dst == NULL) {
49                 dst = (Atom *)malloc(gSizeOfAtomRecord);
50                 if (dst == NULL)
51                         return NULL;
52         }
53         memmove(dst, src, gSizeOfAtomRecord);
54         if (src->aniso != NULL) {
55                 dst->aniso = (Aniso *)malloc(sizeof(Aniso));
56                 if (dst->aniso != NULL)
57                         memmove(dst->aniso, src->aniso, sizeof(Aniso));
58         }
59         if (src->frames != NULL) {
60                 dst->frames = (Vector *)malloc(sizeof(Vector) * src->nframes);
61                 if (dst->frames != NULL) {
62                         memmove(dst->frames, src->frames, sizeof(Vector) * src->nframes);
63                         dst->nframes = src->nframes;
64                 } else {
65                         dst->nframes = 0;
66                 }
67         }
68         return dst;
69 }
70
71 void
72 AtomClean(Atom *ap)
73 {
74         if (ap->aniso != NULL) {
75                 free(ap->aniso);
76                 ap->aniso = NULL;
77         }
78         if (ap->frames != NULL) {
79                 free(ap->frames);
80                 ap->frames = NULL;
81                 ap->nframes = 0;
82         }
83 }
84
85 void
86 CubeRelease(Cube *cp)
87 {
88         if (cp != NULL) {
89                 if (cp->dp != NULL)
90                         free(cp->dp);
91                 free(cp);
92         }
93 }
94
95 void
96 BasisSetRelease(BasisSet *bset)
97 {
98         int i;
99         if (bset == NULL)
100                 return;
101         if (bset->shells != NULL)
102                 free(bset->shells);
103         if (bset->priminfos != NULL)
104                 free(bset->priminfos);
105         if (bset->mo != NULL)
106                 free(bset->mo);
107         if (bset->cns != NULL)
108                 free(bset->cns);
109         if (bset->moenergies != NULL)
110                 free(bset->moenergies);
111         if (bset->scfdensities != NULL)
112                 free(bset->scfdensities);
113         if (bset->pos != NULL)
114                 free(bset->pos);
115         if (bset->nuccharges != NULL)
116                 free(bset->nuccharges);
117         if (bset->cubes != NULL) {
118                 for (i = 0; i < bset->ncubes; i++) {
119                         CubeRelease(bset->cubes[i]);
120                 }
121                 free(bset->cubes);
122         }
123         free(bset);
124 }
125
126 #pragma mark ====== Accessor types ======
127
128 MolEnumerable *
129 MolEnumerableNew(Molecule *mol, int kind)
130 {
131         MolEnumerable *mseq = (MolEnumerable *)calloc(sizeof(MolEnumerable), 1);
132         if (mseq != NULL) {
133                 mseq->mol = MoleculeRetain(mol);
134                 mseq->kind = kind;
135         }
136         return mseq;
137 }
138
139 void
140 MolEnumerableRelease(MolEnumerable *mseq)
141 {
142         if (mseq != NULL) {
143                 MoleculeRelease(mseq->mol);
144                 free(mseq);
145         }
146 }
147
148 AtomRef *
149 AtomRefNew(Molecule *mol, int idx)
150 {
151         AtomRef *aref = (AtomRef *)calloc(sizeof(AtomRef), 1);
152         if (aref != NULL) {
153                 aref->mol = MoleculeRetain(mol);
154                 aref->idx = idx;
155         }
156         return aref;
157 }
158
159 void
160 AtomRefRelease(AtomRef *aref)
161 {
162         if (aref != NULL) {
163                 MoleculeRelease(aref->mol);
164                 free(aref);
165         }
166 }
167
168 #pragma mark ====== Creation of molecules ======
169
170 Molecule *
171 MoleculeNew(void)
172 {
173         char name[40];
174         Molecule *mp = (Molecule *)calloc(sizeof(Molecule), 1);
175         if (mp == NULL)
176                 Panic("Cannot allocate new molecule record");
177         snprintf(name, sizeof name, "Untitled %d", sMoleculeUntitledCount++);
178         ObjectInit((Object *)mp, (Object **)&sMoleculeRoot, name);
179         return mp;
180 }
181
182 Molecule *
183 MoleculeNewWithName(const char *name)
184 {
185         Molecule *mp = MoleculeNew();
186         MoleculeSetName(mp, name);
187         return mp;
188 }
189
190 Molecule *
191 MoleculeInitWithAtoms(Molecule *mp, const Atom *atoms, int natoms)
192 {
193         int i;
194         if (mp == NULL)
195                 mp = MoleculeNew();
196         if (natoms == 0)
197                 return mp;
198         if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
199                 Panic("Cannot allocate memory for atoms");
200         for (i = 0; i < natoms; i++)
201                 AtomDuplicate(mp->atoms + i, atoms + i);
202         mp->nframes = -1;  /*  Should be recalculated later  */
203         return mp;
204 }
205
206 Molecule *
207 MoleculeInitWithMolecule(Molecule *mp2, const Molecule *mp)
208 {
209         MoleculeInitWithAtoms(mp2, mp->atoms, mp->natoms);
210         if (mp->nbonds > 0) {
211                 if (NewArray(&mp2->bonds, &mp2->nbonds, sizeof(Int)*2, mp->nbonds) == NULL)
212                         goto error;
213                 memmove(mp2->bonds, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
214         }
215         if (mp->nangles > 0) {
216                 if (NewArray(&mp2->angles, &mp2->nangles, sizeof(Int)*3, mp->nangles) == NULL)
217                         goto error;
218                 memmove(mp2->angles, mp->angles, sizeof(Int) * 3 * mp->nangles);
219         }
220         if (mp->ndihedrals > 0) {
221                 if (NewArray(&mp2->dihedrals, &mp2->ndihedrals, sizeof(Int)*4, mp->ndihedrals) == NULL)
222                         goto error;
223                 memmove(mp2->dihedrals, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
224         }
225         if (mp->nimpropers > 0) {
226                 if (NewArray(&mp2->impropers, &mp2->nimpropers, sizeof(Int)*4, mp->nimpropers) == NULL)
227                         goto error;
228                 memmove(mp2->impropers, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
229         }
230         if (mp->nresidues > 0) {
231                 if (NewArray(&mp2->residues, &mp2->nresidues, sizeof(mp->residues[0]), mp->nresidues) == NULL)
232                         goto error;
233                 memmove(mp2->residues, mp->residues, sizeof(mp->residues[0]) * mp->nresidues);
234         }
235         if (mp->cell != NULL) {
236                 mp2->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
237                 memmove(mp2->cell, mp->cell, sizeof(XtalCell));
238         }
239         if (mp->nsyms > 0) {
240                 mp2->nsyms = mp->nsyms;
241                 mp2->syms = (Transform *)calloc(sizeof(Transform), mp2->nsyms);
242                 memmove(mp2->syms, mp->syms, sizeof(Transform) * mp2->nsyms);
243         }
244         if (mp->par != NULL)
245                 mp2->par = ParameterDuplicate(mp->par);
246         if (mp->arena != NULL) {
247                 md_arena_new(mp2);
248                 md_arena_init_from_arena(mp2->arena, mp->arena);
249         }
250         
251         return mp2;
252   error:
253         Panic("Cannot allocate memory for duplicate molecule");
254         return NULL;  /*  Not reached  */
255 }
256
257 /*  Assign a unique name to this parameter record  */
258 void
259 MoleculeSetName(Molecule *mp, const char *name)
260 {
261         ObjectSetName((Object *)mp, name, (Object *)sMoleculeRoot);
262 }
263
264 const char *
265 MoleculeGetName(Molecule *mp)
266 {
267         return ObjectGetName((Object *)mp);
268 }
269
270 Molecule *
271 MoleculeWithName(const char *name)
272 {
273         return (Molecule *)ObjectWithName(name, (Object *)sMoleculeRoot);
274 }
275
276 void
277 MoleculeSetPath(Molecule *mol, const char *fname)
278 {
279         char *buf, *cwd;
280         if (mol == NULL || fname == NULL)
281                 return;
282         if (mol->path != NULL)
283                 free((void *)(mol->path));
284         if (fname[0] == '/' || (isalpha(fname[0]) && fname[1] == ':')) {
285                 /*  Full path  */
286                 mol->path = strdup(fname);
287                 return;
288         }
289         cwd = getcwd(NULL, 0);
290         asprintf(&buf, "%s/%s", cwd, fname);
291         free(cwd);
292         mol->path = buf;
293 }
294
295 const char *
296 MoleculeGetPath(Molecule *mol)
297 {
298         if (mol == NULL)
299                 return NULL;
300         return mol->path;
301 }
302
303 Molecule *
304 MoleculeRetain(Molecule *mp)
305 {
306         ObjectIncrRefCount((Object *)mp);
307         return mp;
308 }
309
310 void
311 MoleculeClear(Molecule *mp)
312 {
313         if (mp == NULL)
314                 return;
315         if (mp->arena != NULL) {
316                 md_arena_set_molecule(mp->arena, NULL);
317                 mp->arena = NULL;
318         }
319         if (mp->par != NULL) {
320                 ParameterRelease(mp->par);
321                 mp->par = NULL;
322         }
323         if (mp->bset != NULL) {
324                 BasisSetRelease(mp->bset);
325                 mp->bset = NULL;
326         }
327         if (mp->atoms != NULL) {
328                 int i;
329                 for (i = 0; i < mp->natoms; i++)
330                         AtomClean(mp->atoms + i);
331                 free(mp->atoms);
332                 mp->atoms = NULL;
333                 mp->natoms = 0;
334         }
335         if (mp->bonds != NULL) {
336                 free(mp->bonds);
337                 mp->bonds = NULL;
338                 mp->nbonds = 0;
339         }
340         if (mp->angles != NULL) {
341                 free(mp->angles);
342                 mp->angles = NULL;
343                 mp->nangles = 0;
344         }
345         if (mp->dihedrals != NULL) {
346                 free(mp->dihedrals);
347                 mp->dihedrals = NULL;
348                 mp->ndihedrals = 0;
349         }
350         if (mp->impropers != NULL) {
351                 free(mp->impropers);
352                 mp->impropers = NULL;
353                 mp->nimpropers = 0;
354         }
355         if (mp->residues != NULL) {
356                 free(mp->residues);
357                 mp->residues = NULL;
358                 mp->nresidues = 0;
359         }
360         if (mp->elpots != NULL) {
361                 free(mp->elpots);
362                 mp->elpots = NULL;
363                 mp->nelpots = 0;
364         }
365         if (mp->path != NULL) {
366                 free((void *)mp->path);
367                 mp->path = NULL;
368         }
369 }
370
371 void
372 MoleculeRelease(Molecule *mp)
373 {
374         if (mp == NULL)
375                 return;
376         if (ObjectDecrRefCount((Object *)mp) == 0) {
377                 MoleculeClear(mp);
378                 ObjectDealloc((Object *)mp, (Object **)&sMoleculeRoot);
379         }
380 }
381
382 void
383 MoleculeExchange(Molecule *mp1, Molecule *mp2)
384 {
385         Molecule mp_temp;
386         struct MainView *mview1, *mview2;
387         struct MDArena *arena1, *arena2;
388         /*  mview and arena must be kept as they are  */
389         mview1 = mp1->mview;
390         mview2 = mp2->mview;
391         arena1 = mp1->arena;
392         arena2 = mp2->arena;
393         /*  'natoms' is the first member to be copied  */
394         int ofs = offsetof(Molecule, natoms);
395         memmove((char *)(&mp_temp) + ofs, (char *)mp1 + ofs, sizeof(Molecule) - ofs);
396         memmove((char *)mp1 + ofs, (char *)mp2 + ofs, sizeof(Molecule) - ofs);
397         memmove((char *)mp2 + ofs, (char *)(&mp_temp) + ofs, sizeof(Molecule) - ofs);
398         mp1->arena = arena1;
399         mp2->arena = arena2;
400         mp1->mview = mview1;
401         mp2->mview = mview2;
402 /*      if (mp1->arena != NULL && mp1->arena->mol == mp2)
403                 mp1->arena->mol = mp1;
404         if (mp1->arena != NULL && mp1->arena->xmol == mp2)
405                 mp1->arena->xmol = mp1;
406         if (mp2->arena != NULL && mp2->arena->mol == mp1)
407                 mp2->arena->mol = mp2;
408         if (mp2->arena != NULL && mp2->arena->xmol == mp1)
409                 mp2->arena->xmol = mp2; */
410 }
411
412 #pragma mark ====== Mutex ======
413
414 void
415 MoleculeLock(Molecule *mol)
416 {
417         if (mol == NULL || mol->mutex == NULL)
418                 return;
419         MoleculeCallback_lockMutex(mol->mutex);
420 }
421
422 void
423 MoleculeUnlock(Molecule *mol)
424 {
425         if (mol == NULL || mol->mutex == NULL)
426                 return;
427         MoleculeCallback_unlockMutex(mol->mutex);
428 }
429
430 #pragma mark ====== Modify count ======
431
432 void
433 MoleculeIncrementModifyCount(Molecule *mp)
434 {
435         if (mp != NULL) {
436                 if (++(mp->modifyCount) == 1)
437                         MoleculeCallback_notifyModification(mp, 0);
438         /*      fprintf(stderr, "MoleculeIncrementModifyCount: %d\n", mp->modifyCount); */
439         }
440 }
441
442 void
443 MoleculeClearModifyCount(Molecule *mp)
444 {
445         if (mp != NULL) {
446                 mp->modifyCount = 0;
447         /*      fprintf(stderr, "MoleculeClearModifyCount: %d\n", mp->modifyCount); */
448         }
449 }
450
451 #pragma mark ====== File handling functions ======
452
453 static const char *
454 guessMoleculeType(const char *fname)
455 {
456         char buf[1024], *p;
457         FILE *fp;
458         const char *retval = NULL;
459         fp = fopen(fname, "rb");
460         if (fp != NULL) {
461                 memset(buf, 0, sizeof buf);
462                 if (fread(buf, 1, sizeof buf - 1, fp) > 0) {
463                         if (strncmp(buf, "PSF", 3) == 0)
464                                 retval = "psf";
465                         else if (((p = strstr(buf, "ATOM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r'))
466                         || ((p = strstr(buf, "HETATM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r')))
467                                 retval = "pdb";
468                         else
469                                 retval = "???";  /*  unknown  */
470                 }
471                 fclose(fp);
472         }
473         return retval;
474 }
475
476 static int
477 guessElement(Atom *ap)
478 {
479         int atomicNumber = -1;
480         if (ap->atomicNumber > 0)
481                 atomicNumber = ap->atomicNumber;
482         else {
483                 atomicNumber = GuessAtomicNumber(ap->element, ap->weight);
484                 if (atomicNumber <= 0 && ap->aname[0] != 0)
485                         atomicNumber = GuessAtomicNumber(ap->aname, ap->weight);
486         }
487         if (atomicNumber >= 0) {
488                 ap->atomicNumber = atomicNumber;
489                 if (ap->weight <= 0)
490                         ap->weight = WeightForAtomicNumber(atomicNumber);
491                 if (ap->element[0] == 0)
492                         ElementToString(atomicNumber, ap->element);
493         }
494         return atomicNumber;
495 }
496
497 static int
498 sReadLineWithInterrupt(char *buf, int size, FILE *stream, int *lineNumber)
499 {
500         static int lastLineNumber = 0;
501         if (lineNumber != NULL) {
502                 if (*lineNumber == 0)
503                         lastLineNumber = 0;
504                 else if (*lineNumber >= lastLineNumber + 1000) {
505                         if (MyAppCallback_checkInterrupt() != 0)
506                                 return -1;  /*  User interrupt  */
507                         lastLineNumber = *lineNumber;
508                 }
509         }
510         return ReadLine(buf, size, stream, lineNumber);
511 }
512
513 int
514 MoleculeLoadFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
515 {
516         int retval;
517         if (ftype == NULL || *ftype == 0) {
518                 const char *cp;
519                 cp = strrchr(fname, '.');
520                 if (cp != NULL)
521                         ftype = cp + 1;
522                 else {
523                         cp = guessMoleculeType(fname);
524                         if (strcmp(cp, "???") != 0)
525                                 ftype = cp;
526                 }
527         }
528         if (strcasecmp(ftype, "psf") == 0) {
529                 retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
530         } else if (strcasecmp(ftype, "pdb") == 0) {
531                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
532         } else if (strcasecmp(ftype, "tep") == 0) {
533                 retval = MoleculeLoadTepFile(mp, fname, errbuf, errbufsize);
534         } else if (strcasecmp(ftype, "res") == 0 || strcasecmp(ftype, "ins") == 0) {
535                 retval = MoleculeLoadShelxFile(mp, fname, errbuf, errbufsize);
536         } else if (strcasecmp(ftype, "fchk") == 0 || strcasecmp(ftype, "fch") == 0) {
537                 retval = MoleculeLoadGaussianFchkFile(mp, fname, errbuf, errbufsize);
538         } else {
539                 snprintf(errbuf, errbufsize, "Unknown format %s", ftype);
540                 return 1;
541         }
542 /*      if (retval != 0) {
543                 retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
544         } */
545         if (retval == 0)
546                 MoleculeSetPath(mp, fname);
547         return retval;
548 }
549
550 int
551 MoleculeLoadMbsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
552 {
553         FILE *fp;
554         char buf[1024];
555         int i, j, k, n, err, fn, nframes;
556         int lineNumber;
557         int ibuf[12];
558         Int iibuf[4];
559         double dbuf[12];
560         int mview_ibuf[16];
561         float mview_fbuf[8];
562         char cbuf[12][6];
563         const char **pp;
564         char *bufp, *valp, *comp;
565         Int *ip;
566         Double *dp;
567         Vector v;
568         Atom *ap;
569         const int kUndefined = -10000000;
570         err = 0;
571         if (errbuf == NULL) {
572                 errbuf = buf;
573                 errbufsize = 1024;
574         }
575         errbuf[0] = 0;
576         if (mp->natoms != 0 || mp->par != NULL || mp->arena != NULL) {
577                 snprintf(errbuf, errbufsize, "The molecule must be empty");
578                 return 1;
579         }
580         fp = fopen(fname, "rb");
581         if (fp == NULL) {
582                 snprintf(errbuf, errbufsize, "Cannot open file");
583                 return 1;
584         }
585         for (i = 0; i < 8; i++)
586                 mview_fbuf[i] = kUndefined;
587         for (i = 0; i < 16; i++)
588                 mview_ibuf[i] = kUndefined;
589         /*      flockfile(fp); */
590         lineNumber = 0;
591         fn = 0;
592         nframes = 0;
593         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
594                 if (strncmp(buf, "!:", 2) != 0)
595                         continue;   /*  Skip until section header is found  */
596                 bufp = buf;
597                 strsep(&bufp, " \t\n");
598                 if (strcmp(buf, "!:atoms") == 0) {
599                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
600                                 if (buf[0] == '!')
601                                         continue;
602                                 if (buf[0] == '\n')
603                                         break;
604                                 /* idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge */
605                                 if (sscanf(buf, "%d %4s %d %4s %4s %4s %lf %lf %4s %d %lf %lf %d", &ibuf[0], cbuf[0], &ibuf[1], cbuf[1], cbuf[2], cbuf[3], &dbuf[0], &dbuf[1], cbuf[4], &ibuf[2], &dbuf[2], &dbuf[3], &ibuf[3]) < 13) {
606                                         snprintf(errbuf, errbufsize, "line %d: coordinates cannot be read for atom %d", lineNumber, mp->natoms + 1);
607                                         goto exit;
608                                 }
609                                 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
610                                 strncpy(ap->segName, cbuf[0], 4);
611                                 ap->resSeq = ibuf[1];
612                                 strncpy(ap->resName, cbuf[1], 4);
613                                 strncpy(ap->aname, cbuf[2], 4);
614                                 ap->type = AtomTypeEncodeToUInt(cbuf[3]);
615                                 ap->charge = dbuf[0];
616                                 ap->weight = dbuf[1];
617                                 strncpy(ap->element, cbuf[4], 2);
618                                 ap->atomicNumber = ibuf[2];
619                                 ap->occupancy = dbuf[2];
620                                 ap->tempFactor = dbuf[3];
621                                 ap->intCharge = ibuf[3];
622                         }
623                         continue;
624                 } else if (strcmp(buf, "!:atoms_symop") == 0) {
625                         i = 0;
626                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
627                                 if (buf[0] == '!')
628                                         continue;
629                                 if (buf[0] == '\n')
630                                         break;
631                                 /* idx symop symbase */
632                                 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
633                                         snprintf(errbuf, errbufsize, "line %d: symmetry operations cannot be read for atom %d", lineNumber, i + 1);
634                                         goto exit;
635                                 }
636                                 if (i >= mp->natoms) {
637                                         snprintf(errbuf, errbufsize, "line %d: too many atomic symmetry info\n", lineNumber);
638                                         goto exit;
639                                 }
640                                 ap = ATOM_AT_INDEX(mp->atoms, i);
641                                 ap->symop.sym = ibuf[1] / 1000000;
642                                 ap->symop.dx = (ibuf[1] % 1000000) / 10000;
643                                 ap->symop.dy = (ibuf[1] % 10000) / 100;
644                                 ap->symop.dz = ibuf[1] % 100;
645                                 ap->symbase = ibuf[2];
646                                 i++;
647                         }
648                         continue;
649                 } else if (strcmp(buf, "!:atoms_fix") == 0) {
650                         i = 0;
651                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
652                                 if (buf[0] == '!')
653                                         continue;
654                                 if (buf[0] == '\n')
655                                         break;
656                                 /* idx fix_force fix_pos */
657                                 if (sscanf(buf, "%d %lf %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]) < 5) {
658                                         snprintf(errbuf, errbufsize, "line %d: fix atom info cannot be read for atom %d", lineNumber, i + 1);
659                                         goto exit;
660                                 }
661                                 if (i >= mp->natoms) {
662                                         snprintf(errbuf, errbufsize, "line %d: too many fix atom info\n", lineNumber);
663                                         goto exit;
664                                 }
665                                 ap = ATOM_AT_INDEX(mp->atoms, i);
666                                 ap->fix_force = dbuf[0];
667                                 ap->fix_pos.x = dbuf[1];
668                                 ap->fix_pos.y = dbuf[2];
669                                 ap->fix_pos.z = dbuf[3];
670                                 i++;
671                         }
672                         continue;
673                 } else if (strcmp(buf, "!:mm_exclude") == 0) {
674                         i = 0;
675                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
676                                 if (buf[0] == '!')
677                                         continue;
678                                 if (buf[0] == '\n')
679                                         break;
680                                 /* idx mm_exclude periodic_exclude */
681                                 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
682                                         snprintf(errbuf, errbufsize, "line %d: mm_exclude flags cannot be read for atom %d", lineNumber, i + 1);
683                                         goto exit;
684                                 }
685                                 if (i >= mp->natoms) {
686                                         snprintf(errbuf, errbufsize, "line %d: too many mm_exclude flags\n", lineNumber);
687                                         goto exit;
688                                 }
689                                 ap = ATOM_AT_INDEX(mp->atoms, i);
690                                 ap->mm_exclude = (ibuf[1] != 0);
691                                 ap->periodic_exclude = (ibuf[2] != 0);
692                                 i++;
693                         }
694                         continue;
695                 } else if (strcmp(buf, "!:positions") == 0) {
696                         i = 0;
697                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
698                                 if (buf[0] == '!')
699                                         continue;
700                                 if (buf[0] == '\n')
701                                         break;
702                                 /* idx x y z */
703                                 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
704                                         snprintf(errbuf, errbufsize, "line %d: atom position cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
705                                         goto exit;
706                                 }
707                                 if (i >= mp->natoms) {
708                                         snprintf(errbuf, errbufsize, "line %d: too many atom position records\n", lineNumber);
709                                         goto exit;
710                                 }
711                                 v.x = dbuf[0];
712                                 v.y = dbuf[1];
713                                 v.z = dbuf[2];
714                                 ap = ATOM_AT_INDEX(mp->atoms, i);
715                                 if (nframes > 0) {
716                                         AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes, &v);
717                                         if (nframes == 1)
718                                                 ap->frames[0] = ap->r;
719                                 }
720                                 ap->r = v;
721                                 i++;
722                         }
723                         nframes++;
724                         if (nframes >= 2) {
725                                 mp->nframes = nframes;
726                                 mp->cframe = nframes - 1;
727                         } else {
728                                 mp->nframes = mp->cframe = 0;
729                         }
730                         continue;
731                 } else if (strcmp(buf, "!:bonds") == 0) {
732                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
733                                 if (buf[0] == '!')
734                                         continue;
735                                 if (buf[0] == '\n')
736                                         break;
737                                 /* from1 to1 from2 to2 from3 to3 from4 to4 */ 
738                                 i = sscanf(buf, "%d %d %d %d %d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3], &ibuf[4], &ibuf[5], &ibuf[6], &ibuf[7]);
739                                 if (i < 2 || i % 2 != 0) {
740                                         snprintf(errbuf, errbufsize, "line %d: bad bond format", lineNumber);
741                                         goto exit;
742                                 }
743                                 for (j = 0; j < i; j += 2) {
744                                         iibuf[0] = ibuf[j];
745                                         iibuf[1] = ibuf[j + 1];
746                                         if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[0] == iibuf[1]) {
747                                                 snprintf(errbuf, errbufsize, "line %d: bad bond format", lineNumber);
748                                                 goto exit;
749                                         }
750                                         AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, iibuf);
751                                         for (k = 0; k < 2; k++) {
752                                                 ap = ATOM_AT_INDEX(mp->atoms, iibuf[k]);
753                                                 for (n = 0; n < ap->nconnects; n++) {
754                                                         if (ap->connects[n] == iibuf[1 - k])
755                                                                 break;
756                                                 }
757                                                 if (n >= ap->nconnects) {
758                                                         if (ap->nconnects >= ATOMS_MAX_CONNECTS - 1) {
759                                                                 snprintf(errbuf, errbufsize, "line %d: too many bonds on atom %d", lineNumber, iibuf[k]);
760                                                                 goto exit;
761                                                         }
762                                                         ap->connects[ap->nconnects++] = iibuf[1 - k];
763                                                 }
764                                         }
765                                 }
766                         }
767                         continue;
768                 } else if (strcmp(buf, "!:angles") == 0) {
769                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
770                                 if (buf[0] == '!')
771                                         continue;
772                                 if (buf[0] == '\n')
773                                         break;
774                                 /* a1 b1 c1 a2 b2 c2 a3 b3 c3 */ 
775                                 i = sscanf(buf, "%d %d %d %d %d %d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3], &ibuf[4], &ibuf[5], &ibuf[6], &ibuf[7], &ibuf[8]);
776                                 if (i == 0 || i % 3 != 0) {
777                                         snprintf(errbuf, errbufsize, "line %d: bad angle format", lineNumber);
778                                         goto exit;
779                                 }
780                                 for (j = 0; j < i; j += 3) {
781                                         iibuf[0] = ibuf[j];
782                                         iibuf[1] = ibuf[j + 1];
783                                         iibuf[2] = ibuf[j + 2];
784                                         if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[2] < 0 || iibuf[2] >= mp->natoms || iibuf[0] == iibuf[1] || iibuf[1] == iibuf[2]) {
785                                                 snprintf(errbuf, errbufsize, "line %d: bad angle format", lineNumber);
786                                                 goto exit;
787                                         }
788                                         AssignArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, mp->nangles, iibuf);
789                                 }
790                         }
791                         continue;
792                 } else if (strcmp(buf, "!:dihedrals") == 0) {
793                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
794                                 if (buf[0] == '!')
795                                         continue;
796                                 if (buf[0] == '\n')
797                                         break;
798                                 /* a1 b1 c1 d1 a2 b2 c2 d2 */ 
799                                 i = sscanf(buf, "%d %d %d %d %d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3], &ibuf[4], &ibuf[5], &ibuf[6], &ibuf[7]);
800                                 if (i == 0 || i % 4 != 0) {
801                                         snprintf(errbuf, errbufsize, "line %d: bad dihedral format", lineNumber);
802                                         goto exit;
803                                 }
804                                 for (j = 0; j < i; j += 4) {
805                                         iibuf[0] = ibuf[j];
806                                         iibuf[1] = ibuf[j + 1];
807                                         iibuf[2] = ibuf[j + 2];
808                                         iibuf[3] = ibuf[j + 3];
809                                         if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[2] < 0 || iibuf[2] >= mp->natoms || iibuf[3] < 0 || iibuf[3] >= mp->natoms || iibuf[0] == iibuf[1] || iibuf[1] == iibuf[2] || iibuf[2] == iibuf[3] || iibuf[0] == iibuf[2] || iibuf[1] == iibuf[3] || iibuf[0] == iibuf[3]) {
810                                                 snprintf(errbuf, errbufsize, "line %d: bad dihedral format", lineNumber);
811                                                 goto exit;
812                                         }
813                                         AssignArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, mp->ndihedrals, iibuf);
814                                 }
815                         }
816                         continue;
817                 } else if (strcmp(buf, "!:impropers") == 0) {
818                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
819                                 if (buf[0] == '!')
820                                         continue;
821                                 if (buf[0] == '\n')
822                                         break;
823                                 /* a1 b1 c1 d1 a2 b2 c2 d2 */ 
824                                 i = sscanf(buf, "%d %d %d %d %d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3], &ibuf[4], &ibuf[5], &ibuf[6], &ibuf[7]);
825                                 if (i == 0 || i % 4 != 0) {
826                                         snprintf(errbuf, errbufsize, "line %d: bad improper format", lineNumber);
827                                         goto exit;
828                                 }
829                                 for (j = 0; j < i; j += 4) {
830                                         iibuf[0] = ibuf[j];
831                                         iibuf[1] = ibuf[j + 1];
832                                         iibuf[2] = ibuf[j + 2];
833                                         iibuf[3] = ibuf[j + 3];
834                                         if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[2] < 0 || iibuf[2] >= mp->natoms || iibuf[3] < 0 || iibuf[3] >= mp->natoms || iibuf[0] == iibuf[1] || iibuf[1] == iibuf[2] || iibuf[2] == iibuf[3] || iibuf[0] == iibuf[2] || iibuf[1] == iibuf[3] || iibuf[0] == iibuf[3]) {
835                                                 snprintf(errbuf, errbufsize, "line %d: bad improper format", lineNumber);
836                                                 goto exit;
837                                         }
838                                         AssignArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, mp->nimpropers, iibuf);
839                                 }
840                         }
841                         continue;
842                 } else if (strcmp(buf, "!:xtalcell") == 0 && mp->cell == NULL) {
843                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
844                                 if (buf[0] == '!')
845                                         continue;
846                                 if (buf[0] == '\n')
847                                         break;
848                                 /* a b c alpha beta gamma */ 
849                                 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
850                                         snprintf(errbuf, errbufsize, "line %d: bad xtalcell format", lineNumber);
851                                         goto exit;
852                                 }
853                                 MoleculeSetCell(mp, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], 0);
854                         }
855                         continue;
856                 } else if (strcmp(buf, "!:symmetry_operations") == 0) {
857                         i = 0;
858                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
859                                 Transform tr;
860                                 if (buf[0] == '!')
861                                         continue;
862                                 if (buf[0] == '\n')
863                                         break;
864                                 /* a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3 */
865                                 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
866                                         snprintf(errbuf, errbufsize, "line %d: bad symmetry_operation format", lineNumber);
867                                         goto exit;
868                                 }
869                                 tr[i * 3] = dbuf[0];
870                                 tr[i * 3 + 1] = dbuf[1];
871                                 tr[i * 3 + 2] = dbuf[2];
872                                 i++;
873                                 if (i == 4) {
874                                         AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
875                                         i = 0;
876                                 }
877                         }
878                         continue;
879                 } else if (strcmp(buf, "!:periodic_box") == 0) {
880                         Vector vs[5];
881                         i = 0;
882                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
883                                 if (buf[0] == '!')
884                                         continue;
885                                 if (buf[0] == '\n')
886                                         break;
887                                 /* a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3 */
888                                 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
889                                         snprintf(errbuf, errbufsize, "line %d: bad symmetry_operation format", lineNumber);
890                                         goto exit;
891                                 }
892                                 vs[i].x = dbuf[0];
893                                 vs[i].y = dbuf[1];
894                                 vs[i].z = dbuf[2];
895                                 i++;
896                                 if (i == 5) {
897                                 /*      j = sscanf(buf, "%d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3]); */
898                                         cbuf[0][0] = dbuf[0];
899                                         cbuf[0][1] = dbuf[1];
900                                         cbuf[0][2] = dbuf[2];
901                                         MoleculeSetPeriodicBox(mp, vs, vs + 1, vs + 2, vs + 3, cbuf[0]);
902                                 /*      if (j == 4)
903                                                 mp->is_xtal_coord = (ibuf[3] != 0); */
904                                 }
905                         }
906                         continue;
907                 } else if (strcmp(buf, "!:md_parameters") == 0) {
908                         MDArena *arena;
909                         if (mp->arena == NULL)
910                                 mp->arena = md_arena_new(NULL);
911                         arena = mp->arena;
912                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
913                                 if (buf[0] == '!')
914                                         continue;
915                                 if (buf[0] == '\n')
916                                         break;
917                                 bufp = buf;
918                                 comp = strsep(&bufp, " \t");
919                                 if (bufp != NULL) {
920                                         while (*bufp == ' ' || *bufp == '\t')
921                                                 bufp++;
922                                         valp = strsep(&bufp, "\n");
923                                 } else valp = NULL;
924                                 if (strcmp(comp, "alchem_flags") == 0) {
925                                         j = (valp == NULL ? 0 : atoi(valp));
926                                         if (j > 0) {
927                                                 valp = (char *)malloc(j);
928                                                 i = 0;
929                                                 while ((k = fgetc(fp)) >= 0) {
930                                                         ungetc(k, fp);
931                                                         if (k < '0' || k > '9') {
932                                                                 snprintf(errbuf, errbufsize, "line %d: too few flags in alchem_flags block", lineNumber + 1);
933                                                                 free(valp);
934                                                                 goto exit;
935                                                         }
936                                                         ReadLine(buf, sizeof buf, fp, &lineNumber);
937                                                         bufp = buf;
938                                                         while (*bufp != 0) {
939                                                                 if (*bufp >= '0' && *bufp <= '2') {
940                                                                         if (i >= j) {
941                                                                                 snprintf(errbuf, errbufsize, "line %d: too many flags in alchem_flags block", lineNumber);
942                                                                                 free(valp);
943                                                                                 goto exit;
944                                                                         }
945                                                                         valp[i++] = *bufp - '0';
946                                                                 } else if (*bufp != ' ' && *bufp != '\t' && *bufp != '\n') {
947                                                                         snprintf(errbuf, errbufsize, "line %d: strange character (0x%02x) in alchem_flags block", lineNumber, (int)*bufp);
948                                                                         free(valp);
949                                                                         goto exit;
950                                                                 }
951                                                                 bufp++;
952                                                         }
953                                                         if (i == j)
954                                                                 break;
955                                                 }
956                                                 md_set_alchemical_flags(arena, j, valp);
957                                                 free(valp);
958                                         }
959                                         continue;
960                                 }
961                                 /*  In the following, the redundant "!= NULL" is to suppress suprious warning  */
962                                 if ((strcmp(comp, "log_file") == 0 && (pp = &arena->log_result_name) != NULL)
963                                         || (strcmp(comp, "coord_file") == 0 && (pp = &arena->coord_result_name) != NULL)
964                                         || (strcmp(comp, "vel_file") == 0 && (pp = &arena->vel_result_name) != NULL)
965                                         || (strcmp(comp, "force_file") == 0 && (pp = &arena->force_result_name) != NULL)
966                                         || (strcmp(comp, "debug_file") == 0 && (pp = &arena->debug_result_name) != NULL)) {
967                                         if (*valp == 0 || strstr(valp, "(null)") == valp)
968                                                 *pp = NULL;
969                                         else {
970                                                 valp = strdup(valp);
971                                                 if (valp != NULL) {
972                                                         char *valp1 = strchr(valp, '\n');
973                                                         if (valp1 != NULL)
974                                                                 *valp1 = 0;
975                                                 }
976                                                 *pp = valp;
977                                         }
978                                 } else if ((strcmp(comp, "debug_output_level") == 0 && (ip = &arena->debug_output_level) != NULL)
979                                                    || (strcmp(comp, "coord_output_freq") == 0 && (ip = &arena->coord_output_freq) != NULL)
980                                                    || (strcmp(comp, "energy_output_freq") == 0 && (ip = &arena->energy_output_freq) != NULL)
981                                                    || (strcmp(comp, "coord_frame") == 0 && (ip = &arena->coord_result_frame) != NULL)
982                                                    || (strcmp(comp, "andersen_freq") == 0 && (ip = &arena->andersen_thermo_freq) != NULL)
983                                                    || (strcmp(comp, "random_seed") == 0 && (ip = &arena->random_seed) != NULL)
984                                                    || (strcmp(comp, "use_xplor_shift") == 0 && (ip = &arena->use_xplor_shift) != NULL)
985                                                    || (strcmp(comp, "relocate_center") == 0 && (ip = &arena->relocate_center) != NULL)
986                                                    || (strcmp(comp, "surface_potential_freq") == 0 && (ip = &arena->surface_potential_freq) != NULL)
987                                                    || (strcmp(comp, "use_graphite") == 0 && (ip = &arena->use_graphite) != NULL)) {
988                                         *ip = (valp == NULL ? 0 : atoi(valp));
989                                 } else if ((strcmp(comp, "timestep") == 0 && (dp = &arena->timestep) != NULL)
990                                                    || (strcmp(comp, "cutoff") == 0 && (dp = &arena->cutoff) != NULL)
991                                                    || (strcmp(comp, "electro_cutoff") == 0 && (dp = &arena->electro_cutoff) != NULL)
992                                                    || (strcmp(comp, "pairlist_distance") == 0 && (dp = &arena->pairlist_distance) != NULL)
993                                                    || (strcmp(comp, "temperature") == 0 && (dp = &arena->temperature) != NULL)
994                                                    || (strcmp(comp, "andersen_coupling") == 0 && (dp = &arena->andersen_thermo_coupling) != NULL)
995                                                    || (strcmp(comp, "dielectric") == 0 && (dp = &arena->dielectric) != NULL)
996                                                    || (strcmp(comp, "gradient_convergence") == 0 && (dp = &arena->gradient_convergence) != NULL)
997                                                    || (strcmp(comp, "coordinate_convergence") == 0 && (dp = &arena->coordinate_convergence) != NULL)
998                                                    || (strcmp(comp, "scale14_vdw") == 0 && (dp = &arena->scale14_vdw) != NULL)
999                                                    || (strcmp(comp, "scale14_elect") == 0 && (dp = &arena->scale14_elect) != NULL)
1000                                                    || (strcmp(comp, "surface_probe_radius") == 0 && (dp = &arena->probe_radius) != NULL)
1001                                                    || (strcmp(comp, "surface_tension") == 0 && (dp = &arena->surface_tension) != NULL)
1002                                                    || (strcmp(comp, "alchemical_lambda") == 0 && (dp = &arena->alchem_lambda) != NULL)
1003                                                    || (strcmp(comp, "alchemical_delta_lambda") == 0 && (dp = &arena->alchem_dlambda) != NULL)) {
1004                                         *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1005                                 }
1006                         }
1007                         continue;
1008                 } else if (strcmp(buf, "!:pressure_control_parameters") == 0) {
1009                         MDPressureArena *pressure;
1010                         if (mp->arena == NULL)
1011                                 mp->arena = md_arena_new(mp);
1012                         if (mp->arena->pressure == NULL)
1013                                 mp->arena->pressure = pressure_new();
1014                         pressure = mp->arena->pressure;
1015                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1016                                 if (buf[0] == '!')
1017                                         continue;
1018                                 if (buf[0] == '\n')
1019                                         break;
1020                                 bufp = buf;
1021                                 comp = strsep(&bufp, " \t");
1022                                 if (bufp != NULL) {
1023                                         while (*bufp == ' ' || *bufp == '\t')
1024                                                 bufp++;
1025                                         valp = strsep(&bufp, "\n");
1026                                 } else valp = NULL;
1027                                 if (strcmp(comp, "pressure") == 0) {
1028                                         if (sscanf(valp, "%lf %lf %lf %lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5], &dbuf[6], &dbuf[7], &dbuf[8]) < 9) {
1029                                                 snprintf(errbuf, errbufsize, "line %d: bad format", lineNumber);
1030                                                 goto exit;
1031                                         }
1032                                         for (i = 0; i < 9; i++)
1033                                                 pressure->apply[i] = dbuf[i];
1034                                 } else if (strcmp(comp, "pressure_cell_flexibility") == 0) {
1035                                         if (sscanf(valp, "%lf %lf %lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5], &dbuf[6], &dbuf[7]) < 8) {
1036                                                 snprintf(errbuf, errbufsize, "line %d: bad format", lineNumber);
1037                                                 goto exit;
1038                                         }
1039                                         for (i = 0; i < 8; i++)
1040                                                 pressure->cell_flexibility[i] = dbuf[i];
1041                                 } else if ((strcmp(comp, "pressure_freq") == 0 && (ip = &pressure->freq) != NULL)) {
1042                                         *ip = (valp == NULL ? 0 : atoi(valp));
1043                                 } else if ((strcmp(comp, "pressure_coupling") == 0 && (dp = &pressure->coupling) != NULL)
1044                                                    || (strcmp(comp, "pressure_fluctuate_cell_origin") == 0 && (dp = &pressure->fluctuate_cell_origin) != NULL)
1045                                                    || (strcmp(comp, "pressure_fluctuate_cell_orientation") == 0 && (dp = &pressure->fluctuate_cell_orientation) != NULL)) {
1046                                         *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1047                                 }
1048                         }
1049                         continue;
1050                 } else if (strcmp(buf, "!:velocity") == 0) {
1051                         i = 0;
1052                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1053                                 if (buf[0] == '!')
1054                                         continue;
1055                                 if (buf[0] == '\n')
1056                                         break;
1057                                 /* idx vx vy vz */
1058                                 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1059                                         snprintf(errbuf, errbufsize, "line %d: atom velocity cannot be read for atom %d", lineNumber, i + 1);
1060                                         goto exit;
1061                                 }
1062                                 if (i >= mp->natoms) {
1063                                         snprintf(errbuf, errbufsize, "line %d: too many atom velocity records\n", lineNumber);
1064                                         goto exit;
1065                                 }
1066                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1067                                 ap->v.x = dbuf[0];
1068                                 ap->v.y = dbuf[1];
1069                                 ap->v.z = dbuf[2];
1070                                 i++;
1071                         }
1072                         continue;
1073                 } else if (strcmp(buf, "!:force") == 0) {
1074                         i = 0;
1075                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1076                                 if (buf[0] == '!')
1077                                         continue;
1078                                 if (buf[0] == '\n')
1079                                         break;
1080                                 /* idx fx fy fz */
1081                                 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1082                                         snprintf(errbuf, errbufsize, "line %d: atom force cannot be read for atom %d", lineNumber, i + 1);
1083                                         goto exit;
1084                                 }
1085                                 if (i >= mp->natoms) {
1086                                         snprintf(errbuf, errbufsize, "line %d: too many atom force records\n", lineNumber);
1087                                         goto exit;
1088                                 }
1089                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1090                                 ap->f.x = dbuf[0];
1091                                 ap->f.y = dbuf[1];
1092                                 ap->f.z = dbuf[2];
1093                                 i++;
1094                         }
1095                         continue;
1096                 } else if (strcmp(buf, "!:parameter") == 0 || strcmp(buf, "!:parameters") == 0) {
1097                         Parameter *par = mp->par;
1098                         if (par == NULL) {
1099                                 mp->par = ParameterNew();
1100                                 par = mp->par;
1101                         }
1102                         bufp = NULL;
1103                         i = 0;
1104                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1105                                 if (buf[0] == '!')
1106                                         continue;
1107                                 if (buf[0] == '\n')
1108                                         break;
1109                                 j = ParameterReadFromString(par, buf, &bufp, fname, lineNumber, 0);
1110                                 if (j < 0) {
1111                                         snprintf(errbuf, errbufsize, "%s", bufp);
1112                                         goto exit;
1113                                 }
1114                                 i += j;
1115                         }
1116                         if (bufp != NULL) {
1117                                 MyAppCallback_setConsoleColor(1);
1118                                 MyAppCallback_showScriptMessage("%s", bufp);
1119                                 MyAppCallback_setConsoleColor(0);
1120                                 free(bufp);
1121                         }
1122                         continue;
1123                 } else if (strcmp(buf, "!:trackball") == 0) {
1124                         i = 0;
1125                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1126                                 if (buf[0] == '!')
1127                                         continue;
1128                                 if (buf[0] == '\n')
1129                                         break;
1130                                 /* scale; trx try trz; theta_deg x y z */
1131                                 if ((i == 0 && sscanf(buf, "%f", &mview_fbuf[0]) < 1)
1132                                         || (i == 1 && sscanf(buf, "%f %f %f",
1133                                                                                  &mview_fbuf[1], &mview_fbuf[2], &mview_fbuf[3]) < 3)
1134                                         || (i == 2 && sscanf(buf, "%f %f %f %f",
1135                                                                                  &mview_fbuf[4], &mview_fbuf[5], &mview_fbuf[6], &mview_fbuf[7]) < 4)) {
1136                                         snprintf(errbuf, errbufsize, "line %d: bad trackball format", lineNumber);
1137                                         goto exit;
1138                                 }
1139                                 i++;
1140                         }
1141                         continue;
1142                 } else if (strcmp(buf, "!:view") == 0) {
1143                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1144                                 if (buf[0] == '!')
1145                                         continue;
1146                                 if (buf[0] == '\n')
1147                                         break;
1148                                 bufp = buf;
1149                                 comp = strsep(&bufp, " \t");
1150                                 if (bufp != NULL) {
1151                                         while (*bufp == ' ' || *bufp == '\t')
1152                                                 bufp++;
1153                                         valp = strsep(&bufp, "\n");
1154                                 } else valp = NULL;
1155                                 /*  In the following, the redundant "!= NULL" is to suppress suprious warning  */
1156                                 if ((strcmp(comp, "show_unit_cell") == 0 && (i = 1))
1157                                         || (strcmp(comp, "show_periodic_box") == 0 && (i = 2))
1158                                         || (strcmp(comp, "show_expanded_atoms") == 0 && (i = 3))
1159                                         || (strcmp(comp, "show_ellipsoids") == 0 && (i = 4))
1160                                         || (strcmp(comp, "show_hydrogens") == 0 && (i = 5))
1161                                         || (strcmp(comp, "show_dummy_atoms") == 0 && (i = 6))
1162                                         || (strcmp(comp, "show_rotation_center") == 0 && (i = 7))
1163                                         || (strcmp(comp, "show_graphite_flag") == 0 && (i = 8))
1164                                         || (strcmp(comp, "show_periodic_image_flag") == 0 && (i = 9))
1165                                         || (strcmp(comp, "show_graphite") == 0 && (i = 10))) {
1166                                         mview_ibuf[i - 1] = atoi(valp);
1167                                 } else if (strcmp(comp, "show_periodic_image") == 0) {
1168                                         sscanf(valp, "%d %d %d %d %d %d",
1169                                                    &mview_ibuf[10], &mview_ibuf[11], &mview_ibuf[12],
1170                                                    &mview_ibuf[13], &mview_ibuf[14], &mview_ibuf[15]);
1171                                 }
1172                         }
1173                         continue;
1174                 }
1175                 /*  Unknown sections are silently ignored  */
1176         }
1177
1178         MoleculeCleanUpResidueTable(mp);
1179         if (mp->arena != NULL)
1180                 md_arena_set_molecule(mp->arena, mp);
1181
1182 exit:
1183         fclose(fp);
1184         if (errbuf[0] != 0) {
1185                 /*  The content of mp may be broken, so make it empty  */
1186                 MoleculeClear(mp);
1187                 return -1;
1188         } else {
1189                 MainView *mview = mp->mview;
1190                 if (mview != NULL) {
1191                         if (mview_ibuf[0] != kUndefined)
1192                                 mview->showUnitCell = mview_ibuf[0];
1193                         if (mview_ibuf[1] != kUndefined)
1194                                 mview->showPeriodicBox = mview_ibuf[1];
1195                         if (mview_ibuf[2] != kUndefined)
1196                                 mview->showExpandedAtoms = mview_ibuf[2];
1197                         if (mview_ibuf[3] != kUndefined)
1198                                 mview->showEllipsoids = mview_ibuf[3];
1199                         if (mview_ibuf[4] != kUndefined)
1200                                 mview->showHydrogens = mview_ibuf[4];
1201                         if (mview_ibuf[5] != kUndefined)
1202                                 mview->showDummyAtoms = mview_ibuf[5];
1203                         if (mview_ibuf[6] != kUndefined)
1204                                 mview->showRotationCenter = mview_ibuf[6];
1205                         if (mview_ibuf[7] != kUndefined)
1206                                 mview->showGraphiteFlag = mview_ibuf[7];
1207                         if (mview_ibuf[8] != kUndefined)
1208                                 mview->showPeriodicImageFlag = mview_ibuf[8];
1209                         if (mview_ibuf[9] != kUndefined)
1210                                 mview->showGraphite = mview_ibuf[9];
1211                         for (i = 0; i < 6; i++) {
1212                                 if (mview_ibuf[10 + i] != kUndefined)
1213                                         mview->showPeriodicImage[i] = mview_ibuf[10 + i];
1214                         }
1215                         if (mview->track != NULL) {
1216                                 if (mview_fbuf[0] != kUndefined)
1217                                         TrackballSetScale(mview->track, mview_fbuf[0]);
1218                                 if (mview_fbuf[1] != kUndefined)
1219                                         TrackballSetTranslate(mview->track, mview_fbuf + 1);
1220                                 if (mview_fbuf[4] != kUndefined)
1221                                         TrackballSetRotate(mview->track, mview_fbuf + 4);
1222                         }
1223                 }
1224         }
1225         return 0;
1226 }
1227
1228 int
1229 MoleculeLoadPsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
1230 {
1231         FILE *fp;
1232         char buf[1024];
1233         char *p;
1234         int section = -1;
1235         int i, j, err, fn;
1236         int lineNumber;
1237         Int ibuf[12];
1238         Vector *frames = NULL;
1239         Atom *ap;
1240         err = 0;
1241         if (errbuf == NULL) {
1242                 errbuf = buf;
1243                 errbufsize = 1024;
1244         }
1245         errbuf[0] = 0;
1246         if (mp == NULL)
1247                 mp = MoleculeNew();
1248         fp = fopen(fname, "rb");
1249         if (fp == NULL) {
1250                 snprintf(errbuf, errbufsize, "Cannot open file");
1251                 return 1;
1252         }
1253 /*      flockfile(fp); */
1254         lineNumber = 0;
1255         fn = 0;
1256         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1257                 if (strncmp(buf, "PSF", 3) == 0) {
1258                         section = 0;
1259                         continue;
1260                 } else {
1261                         for (p = buf; *p != 0 && isspace(*p); p++) {}
1262                         if (*p == 0) {
1263                                 section++;
1264                                 continue;
1265                         }
1266                 }
1267                 if (strstr(buf, "!COORD") != NULL) {
1268                         /*  Extended psf file with coordinates  */
1269                         if (fn > 0) {
1270                                 /*  Allocate a temporary storage for frames  */
1271                                 size_t size = sizeof(Vector) * mp->natoms * fn;
1272                                 if (frames == NULL)
1273                                         frames = (Vector *)malloc(size);
1274                                 else
1275                                         frames = (Vector *)realloc(frames, size);
1276                                 if (frames == NULL)
1277                                         goto panic;
1278                         #if 0
1279                                 if (fn == 1) {
1280                                         /*  Copy the coordinates of the first frame  */
1281                                         for (i = 0; i < mp->natoms; i++) {
1282                                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1283                                                 frames[i] = ap->r;
1284                                         }
1285                                 }
1286                                 /*  Copy the coordinates of the last frame to the newly created frame  */
1287                                 memmove(frames + sizeof(Vector) * mp->natoms * fn, frames + sizeof(Vector) * mp->natoms * (fn - 1), sizeof(Vector) * mp->natoms);
1288                         #endif
1289                         }
1290                         /*  Read coordinates  */
1291                         for (i = 0; i < mp->natoms; i++) {
1292                                 double dval[3];
1293                                 Vector r;
1294                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1295                                         err = 1;
1296                                         snprintf(errbuf, errbufsize, "line %d: premature end of file while reading coordinates (frame %d)", lineNumber, fn);
1297                                         goto exit;
1298                                 }
1299                                 if (sscanf(buf, "%lg %lg %lg", dval, dval + 1, dval + 2) != 3) {
1300                                         err = 1;
1301                                         snprintf(errbuf, errbufsize, "line %d: coordinates cannot be read for atom %d", lineNumber, i + 1);
1302                                         goto exit;
1303                                 }
1304                                 r.x = dval[0];
1305                                 r.y = dval[1];
1306                                 r.z = dval[2];
1307                                 if (fn == 0)
1308                                         ATOM_AT_INDEX(mp->atoms, i)->r = r;
1309                                 else
1310                                         frames[mp->natoms * (fn - 1) + i] = r;
1311                         }
1312                         fn++;
1313                         continue;
1314                 }
1315                 
1316                 if (section == 2) {
1317                         /*  Atoms  */
1318                         Int natoms;
1319                         ReadFormat(buf, "I8", &natoms);
1320                         if (mp->atoms != NULL)
1321                                 free(mp->atoms);
1322                         if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
1323                                 goto panic;
1324                         mp->nresidues = 0;
1325                         for (i = 0; i < natoms; i++) {
1326                                 struct {
1327                                         char segName[5], resName[4], atomName[5], atomType[3], element[3];
1328                                         Int serial;
1329                                 } w;
1330                                 memset(&w, 0, sizeof(w));
1331                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1332                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1333                                         err = 1;
1334                                         snprintf(errbuf, errbufsize, "line %d: premature end of file while reading atoms", lineNumber);
1335                                         goto exit;
1336                                 }
1337                                 ReadFormat(buf, "I8 x1 S4 I5 x1 S3 x2 S4 x1 S4 F16 F10",
1338                                         &w.serial, w.segName, &ap->resSeq, w.resName, w.atomName, 
1339                                         w.atomType, &ap->charge, &ap->weight);
1340                                 strncpy(ap->segName, w.segName, 4);
1341                                 strncpy(ap->resName, w.resName, 3);
1342                                 strncpy(ap->aname, w.atomName, 4);
1343                                 ap->type = AtomTypeEncodeToUInt(w.atomType);
1344                                 /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
1345                                 ap->atomicNumber = GuessAtomicNumber(w.atomName, ap->weight);
1346                                 ElementToString(ap->atomicNumber, w.element);
1347                                 strncpy(ap->element, w.element, 2);
1348                         /*      w.element[0] = 0;
1349                                 for (p = w.atomName; *p != 0; p++) {
1350                                         if (isalpha(*p) && *p != '_') {
1351                                                 w.element[0] = toupper(*p);
1352                                                 if (isalpha(p[1]) && p[1] != '_') {
1353                                                         w.element[1] = toupper(p[1]);
1354                                                         w.element[2] = 0;
1355                                                 } else {
1356                                                         w.element[1] = 0;
1357                                                 }
1358                                                 break;
1359                                         }
1360                                 }
1361                                 strncpy(ap->element, w.element, 2);
1362                                 ap->atomicNumber = ElementToInt(w.element); */
1363                                 if (w.resName[0] == 0)
1364                                         strncpy(ap->resName, "XXX", 3);
1365                                 if (ap->resSeq > mp->nresidues)
1366                                         mp->nresidues = ap->resSeq;
1367                         }
1368                         if (mp->residues != NULL)
1369                                 free(mp->residues);
1370                         if (NewArray(&mp->residues, &mp->nresidues, sizeof(char (*)[4]), mp->nresidues + 1) == 0)
1371                                 goto panic;
1372                         for (i = 0; i < mp->natoms; i++) {
1373                                 j = mp->atoms[i].resSeq;
1374                                 if (mp->residues[j][0] == 0)
1375                                         strncpy(mp->residues[j], mp->atoms[i].resName, 4);
1376                         }
1377                         continue;
1378                 } else if (section == 3) {
1379                         /*  Bonds  */
1380                         Int nbonds;
1381                         Int *bp;
1382                         ReadFormat(buf, "I8", &nbonds);
1383                         if (mp->bonds != NULL)
1384                                 free(mp->bonds);
1385                         if (NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, nbonds) == NULL)
1386                                 goto panic;
1387                         bp = mp->bonds;
1388                         for (i = 0; i < nbonds; i += 4) {
1389                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1390                                         snprintf(errbuf, errbufsize, "line %d: premature end of file while reading bonds", lineNumber);
1391                                         err = 1;
1392                                         goto exit;
1393                                 }
1394                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1395                                         ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1396                                 for (j = 0; j < 4 && i + j < nbonds; j++) {
1397                                         Int b1, b2;
1398                                         Atom *ap;
1399                                         b1 = ibuf[j * 2] - 1;    /* Internal atom number is 0-based */
1400                                         b2 = ibuf[j * 2 + 1] - 1;
1401                                         if (b1 < 0 || b1 >= mp->natoms || b2 < 0 || b2 >= mp->natoms) {
1402                                                 snprintf(errbuf, errbufsize, "line %d: The bond %d-%d includes non-existent atom", lineNumber, b1+1, b2+1);
1403                                                 err = 1;
1404                                                 goto exit;
1405                                         }
1406                                         *bp++ = b1;
1407                                         *bp++ = b2;
1408                                         ap = ATOM_AT_INDEX(mp->atoms, b1);
1409                                         if (ap->nconnects < ATOMS_MAX_CONNECTS)
1410                                                 ap->connects[ap->nconnects++] = b2;
1411                                         else {
1412                                                 snprintf(errbuf, errbufsize, "line %d: The atom %d has more than %d bonds", lineNumber, b1+1, ATOMS_MAX_CONNECTS);
1413                                                 err = 1;
1414                                                 goto exit;
1415                                         }
1416                                         ap = ATOM_AT_INDEX(mp->atoms, b2);
1417                                         if (ap->nconnects < ATOMS_MAX_CONNECTS)
1418                                                 ap->connects[ap->nconnects++] = b1;
1419                                         else {
1420                                                 snprintf(errbuf, errbufsize, "line %d: The atom %d has more than %d bonds", lineNumber, b2+1, ATOMS_MAX_CONNECTS);
1421                                                 err = 1;
1422                                                 goto exit;
1423                                         }
1424                                 }
1425                         }
1426                         continue;
1427                 } else if (section == 4) {
1428                         /*  Angles  */
1429                         Int nangles;
1430                         Int *gp;
1431                         ReadFormat(buf, "I8", &nangles);
1432                         if (mp->angles != NULL)
1433                                 free(mp->angles);
1434                         if (NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, nangles) == NULL)
1435                                 goto panic;
1436                         gp = mp->angles;
1437                         for (i = 0; i < nangles; i += 3) {
1438                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1439                                         snprintf(errbuf, errbufsize, "line %d: premature end of file while reading angles", lineNumber);
1440                                         err = 1;
1441                                         goto exit;
1442                                 }
1443                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1444                                         ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7, ibuf + 8);
1445                                 for (j = 0; j < 3 && i + j < nangles; j++) {
1446                                         Int a1, a2, a3;
1447                                         a1 = ibuf[j * 3] - 1;   /* Internal atom number is 0-based */
1448                                         a2 = ibuf[j * 3 + 1] - 1;
1449                                         a3 = ibuf[j * 3 + 2] - 1;
1450                                         if (a1 < 0 || a1 >= mp->natoms || a2 < 0 || a2 >= mp->natoms || a3 < 0 || a3 >= mp->natoms) {
1451                                                 snprintf(errbuf, errbufsize, "line %d: The angle %d-%d-%d includes non-existent atom", lineNumber, a1+1, a2+1, a3+1);
1452                                                 err = 1;
1453                                                 goto exit;
1454                                         }
1455                                         *gp++ = a1;
1456                                         *gp++ = a2;
1457                                         *gp++ = a3;
1458                                 }
1459                         }
1460                         continue;
1461                 } else if (section == 5 || section == 6) {
1462                         /*  Dihedrals and Impropers  */
1463                         Int ndihedrals;
1464                         Int *dp;
1465                         ReadFormat(buf, "I8", &ndihedrals);
1466                         if (section == 5) {
1467                                 if (NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, ndihedrals) == NULL)
1468                                         goto panic;
1469                                 dp = mp->dihedrals;
1470                         } else {
1471                                 if (NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, ndihedrals) == NULL)
1472                                         goto panic;
1473                                 dp = mp->impropers;
1474                         }
1475                         for (i = 0; i < ndihedrals; i += 2) {
1476                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1477                                         fclose(fp);
1478                                         snprintf(errbuf, errbufsize, "line %d: premature end of file while reading %s", lineNumber, (section == 5 ? "dihedral" : "improper"));
1479                                         err = 1;
1480                                         goto exit;
1481                                 }
1482                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3, ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1483                                 for (j = 0; j < 2 && i + j < ndihedrals; j++) {
1484                                         Int d1, d2, d3, d4;
1485                                         d1 = ibuf[j * 4] - 1;   /*  Internal atom number is 0-based  */
1486                                         d2 = ibuf[j * 4 + 1] - 1;
1487                                         d3 = ibuf[j * 4 + 2] - 1;
1488                                         d4 = ibuf[j * 4 + 3] - 1;
1489                                         if (d1 < 0 || d1 >= mp->natoms || d2 < 0 || d2 >= mp->natoms || d3 < 0 || d3 >= mp->natoms || d4 < 0 || d4 >= mp->natoms) {
1490                                                 snprintf(errbuf, errbufsize, "line %d: The %s %d-%d-%d-%d angle includes non-existent atom", lineNumber, (section == 5 ? "dihedral" : "improper"), d1+1, d2+1, d3+1, d4+1);
1491                                                 err = 1;
1492                                                 goto exit;
1493                                         }
1494                                         *dp++ = d1;
1495                                         *dp++ = d2;
1496                                         *dp++ = d3;
1497                                         *dp++ = d4;
1498                                 }
1499                         }
1500                         continue;
1501                 }
1502         }
1503         
1504         /*  Create frames for each atom if necessary  */
1505         if (fn > 1) {
1506                 for (i = 0; i < mp->natoms; i++) {
1507                         ap = ATOM_AT_INDEX(mp->atoms, i);
1508                         ap->frames = (Vector *)malloc(sizeof(Vector) * fn);
1509                         if (ap->frames == NULL)
1510                                 goto panic;
1511                         ap->nframes = fn;
1512                         for (j = 0; j < fn; j++)
1513                                 ap->frames[j] = frames[mp->natoms * j + i];
1514                 }
1515                 free(frames);
1516                 frames = NULL;
1517         }
1518
1519   exit:
1520 /*      funlockfile(fp); */
1521         fclose(fp);
1522         mp->nframes = -1;  /*  Should be recalculated later  */
1523         if (err)
1524                 return 1;
1525         else if (section == -1)
1526                 return -1;
1527         return 0;
1528   panic:
1529         Panic("low memory while reading structure file %s", fname);
1530         return 1; /* not reached */
1531 }
1532
1533 /* ("-x", "y", "-z+0.5") -> (-1,0,0,0,0,1,0,0,0,0,-1,0.5)  */
1534 static int
1535 sMoleculeSymopStringsToTransform(char **symops, Transform tr)
1536 {
1537         int i;
1538         char *symop;
1539         memset(tr, 0, sizeof(Transform));
1540         for (i = 0; i < 3; i++) {
1541                 symop = symops[i];
1542                 if (symop == NULL)
1543                         return 1;
1544                 while (*symop != 0) {
1545                         int sn = 1;
1546                         while (isspace(*symop))
1547                                 symop++;
1548                         if (*symop == 0 || *symop == '\r' || *symop == 'n')
1549                                 break;
1550                         if (*symop == '-') {
1551                                 sn = -1;
1552                                 symop++;
1553                         } else if (*symop == '+') {
1554                                 sn = 1;
1555                                 symop++;
1556                         }
1557                         while (isspace(*symop))
1558                                 symop++;
1559                         if (*symop == '.' || isdigit(*symop)) {
1560                                 /*  Numerical offset  */
1561                                 double d = strtod(symop, &symop);
1562                                 if (*symop == '/') {
1563                                         double dd = strtod(symop + 1, &symop);
1564                                         if (dd > 0)
1565                                                 d /= dd;
1566                                         else
1567                                                 return 1;  /*  Bad format  */
1568                                 }
1569                                 tr[9 + i] = d * sn;
1570                         } else if (*symop == 'x' || *symop == 'X') {
1571                                 tr[i * 3] = sn;
1572                                 symop++;
1573                         } else if (*symop == 'y' || *symop == 'Y') {
1574                                 tr[i * 3 + 1] = sn;
1575                                 symop++;
1576                         } else if (*symop == 'z' || *symop == 'Z') {
1577                                 tr[i * 3 + 2] = sn;
1578                                 symop++;
1579                         } else return 1;  /*  Bad format  */
1580                 } /* end while (*symop != 0) */
1581         }
1582         return 0;
1583 }
1584
1585 static void
1586 sMoleculeGenerateSymopWithTransform(Molecule *mp, Transform gtr, int num)
1587 {
1588         int i, j;
1589         Transform tr;
1590         if (num <= 0)
1591                 num = mp->nsyms;
1592         for (i = 0; i < num; i++) {
1593                 memmove(tr, mp->syms[i], sizeof(Transform));
1594                 TransformMul(tr, gtr, tr);
1595                 for (j = 9; j < 12; j++) {
1596                         if (tr[j] >= 1.0)
1597                                 tr[j] -= 1.0;
1598                         else if (tr[j] <= 0.0)
1599                                 tr[j] += 1.0;
1600                 }
1601                 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1602         }
1603 }
1604
1605 static char *
1606 sChomp(char *buf)
1607 {
1608         char *p = buf + strlen(buf) - 1;
1609         if (p >= buf && (*p == '\n' || *p == '\r')) {
1610                 *p = 0;
1611                 if (--p >= buf && (*p == '\n' || *p == '\r')) {
1612                         *p = 0;
1613                 }
1614         }
1615         return buf;
1616 }
1617
1618 int
1619 MoleculeLoadTepFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
1620 {
1621         FILE *fp;
1622         char buf[1024];
1623         int section = -1;
1624         int lineNumber;
1625         int cellType;
1626         Int ibuf[12];
1627         Double fbuf[12];
1628         Int *bonds, nbonds;
1629         if (errbuf == NULL) {
1630                 errbuf = buf;
1631                 errbufsize = 1024;
1632         }
1633         errbuf[0] = 0;
1634         if (mp == NULL)
1635                 mp = MoleculeNew();
1636         fp = fopen(fname, "rb");
1637         if (fp == NULL) {
1638                 snprintf(errbuf, errbufsize, "Cannot open file");
1639                 return 1;
1640         }
1641         lineNumber = 0;
1642         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1643                 if (section == -1) {
1644                         /*  Title  */
1645                         section = 0;
1646                         continue;
1647                 }
1648                 if (section == 0) {
1649                         /*  XtalCell  */
1650                         ReadFormat(buf, "I1F8F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5);
1651                         cellType = ibuf[0];
1652                         MoleculeSetCell(mp, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], 0);
1653                         section = 1;
1654                         continue;
1655                 }
1656                 if (section == 1) {
1657                         /*  Symmetry  */
1658                         Transform tr;
1659                         if (cellType == 0) {
1660                                 ReadFormat(buf, "I1F14F3F3F3F15F3F3F3F15F3F3F3", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6, fbuf+7, fbuf+8, fbuf+9, fbuf+10, fbuf+11);
1661                                 tr[0] = fbuf[1];
1662                                 tr[1] = fbuf[2];
1663                                 tr[2] = fbuf[3];
1664                                 tr[3] = fbuf[5];
1665                                 tr[4] = fbuf[6];
1666                                 tr[5] = fbuf[7];
1667                                 tr[6] = fbuf[9];
1668                                 tr[7] = fbuf[10];
1669                                 tr[8] = fbuf[11];
1670                                 tr[9] = fbuf[0];
1671                                 tr[10] = fbuf[4];
1672                                 tr[11] = fbuf[8];
1673                         } else {
1674                                 char *symops[3], *brks;
1675                                 sChomp(buf);
1676                                 memset(tr, 0, sizeof(Transform));
1677                                 ReadFormat(buf, "I1", ibuf);
1678                                 symops[0] = strtok_r(buf + 1, ", ", &brks);
1679                                 symops[1] = strtok_r(NULL, ", ", &brks);
1680                                 symops[2] = strtok_r(NULL, ", ", &brks);
1681                                 if (sMoleculeSymopStringsToTransform(symops, tr)) {
1682                                         snprintf(errbuf, errbufsize, "line %d: bad symmetry specification", lineNumber);
1683                                         return 1;
1684                                 }
1685                         }
1686                         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
1687                                 goto panic;
1688                         if (ibuf[0] != 0)
1689                                 section = 2;
1690                         continue;
1691                 }
1692                 if (section == 2) {      /*  Atoms  */
1693                         char name[8];
1694                         Atom *ap;
1695                         int atomType;
1696                         int atomIndex = mp->natoms;
1697                         ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
1698                         memset(ap, 0, gSizeOfAtomRecord);
1699                         ReadFormat(buf, "S6x3x9x9F9F9F9F9", name, fbuf, fbuf+1, fbuf+2, fbuf+3);
1700                         strncpy(ap->aname, name, 4);
1701                         ap->r.x = fbuf[0];
1702                         ap->r.y = fbuf[1];
1703                         ap->r.z = fbuf[2];
1704                         MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
1705                 /*      ap->atomicNumber = AtomNameToElement(ap->name);
1706                         ElementToString(ap->atomicNumber, ap->element); */
1707                 /*      sAtomSetElement(ap, -1, ap->name); */
1708                         guessElement(ap);
1709                         atomType = fbuf[3];
1710                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1711                                 snprintf(errbuf, errbufsize, "unexpected end of file");
1712                                 return 1;
1713                         }
1714                         ReadFormat(buf, "I1F8F9F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
1715                         atomType = fbuf[6];
1716                         if ((atomType >= 0 && atomType <= 5) || (atomType >= 8 && atomType <= 10)) { 
1717                                 /*  Anisotropic thermal parameters  */
1718                                 MoleculeSetAniso(mp, atomIndex, atomType, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[5], fbuf[4]);
1719                         }
1720                         if (ibuf[0] != 0)
1721                                 section = 3;
1722                         continue;
1723                 }
1724         }
1725         fclose(fp);
1726         MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
1727         if (nbonds > 0) {
1728                 MoleculeAddBonds(mp, nbonds, bonds);
1729                 free(bonds);
1730         }
1731         mp->nframes = -1;  /*  Should be recalculated later  */
1732         return 0;
1733   panic:
1734         Panic("low memory while reading structure file %s", fname);
1735         return -1; /* not reached */
1736 }
1737
1738 int
1739 MoleculeLoadShelxFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
1740 {
1741         FILE *fp;
1742         char buf[1024];
1743         char *p1, *p2;
1744         int n;
1745         int lineNumber;
1746         int latticeType;
1747         int currentResSeq = 0;
1748         char currentResName[6];
1749         Transform tr;
1750         int ibuf[12];
1751         float fbuf[12];
1752         Int nsfacs = 0;
1753         Int nbonds, *bonds;
1754         char (*sfacs)[4] = NULL;
1755
1756         if (errbuf == NULL) {
1757                 errbuf = buf;
1758                 errbufsize = 1024;
1759         }
1760         errbuf[0] = 0;
1761         if (mp == NULL)
1762                 mp = MoleculeNew();
1763         currentResName[0] = 0;
1764         fp = fopen(fname, "rb");
1765         if (fp == NULL) {
1766                 snprintf(errbuf, errbufsize, "Cannot open file");
1767                 return 1;
1768         }
1769         lineNumber = 0;
1770         tr[0] = tr[4] = tr[8] = 1;
1771         tr[1] = tr[2] = tr[3] = tr[5] = tr[6] = tr[7] = tr[9] = tr[10] = tr[11] = 0;
1772         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), 0, tr) == 0)
1773                 goto panic;
1774         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1775                 if (strncmp(buf, "CELL", 4) == 0) {
1776                         /*  XtalCell  */
1777                         sscanf(buf + 4, " %f %f %f %f %f %f %f", fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
1778                         MoleculeSetCell(mp, fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], fbuf[6], 0);
1779                         continue;
1780                 } else if (strncmp(buf, "SFAC", 4) == 0) {
1781                         sChomp(buf);
1782                         for (p1 = strtok_r(buf + 4, " ", &p2); p1 != NULL; p1 = strtok_r(NULL, " ", &p2)) {
1783                                 char *pp = (char *)AssignArray(&sfacs, &nsfacs, 4, nsfacs, NULL);
1784                                 if (pp == NULL)
1785                                         goto panic;
1786                                 strncpy(pp, p1, 3);
1787                                 pp[3] = 0;
1788                         }
1789                         continue;
1790                 } else if (strncmp(buf, "LATT", 4) == 0) {
1791                         sscanf(buf + 4, " %d", &latticeType);
1792                         continue;
1793                 } else if (strncmp(buf, "SYMM", 4) == 0) {
1794                         char *symops[3], *brks;
1795                         memset(tr, 0, sizeof(Transform));
1796                 //      ReadFormat(buf + 4, "I1", ibuf);
1797                         sChomp(buf);
1798                         symops[0] = strtok_r(buf + 4, ",", &brks);
1799                         symops[1] = strtok_r(NULL, ",", &brks);
1800                         symops[2] = strtok_r(NULL, ",", &brks);
1801                         if (sMoleculeSymopStringsToTransform(symops, tr)) {
1802                                 snprintf(errbuf, errbufsize, "line %d: bad symmetry specification", lineNumber);
1803                                 return 1;
1804                         }
1805                         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
1806                                 goto panic;
1807                         continue;
1808                 } else if (strncmp(buf, "RESI", 4) == 0) {
1809                         for (p1 = buf + 4; isspace(*p1); p1++);
1810                         if (isalpha(*p1)) {
1811                                 for (p2 = p1 + 1; isalnum(*p2); p2++);
1812                                 *p2 = 0;
1813                                 strncpy(currentResName, p1, 4);
1814                                 currentResName[4] = 0;
1815                                 p1 = p2 + 1;
1816                         } else currentResName[0] = 0;
1817                         sscanf(buf + 4, " %d", &currentResSeq);
1818                         continue;
1819                 } else {
1820                         /* Atom name: [A-Za-z]{1,2}[0-9]*  */
1821                         for (p1 = buf; p1 < buf + 2 && (isalpha(*p1) && *p1 != '_'); p1++);
1822                         if (p1 > buf) {
1823                                 while (isdigit(*p1))
1824                                         p1++;
1825                         }
1826                         if (p1 > buf && p1 <= buf + 4 && isspace(*p1)) {
1827                                 /*  Atom  */
1828                                 Atom *ap;
1829                                 char cont[4];
1830                                 int atomIndex = mp->natoms;
1831                                 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
1832                                 memset(ap, 0, gSizeOfAtomRecord);
1833                                 strncpy(ap->aname, buf, 4);
1834                                 n = sscanf(p1, " %d %f %f %f %f %f %f %2s", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, cont);
1835                                 if (n == 8 && strcmp(cont, "=") == 0) {
1836                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1837                                                 snprintf(errbuf, errbufsize, "line %d: unexpected end of file within the atom cards", lineNumber);
1838                                                 return 1;
1839                                         }
1840                                         sscanf(buf, " %f %f %f %f", fbuf+6, fbuf+7, fbuf+8, fbuf+9);
1841                                         n = 10;   /*  Aniso  */
1842                                 } else n = 5; /*  Iso  */
1843                                 ap->r.x = fbuf[0];
1844                                 ap->r.y = fbuf[1];
1845                                 ap->r.z = fbuf[2];
1846                                 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
1847                                 ap->occupancy = fbuf[3];
1848                                 if (ap->aname[0] != 'Q' && ibuf[0] >= 1 && ibuf[0] <= nsfacs) {
1849                                         strncpy(ap->element, sfacs[ibuf[0] - 1], 2);
1850                                         ap->element[2] = 0;
1851                                 /*      sAtomSetElement(ap, -1, sfacs[ibuf[0] - 1]); */
1852                                 /*      strncpy(ap->element, sfacs[ibuf[0] - 1], 4);
1853                                         ap->atomicNumber = ElementToInt(ap->element); */
1854                         /*      } else {
1855                                         sAtomSetElement(ap, -1, ap->name); */
1856                                 /*      ap->atomicNumber = AtomNameToElement(ap->name);
1857                                         ElementToString(ap->atomicNumber, ap->element); */
1858                                 }
1859                                 guessElement(ap);
1860                                 if (n == 5)
1861                                         ap->tempFactor = fbuf[4] * 78.9568352087147; /* 8*pi*pi */
1862                                 else
1863                                         MoleculeSetAniso(mp, atomIndex, 8, fbuf[4], fbuf[5], fbuf[6], fbuf[9], fbuf[7], fbuf[8]);
1864                                 ap->resSeq = currentResSeq;
1865                                 strncpy(ap->resName, currentResName, 4);
1866                         }
1867                         continue;
1868                 }
1869         }
1870         fclose(fp);
1871
1872         /*  Add symmetry operations according to the lattice type  */
1873         switch (latticeType < 0 ? -latticeType : latticeType) {
1874                 static Transform tr_i = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0.5};
1875                 static Transform tr_c = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0};
1876                 static Transform tr_a = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.5};
1877                 static Transform tr_b = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5};
1878                 static Transform tr_r1 = {0, -1, 0, 1, -1, 0, 0, 0, 1, 0, 0, 0};
1879                 static Transform tr_r2 = {-1, 1, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0};
1880                 case 1:  /* P */
1881                         break;
1882                 case 2:  /* I */
1883                         sMoleculeGenerateSymopWithTransform(mp, tr_i, 0);
1884                         break;
1885                 case 3:  /* Rhombohedral obverse on hexagonal axes  */
1886                         n = mp->nsyms;
1887                         sMoleculeGenerateSymopWithTransform(mp, tr_r1, n);
1888                         sMoleculeGenerateSymopWithTransform(mp, tr_r2, n);
1889                         break;
1890                 case 4:  /* F */
1891                         n = mp->nsyms;
1892                         sMoleculeGenerateSymopWithTransform(mp, tr_a, n);
1893                         sMoleculeGenerateSymopWithTransform(mp, tr_b, n);
1894                         sMoleculeGenerateSymopWithTransform(mp, tr_c, n);
1895                         break;
1896                 case 5:  /* A */
1897                         sMoleculeGenerateSymopWithTransform(mp, tr_a, 0);
1898                         break;
1899                 case 6:  /* B */
1900                         sMoleculeGenerateSymopWithTransform(mp, tr_b, 0);
1901                         break;
1902                 case 7:  /* C */
1903                         sMoleculeGenerateSymopWithTransform(mp, tr_c, 0);
1904                         break;
1905         }
1906                 
1907         if (latticeType > 0) {
1908                 static Transform tr_inv = {-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0};
1909                 sMoleculeGenerateSymopWithTransform(mp, tr_inv, 0);
1910         }
1911         
1912         MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
1913         if (nbonds > 0) {
1914                 MoleculeAddBonds(mp, nbonds, bonds);
1915                 free(bonds);
1916         }
1917         mp->nframes = -1;  /*  Should be recalculated later  */
1918         return 0;
1919   panic:
1920         Panic("low memory while reading structure file %s", fname);
1921         return -1; /* not reached */
1922 }
1923
1924 /*  Add one gaussian orbital shell information (not undoable)  */
1925 int
1926 MoleculeAddGaussianOrbitalShell(Molecule *mol, Int sym, Int nprims, Int a_idx)
1927 {
1928         BasisSet *bset;
1929         ShellInfo *shellp;
1930         if (mol == NULL)
1931                 return -1;  /*  Molecule is empty  */
1932         bset = mol->bset;
1933         if (bset == NULL) {
1934                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
1935                 if (bset == NULL)
1936                         return -2;  /*  Low memory  */
1937         }
1938         shellp = AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), bset->nshells, NULL);
1939         if (shellp == NULL)
1940                 return -2;  /*  Low memory  */
1941         switch (sym) {
1942                 case 0:  shellp->sym = kGTOType_S;  shellp->ncomp = 1; break;
1943                 case 1:  shellp->sym = kGTOType_P;  shellp->ncomp = 3; break;
1944                 case -1: shellp->sym = kGTOType_SP; shellp->ncomp = 4; break;
1945                 case 2:  shellp->sym = kGTOType_D;  shellp->ncomp = 6; break;
1946                 case -2: shellp->sym = kGTOType_D5; shellp->ncomp = 5; break;
1947                         /*  TODO: Support F/F7 type orbitals  */
1948                         /*      case 3: sp->sym = kGTOtype_F;  sp->ncomp = 10; break;
1949                          case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break; */
1950                 default:
1951                         return -3;  /* Unsupported shell type  */
1952         }
1953         shellp->nprim = nprims;
1954         shellp->a_idx = a_idx;
1955         if (bset->shells < shellp) {
1956                 shellp->m_idx = shellp[-1].m_idx + shellp[-1].ncomp;
1957                 shellp->p_idx = shellp[-1].p_idx + shellp[-1].nprim;
1958         } else {
1959                 shellp->m_idx = 0;
1960                 shellp->p_idx = 0;
1961         }
1962         return 0;
1963 }
1964
1965 /*  Add a set of gaussian primitive coefficients (not undoable)  */
1966 int
1967 MoleculeAddGaussianPrimitiveCoefficients(Molecule *mol, Double exponent, Double contraction, Double contraction_sp)
1968 {
1969         BasisSet *bset;
1970         PrimInfo *primp;
1971         if (mol == NULL)
1972                 return -1;  /*  Molecule is empty  */
1973         bset = mol->bset;
1974         if (bset == NULL) {
1975                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
1976                 if (bset == NULL)
1977                         return -2;  /*  Low memory  */
1978         }
1979         primp = AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), bset->npriminfos, NULL);
1980         if (primp == NULL)
1981                 return -2;  /*  Low memory  */
1982         primp->A = exponent;
1983         primp->C = contraction;
1984         primp->Csp = contraction_sp;
1985         return 0;
1986 }
1987
1988 /*  Set MO coefficients for idx-th MO  */
1989 int
1990 MoleculeSetMOCoefficients(Molecule *mol, Int idx, Double energy, Int ncomps, Double *coeffs)
1991 {
1992         BasisSet *bset;
1993         int i, n;
1994         if (mol == NULL)
1995                 return -1;  /*  Molecule is empty  */
1996         bset = mol->bset;
1997         if (bset == NULL) {
1998                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
1999                 if (bset == NULL)
2000                         return -2;  /*  Low memory  */
2001         }
2002         if (bset->nmos == 0) {
2003                 if (bset->nshells > 0) {
2004                         /*  Shell info is already set: calculate the number of MOs from there  */
2005                         for (i = n = 0; i < bset->nshells; i++)
2006                                 n += bset->shells[i].ncomp;
2007                         bset->ncomps = n;
2008                 } else if (ncomps > 0) {
2009                         bset->ncomps = ncomps;
2010                 }
2011                 if (bset->rflag == 0)
2012                         bset->nmos = bset->ncomps * 2;
2013                 else
2014                         bset->nmos = bset->ncomps;
2015                 if (bset->nmos <= 0)
2016                         return -3;  /*  Bad or inconsistent number of MOs  */
2017                 bset->mo = (Double *)calloc(sizeof(Double), bset->nmos * bset->ncomps);
2018                 bset->moenergies = (Double *)calloc(sizeof(Double), bset->nmos);
2019                 if (bset->mo == NULL || bset->moenergies == NULL) {
2020                         if (bset->mo != NULL)
2021                                 free(bset->mo);
2022                         if (bset->moenergies != NULL)
2023                                 free(bset->moenergies);
2024                         bset->mo = NULL;
2025                         bset->moenergies = NULL;
2026                         bset->nmos = 0;
2027                         return -2;  /*  Low memory  */
2028                 }
2029         }
2030         if (idx < 0 || idx >= bset->nmos)
2031                 return -4;  /*  Bad MO index  */
2032         if (energy != -1000000)
2033                 bset->moenergies[idx] = energy;
2034         if (ncomps < bset->ncomps)
2035                 return -5;  /*  Insufficient number of data provided  */
2036         memmove(bset->mo + (idx * bset->ncomps), coeffs, sizeof(Double) * bset->ncomps);
2037         if (bset->cns != NULL) {
2038                 /*  Clear the cached values  */
2039                 free(bset->cns);
2040                 bset->cns = NULL;
2041                 bset->ncns = 0;
2042         }
2043         return 0;
2044 }
2045
2046 /*  Allocate BasisSet record. rflag: UHF, 0; RHF, 1; ROHF, 2
2047     ne_alpha: number of alpha electrons, ne_beta: number of beta electrons
2048     The natoms and pos are copied from mol.  */
2049 int
2050 MoleculeAllocateBasisSetRecord(Molecule *mol, Int rflag, Int ne_alpha, Int ne_beta)
2051 {
2052         BasisSet *bset;
2053         int i;
2054         Atom *ap;
2055         if (mol == NULL || mol->natoms == 0)
2056                 return -1;  /*  Molecule is empty  */
2057         bset = mol->bset;
2058         if (bset == NULL) {
2059                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2060                 if (bset == NULL)
2061                         return -2;  /*  Low memory  */
2062         }
2063         if (bset->pos != NULL) {
2064                 free(bset->pos);
2065                 bset->pos = NULL;
2066         }
2067         bset->natoms = mol->natoms;
2068         bset->pos = (Vector *)calloc(sizeof(Vector), bset->natoms);
2069         if (bset->pos == NULL)
2070                 return -2;  /*  Low memory  */
2071         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
2072                 bset->pos[i].x = ap->r.x * kAngstrom2Bohr;
2073                 bset->pos[i].y = ap->r.y * kAngstrom2Bohr;
2074                 bset->pos[i].z = ap->r.z * kAngstrom2Bohr;
2075         }
2076         bset->ne_alpha = ne_alpha;
2077         bset->ne_beta = ne_beta;
2078         bset->rflag = rflag;
2079         return 0;
2080 }
2081
2082 static void
2083 sSeparateTokens(char *inString, char **outPtr, int size)
2084 {
2085         char *p;
2086         int i;
2087         for (i = 0; i < size; i++) {
2088                 p = strtok((i == 0 ? inString : NULL), " \r\n");
2089                 if (p == NULL)
2090                         break;
2091                 outPtr[i] = p;
2092         }
2093         while (i < size) {
2094                 outPtr[i++] = NULL;
2095         }
2096 }
2097
2098 static int
2099 sReadNumberArray(void *basep, Int *countp, Int size, Int num, FILE *fp, int *lnp)
2100 {
2101         char buf[256];
2102         Int i, n;
2103         *((void **)basep) = NULL;
2104         *countp = 0;
2105         if (AssignArray(basep, countp, size, num - 1, NULL) == NULL)
2106                 return 4;  /*  Out of memory  */
2107         n = 0;
2108         while (ReadLine(buf, sizeof buf, fp, lnp) > 0) {
2109                 char *tokens[16], *p;
2110                 sSeparateTokens(buf, tokens, 16);
2111                 for (i = 0; i < 16; i++) {
2112                         if (tokens[i] == NULL)
2113                                 break;
2114                         if (size == sizeof(Int)) {
2115                                 (*((Int **)basep))[n] = strtol(tokens[i], &p, 0);
2116                         } else if (size == sizeof(Double)) {
2117                                 (*((Double **)basep))[n] = strtod(tokens[i], &p);
2118                         } else return -1;  /*  Internal error  */
2119                         if (tokens[i] == p || *p != 0)
2120                                 return 1;  /*  Non-digit character  */
2121                         if (++n == num) {
2122                                 if (i < 15 && tokens[i + 1] != NULL)
2123                                         return 2;  /*  Too many data  */
2124                                 return 0;  /*  All data are successfully read  */
2125                         }
2126                 }
2127         }
2128         return 3;  /*  Unexpected EOF  */                       
2129 }
2130
2131 static int
2132 sSetupGaussianCoefficients(BasisSet *bset)
2133 {
2134         ShellInfo *sp;
2135         PrimInfo *pp;
2136         int i, j, k;
2137         Double *dp, d;
2138         
2139         /*  Cache the contraction coefficients for efficient calculation  */
2140         /*  Sum up the number of components for all primitives  */
2141         for (i = k = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2142                 sp->cn_idx = k;
2143                 k += sp->nprim * sp->ncomp;
2144         }
2145         /*  Allocate memory for the cached values  */
2146         if (AssignArray(&bset->cns, &bset->ncns, sizeof(Double), k - 1, NULL) == NULL)
2147                 return 1;
2148         /*  Iterate over all primitives  */
2149         dp = bset->cns;
2150         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2151                 for (j = 0, pp = bset->priminfos + sp->p_idx; j < sp->nprim; j++, pp++) {
2152                         switch (sp->sym) {
2153                                 case kGTOType_S:
2154                                         // (8 alpha^3/pi^3)^0.25 exp(-alpha r^2)
2155                                         *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2156                                         break;
2157                                 case kGTOType_P:
2158                                         // (128 alpha^5/pi^3)^0.25 [x|y|z]exp(-alpha r^2)
2159                                         d = pp->C * pow(pp->A, 1.25) * 1.425410941;
2160                                         *dp++ = d;
2161                                         *dp++ = d;
2162                                         *dp++ = d;
2163                                         break;
2164                                 case kGTOType_SP:
2165                                         *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2166                                         d = pp->Csp * pow(pp->A, 1.25) * 1.425410941;
2167                                         *dp++ = d;
2168                                         *dp++ = d;
2169                                         *dp++ = d;
2170                                         break;
2171                                 case kGTOType_D:
2172                                         //  xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2)
2173                                         //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2174                                         d = pp->C * pow(pp->A, 1.75);
2175                                         dp[0] = dp[1] = dp[2] = d * 1.645922781;
2176                                         dp[3] = dp[4] = dp[5] = d * 2.850821881;
2177                                         dp += 6;
2178                                         break;
2179                                 case kGTOType_D5:
2180                                         //  3zz-rr:   (128 alpha^7/9pi^3)^0.25 (3zz-rr)exp(-alpha r^2)
2181                                         //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2182                                         //  xx-yy:    (128 alpha^7/pi^3)^0.25 (xx-yy)exp(-alpha r^2)
2183                                         d = pp->C * pow(pp->A, 1.75);
2184                                         dp[0] = d * 0.822961390;
2185                                         dp[1] = dp[2] = dp[4] = d * 2.850821881;
2186                                         dp[3] = d * 1.425410941;
2187                                         dp += 5;
2188                                         break;
2189                         }
2190                 }
2191         }
2192         return 0;
2193 }
2194
2195 int
2196 MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
2197 {
2198         FILE *fp;
2199         char buf[1024];
2200         int lineNumber;
2201         int natoms, nbasis, i, j, k, n, mxbond, retval, ncomps, nprims, nelec;
2202         BasisSet *bset;
2203         ShellInfo *sp;
2204         PrimInfo *pp;
2205         Int nary;
2206         Int *iary;
2207         Double *dary;
2208         Atom *ap;
2209         Vector *vp;
2210         Double w;
2211
2212         if (errbuf == NULL) {
2213                 errbuf = buf;
2214                 errbufsize = 1024;
2215         }
2216         errbuf[0] = 0;
2217         if (mp == NULL)
2218                 mp = MoleculeNew();
2219         bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2220         if (bset == NULL)
2221                 goto panic;
2222         mp->bset = bset;
2223         fp = fopen(fname, "rb");
2224         if (fp == NULL) {
2225                 snprintf(errbuf, errbufsize, "Cannot open file");
2226                 return 1;
2227         }
2228         lineNumber = 0;
2229         natoms = nbasis = -1;
2230         mxbond = 0;
2231         ncomps = 0;
2232         nelec = 0;
2233         nprims = 0;
2234         nary = 0;
2235         iary = NULL;
2236         dary = NULL;
2237         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2238                 char *tokens[16];
2239                 char *p = buf + 41;
2240                 if (lineNumber == 2) {
2241                         /*  job info line  */
2242                         if (buf[10] == 'U')
2243                                 bset->rflag = 0;  /*  UHF  */
2244                         else if (buf[11] == 'O')
2245                                 bset->rflag = 2;  /*  ROHF  */
2246                         else bset->rflag = 1; /*  RHF  */
2247                         continue;
2248                 }
2249                 while (p > buf && *p == ' ')
2250                         p--;
2251                 p[1] = 0;
2252                 sSeparateTokens(buf + 42, tokens, 16);
2253                 if (strcmp(buf, "Number of atoms") == 0) {
2254                         if (tokens[1] == NULL || (natoms = atoi(tokens[1])) <= 0) {
2255                                 snprintf(errbuf, errbufsize, "Line %d: strange number of atoms: %s", lineNumber, tokens[1]);
2256                                 retval = 2;
2257                                 goto cleanup;
2258                         }
2259                         /*  Allocate atom records (all are empty for now)  */
2260                         AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms - 1, NULL);
2261                         /*  Also allocate atom position array for MO calculations  */
2262                         AssignArray(&bset->pos, &bset->natoms, sizeof(Vector), natoms - 1, NULL);
2263                         /*  Also allocate nuclear charge array  */
2264                         bset->nuccharges = (Double *)calloc(sizeof(Double), natoms);
2265                 } else if (strcmp(buf, "Number of electrons") == 0) {
2266                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2267                                 snprintf(errbuf, errbufsize, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
2268                                 retval = 2;
2269                                 goto cleanup;
2270                         }
2271                         nelec = i;
2272                 } else if (strcmp(buf, "Number of alpha electrons") == 0) {
2273                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2274                                 snprintf(errbuf, errbufsize, "Line %d: strange number of alpha electrons: %s", lineNumber, tokens[1]);
2275                                 retval = 2;
2276                                 goto cleanup;
2277                         }
2278                         bset->ne_alpha = i;
2279                 } else if (strcmp(buf, "Number of beta electrons") == 0) {
2280                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2281                                 snprintf(errbuf, errbufsize, "Line %d: strange number of beta electrons: %s", lineNumber, tokens[1]);
2282                                 retval = 2;
2283                                 goto cleanup;
2284                         }
2285                         bset->ne_beta = i;
2286                         if (bset->ne_alpha + bset->ne_beta != nelec) {
2287                                 snprintf(errbuf, errbufsize, "Line %d: sum of alpha (%d) and beta (%d) electrons does not match the number of electrons (%d)", lineNumber, (int)bset->ne_alpha, (int)bset->ne_beta, (int)nelec);
2288                                 retval = 2;
2289                                 goto cleanup;
2290                         }
2291                 } else if (strcmp(buf, "Number of basis functions") == 0) {
2292                         if (tokens[1] == NULL || (nbasis = atoi(tokens[1])) <= 0) {
2293                                 snprintf(errbuf, errbufsize, "Line %d: strange number of basis functions: %s", lineNumber, tokens[1]);
2294                                 retval = 2;
2295                                 goto cleanup;
2296                         }
2297                 } else if (strcmp(buf, "Atomic numbers") == 0) {
2298                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2299                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2300                                 retval = 2;
2301                                 goto cleanup;
2302                         }
2303                         if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms, fp, &lineNumber) != 0) {
2304                                 snprintf(errbuf, errbufsize, "Line %d: cannot read atomic numbers", lineNumber);
2305                                 retval = 2;
2306                                 goto cleanup;
2307                         }
2308                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2309                                 ap->atomicNumber = iary[i];
2310                                 bset->nuccharges[i] = iary[i];
2311                                 ElementToString(ap->atomicNumber, ap->element);
2312                                 memmove(ap->aname, ap->element, 4);
2313                                 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
2314                                         ap->weight = w;
2315                         }
2316                         free(iary);
2317                         iary = NULL;
2318                 } else if (strcmp(buf, "Nuclear charges") == 0) {
2319                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2320                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2321                                 retval = 2;
2322                                 goto cleanup;
2323                         }
2324                         if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms, fp, &lineNumber) != 0) {
2325                                 snprintf(errbuf, errbufsize, "Line %d: cannot read nuclear charges", lineNumber);
2326                                 retval = 2;
2327                                 goto cleanup;
2328                         }
2329                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2330                                 bset->nuccharges[i] = dary[i];
2331                         }
2332                         free(iary);
2333                         iary = NULL;
2334                 } else if (strcmp(buf, "Current cartesian coordinates") == 0) {
2335                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * 3) {
2336                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of cartesian coordinates: %s", lineNumber, tokens[2]);
2337                                 retval = 2;
2338                                 goto cleanup;
2339                         }
2340                         if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms * 3, fp, &lineNumber) != 0) {
2341                                 snprintf(errbuf, errbufsize, "Line %d: cannot read cartesian coordinates", lineNumber);
2342                                 retval = 2;
2343                                 goto cleanup;
2344                         }
2345                         for (i = 0, ap = mp->atoms, vp = bset->pos; i < natoms; i++, ap = ATOM_NEXT(ap), vp++) {
2346                                 vp->x = dary[i * 3];
2347                                 vp->y = dary[i * 3 + 1];
2348                                 vp->z = dary[i * 3 + 2];
2349                                 ap->r.x = vp->x * kBohr2Angstrom;
2350                                 ap->r.y = vp->y * kBohr2Angstrom;
2351                                 ap->r.z = vp->z * kBohr2Angstrom;
2352                         }
2353                         free(dary);
2354                         dary = NULL;
2355                 } else if (strcmp(buf, "MxBond") == 0) {
2356                         if (tokens[1] == NULL || (mxbond = atoi(tokens[1])) <= 0) {
2357                                 snprintf(errbuf, errbufsize, "Line %d: strange number of bonds per atom: %s", lineNumber, tokens[1]);
2358                                 retval = 2;
2359                                 goto cleanup;
2360                         }
2361                 } else if (strcmp(buf, "IBond") == 0) {
2362                         Int bonds[ATOMS_MAX_CONNECTS * 2 + 1], lim;
2363                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * mxbond) {
2364                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of bonds: %s", lineNumber, tokens[2]);
2365                                 retval = 2;
2366                                 goto cleanup;
2367                         }
2368                         if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms * mxbond, fp, &lineNumber) != 0) {
2369                                 snprintf(errbuf, errbufsize, "Line %d: cannot read bond information", lineNumber);
2370                                 retval = 2;
2371                                 goto cleanup;
2372                         }
2373                         lim = (mxbond > ATOMS_MAX_CONNECTS ? ATOMS_MAX_CONNECTS : mxbond);
2374                         for (i = 0; i < natoms; i++) {
2375                                 for (j = k = 0; j < lim; j++) {
2376                                         n = iary[i * mxbond + j] - 1;
2377                                         if (n > i) {
2378                                                 /*  Connect atom i and atom n  */
2379                                                 bonds[k++] = i;
2380                                                 bonds[k++] = n;
2381                                         }
2382                                 }
2383                                 if (k > 0) {
2384                                         bonds[k] = kInvalidIndex;
2385                                         MoleculeAddBonds(mp, k / 2, bonds);
2386                                 }
2387                         }
2388                         free(iary);
2389                         iary = NULL;
2390                 } else if (strcmp(buf, "Shell types") == 0) {
2391                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0) {
2392                                 snprintf(errbuf, errbufsize, "Line %d: wrong number of shell types: %s", lineNumber, tokens[2]);
2393                                 retval = 2;
2394                                 goto cleanup;
2395                         }
2396                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2397                                 snprintf(errbuf, errbufsize, "Line %d: cannot read shell types", lineNumber);
2398                                 retval = 2;
2399                                 goto cleanup;
2400                         }
2401                         /*  Allocate ShellInfo table and store shell type information  */
2402                         AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), nary - 1, NULL);
2403                         for (i = n = 0, sp = bset->shells; i < nary; i++, sp++) {
2404                                 switch (iary[i]) {
2405                                         case 0:  sp->sym = kGTOType_S;  sp->ncomp = 1; break;
2406                                         case 1:  sp->sym = kGTOType_P;  sp->ncomp = 3; break;
2407                                         case -1: sp->sym = kGTOType_SP; sp->ncomp = 4; break;
2408                                         case 2:  sp->sym = kGTOType_D;  sp->ncomp = 6; break;
2409                                         case -2: sp->sym = kGTOType_D5; sp->ncomp = 5; break;
2410                                                 /*  TODO: Support F/F7 type orbitals  */
2411                                                 /*      case 3: sp->sym = kGTOtype_F;  sp->ncomp = 10; break;
2412                                                  case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break; */
2413                                         default:
2414                                                 snprintf(errbuf, errbufsize, "Line %d: unsupported shell type %d", lineNumber, iary[i]);
2415                                                 retval = 2;
2416                                                 goto cleanup;
2417                                 }
2418                                 sp->m_idx = n;
2419                                 n += sp->ncomp;
2420                         }
2421                         bset->ncomps = ncomps = n;
2422                         free(iary);
2423                         iary = NULL;
2424                 } else if (strcmp(buf, "Number of primitives per shell") == 0) {
2425                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2426                                 snprintf(errbuf, errbufsize, "Line %d: wrong size of the primitive table: %s", lineNumber, tokens[2]);
2427                                 retval = 2;
2428                                 goto cleanup;
2429                         }
2430                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2431                                 snprintf(errbuf, errbufsize, "Line %d: cannot read primitive table", lineNumber);
2432                                 retval = 2;
2433                                 goto cleanup;
2434                         }
2435                         for (i = n = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2436                                 sp->nprim = iary[i];
2437                                 sp->p_idx = n;
2438                                 n += sp->nprim;
2439                         }
2440                         nprims = n;
2441                         free(iary);
2442                         iary = NULL;
2443                 } else if (strcmp(buf, "Shell to atom map") == 0) {
2444                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2445                                 snprintf(errbuf, errbufsize, "Line %d: wrong size of the shell-to-atom map: %s", lineNumber, tokens[2]);
2446                                 retval = 2;
2447                                 goto cleanup;
2448                         }
2449                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2450                                 snprintf(errbuf, errbufsize, "Line %d: cannot read shell-to-atom table", lineNumber);
2451                                 retval = 2;
2452                                 goto cleanup;
2453                         }
2454                         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2455                                 sp->a_idx = iary[i] - 1;
2456                         }
2457                         free(iary);
2458                         iary = NULL;
2459                 } else if (strcmp(buf, "Primitive exponents") == 0) {
2460                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nprims) {
2461                                 snprintf(errbuf, errbufsize, "Line %d: wrong number of primitive exponents: %s", lineNumber, tokens[2]);
2462                                 retval = 2;
2463                                 goto cleanup;
2464                         }
2465                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2466                                 snprintf(errbuf, errbufsize, "Line %d: cannot read primitive exponents", lineNumber);
2467                                 retval = 2;
2468                                 goto cleanup;
2469                         }
2470                         /*  Allocate PrimInfo table  */
2471                         AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), nprims - 1, NULL);
2472                         for (i = 0, pp = bset->priminfos; i < nprims; i++, pp++) {
2473                                 pp->A = dary[i];
2474                         }
2475                         free(dary);
2476                         dary = NULL;
2477                 } else if (strcmp(buf, "Contraction coefficients") == 0) {
2478                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2479                                 snprintf(errbuf, errbufsize, "Line %d: wrong number of contraction coefficients: %s", lineNumber, tokens[2]);
2480                                 retval = 2;
2481                                 goto cleanup;
2482                         }
2483                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2484                                 snprintf(errbuf, errbufsize, "Line %d: cannot read contraction coefficients", lineNumber);
2485                                 retval = 2;
2486                                 goto cleanup;
2487                         }
2488                         for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2489                                 pp->C = dary[i];
2490                         }
2491                         free(dary);
2492                         dary = NULL;
2493                 } else if (strcmp(buf, "P(S=P) Contraction coefficients") == 0) {
2494                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2495                                 snprintf(errbuf, errbufsize, "Line %d: wrong number of P(S=P) contraction coefficients: %s", lineNumber, tokens[2]);
2496                                 retval = 2;
2497                                 goto cleanup;
2498                         }
2499                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2500                                 snprintf(errbuf, errbufsize, "Line %d: cannot read P(S=P) contraction coefficients", lineNumber);
2501                                 retval = 2;
2502                                 goto cleanup;
2503                         }
2504                         for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2505                                 pp->Csp = dary[i];
2506                         }
2507                         free(dary);
2508                         dary = NULL;
2509                 } else if (strcmp(buf, "Alpha Orbital Energies") == 0) {
2510                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2511                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of alpha orbitals: %s", lineNumber, tokens[2]);
2512                                 retval = 2;
2513                                 goto cleanup;
2514                         }
2515                         if (sReadNumberArray(&bset->moenergies, &bset->nmos, sizeof(Double), i, fp, &lineNumber) != 0) {
2516                                 snprintf(errbuf, errbufsize, "Line %d: cannot read alpha orbital energies", lineNumber);
2517                                 retval = 2;
2518                                 goto cleanup;
2519                         }
2520                 } else if (strcmp(buf, "Alpha MO coefficients") == 0) {
2521                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2522                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of alpha MO coefficients: %s", lineNumber, tokens[2]);
2523                                 retval = 2;
2524                                 goto cleanup;
2525                         }
2526                         if (sReadNumberArray(&bset->mo, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2527                                 snprintf(errbuf, errbufsize, "Line %d: cannot read MO coefficients", lineNumber);
2528                                 retval = 2;
2529                                 goto cleanup;
2530                         }
2531                 } else if (strcmp(buf, "Beta Orbital Energies") == 0) {
2532                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2533                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of beta orbitals: %s", lineNumber, tokens[2]);
2534                                 retval = 2;
2535                                 goto cleanup;
2536                         }
2537                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2538                                 snprintf(errbuf, errbufsize, "Line %d: cannot read beta orbital energies", lineNumber);
2539                                 retval = 2;
2540                                 goto cleanup;
2541                         }
2542                         bset->moenergies = (Double *)realloc(bset->moenergies, sizeof(Double) * 2 * ncomps);
2543                         bset->nmos = ncomps * 2;
2544                         bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);
2545                         memmove(bset->moenergies + ncomps, dary, sizeof(Double) * ncomps);
2546                         memset(bset->mo + ncomps * ncomps, 0, sizeof(Double) * ncomps * ncomps);
2547                         free(dary);
2548                         dary = NULL;
2549                 } else if (strcmp(buf, "Beta MO coefficients") == 0) {
2550                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2551                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of beta MO coefficients: %s", lineNumber, tokens[2]);
2552                                 retval = 2;
2553                                 goto cleanup;
2554                         }
2555                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2556                                 snprintf(errbuf, errbufsize, "Line %d: cannot read alpha MO coefficients", lineNumber);
2557                                 retval = 2;
2558                                 goto cleanup;
2559                         }
2560                         bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);  /*  Should be unnecessary, just in case  */
2561                         memmove(bset->mo + ncomps * ncomps, dary, sizeof(Double) * ncomps * ncomps);
2562                         free(dary);
2563                         dary = NULL;
2564                 } else if (strcmp(buf, "Total SCF Density") == 0) {
2565                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * (ncomps + 1) / 2) {
2566                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of SCF densities: %s", lineNumber, tokens[2]);
2567                                 retval = 2;
2568                                 goto cleanup;
2569                         }
2570                         if (sReadNumberArray(&bset->scfdensities, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2571                                 snprintf(errbuf, errbufsize, "Line %d: cannot read SCF densities", lineNumber);
2572                                 retval = 2;
2573                                 goto cleanup;
2574                         }
2575                 }
2576         }
2577         if (mp->natoms == 0) {
2578                 snprintf(errbuf, errbufsize, "Atom information is missing");
2579                 retval = 2;
2580                 goto cleanup;
2581         }
2582         if (bset->shells == NULL || bset->priminfos == NULL) {
2583                 snprintf(errbuf, errbufsize, "Gaussian primitive information is missing");
2584                 retval = 2;
2585                 goto cleanup;
2586         }
2587         if (bset->mo == NULL) {
2588                 snprintf(errbuf, errbufsize, "MO coefficients were not found");
2589                 retval = 2;
2590                 goto cleanup;
2591         }
2592         if (sSetupGaussianCoefficients(bset) != 0) {
2593                 snprintf(errbuf, errbufsize, "Internal error during setup MO calculation");
2594                 retval = 2;
2595                 goto cleanup;
2596         }
2597         mp->nframes = -1;
2598         retval = 0;
2599 cleanup:
2600         fclose(fp);
2601         if (iary != NULL)
2602                 free(iary);
2603         if (dary != NULL)
2604                 free(dary);
2605         if (retval != 0) {
2606                 if (mp->bset != NULL) {
2607                         BasisSetRelease(mp->bset);
2608                         mp->bset = NULL;
2609                 }
2610         }
2611         return retval;
2612 panic:
2613         Panic("low memory while reading fchk file %s", fname);
2614         return -1; /* not reached */    
2615 }
2616
2617 int
2618 MoleculeLoadGamessDatFile(Molecule *mol, const char *fname, char *errbuf, int errbufsize)
2619 {
2620         FILE *fp;
2621         int newmol = 0;
2622         char buf[1024];
2623         int lineNumber, i, j, k, len, natoms = 0;
2624         int nframes = 0;
2625         int n1;
2626         int ival[8];
2627         double dval[8];
2628         char sval[16];
2629         Vector *vbuf = NULL;
2630         IntGroup *ig;
2631         int optimizing = 0, status = 0;
2632
2633         if (errbuf == NULL) {
2634                 errbuf = buf;
2635                 errbufsize = 1024;
2636         }
2637         errbuf[0] = 0;
2638         if (mol == NULL) {
2639                 mol = MoleculeNew();
2640         }
2641         if (mol->natoms == 0)
2642                 newmol = 1;
2643
2644         fp = fopen(fname, "rb");
2645         if (fp == NULL) {
2646                 snprintf(errbuf, errbufsize, "Cannot open file");
2647                 return 1;
2648         }
2649         
2650         /*  ESP is cleared (not undoable!)  */
2651         if (mol->elpots != NULL) {
2652                 free(mol->elpots);
2653                 mol->elpots = NULL;
2654                 mol->nelpots = 0;
2655         }
2656         
2657         lineNumber = 0;
2658         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2659         redo:
2660                 n1 = 0;
2661                 if (strncmp(buf, " $DATA", 6) == 0) {
2662                         /*  Initial geometry  */
2663                         if (!newmol) {
2664                                 vbuf = (Vector *)calloc(sizeof(Vector), mol->natoms);
2665                         }
2666                         i = 0;
2667                         ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Title  */
2668                         ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Symmetry  */
2669                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2670                                 if (strncmp(buf, " $END", 5) == 0)
2671                                         break;
2672                                 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
2673                                         snprintf(errbuf, errbufsize, "Line %d: bad format in $DATA section", lineNumber);
2674                                         return 2;
2675                                 }
2676                                 if (newmol) {
2677                                         Atom a;
2678                                         memset(&a, 0, sizeof(a));
2679                                         strncpy(a.aname, sval, 4);
2680                                         a.r.x = dval[1];
2681                                         a.r.y = dval[2];
2682                                         a.r.z = dval[3];
2683                                         a.atomicNumber = (Int)dval[0];
2684                                         strncpy(a.element, ElementToString(a.atomicNumber, sval), 3);
2685                                         a.type = AtomTypeEncodeToUInt(a.element);
2686                                         a.weight = WeightForAtomicNumber(a.atomicNumber);
2687                                         MoleculeCreateAnAtom(mol, &a, mol->natoms);
2688                                 } else {
2689                                         Atom *ap;
2690                                         if (i >= mol->natoms) {
2691                                                 snprintf(errbuf, errbufsize, "Line %d: too many atoms", lineNumber);
2692                                                 return 3;
2693                                         }
2694                                         if ((ap = ATOM_AT_INDEX(mol->atoms, i))->atomicNumber != dval[0]) {
2695                                                 snprintf(errbuf, errbufsize, "Line %d: atomic number does not match", lineNumber);
2696                                                 return 4;
2697                                         }
2698                                         vbuf[i].x = dval[1];
2699                                         vbuf[i].y = dval[2];
2700                                         vbuf[i].z = dval[3];
2701                                 }
2702                                 /*  Skip until a blank line is found  */
2703                                 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2704                                         for (j = 0; buf[j] == ' '; j++);
2705                                         if (buf[j] == '\n')
2706                                                 break;
2707                                 }
2708                                 i++;
2709                         }
2710                         natoms = i;
2711                         if (!newmol) {
2712                                 /*  Set atom positions  */
2713                                 IntGroup *ig;
2714                                 if (natoms < mol->natoms) {
2715                                         snprintf(errbuf, errbufsize, "Line %d: too few atoms", lineNumber);
2716                                         return 5;
2717                                 }
2718                                 ig = IntGroupNewWithPoints(0, natoms, -1);
2719                                 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, natoms, vbuf);
2720                                 IntGroupRelease(ig);
2721                         }
2722                         if (vbuf == NULL)
2723                                 vbuf = (Vector *)calloc(sizeof(Vector), natoms);
2724                         nframes = MoleculeGetNumberOfFrames(mol);
2725                         if (status < 0)
2726                                 break;
2727                         continue;
2728                 } else if (strstr(buf, "DATA FROM NSERCH") != NULL || (strstr(buf, "RESULTS FROM SUCCESSFUL") != NULL && (n1 = 1))) {
2729                         /*  Skip until the separator line is read (three or four lines)  */
2730                         i = 0;
2731                         do {
2732                                 if (i++ >= 4) {
2733                                         snprintf(errbuf, errbufsize, "Line %d: the separator line at the top of the coordinates is not found: bad format?", lineNumber);
2734                                         return 6;
2735                                 }
2736                                 ReadLine(buf, sizeof buf, fp, &lineNumber);
2737                         } while (strstr(buf, "----------------------------") == NULL);
2738                         for (i = 0; i < natoms; i++) {
2739                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2740                                         snprintf(errbuf, errbufsize, "Unexpected end of file in reading NSERCH data");
2741                                         return 6;
2742                                 }
2743                                 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
2744                                         snprintf(errbuf, errbufsize, "Line %d: bad format in NSERCH coordinate data", lineNumber);
2745                                         return 7;
2746                                 }
2747                                 vbuf[i].x = dval[1];
2748                                 vbuf[i].y = dval[2];
2749                                 vbuf[i].z = dval[3];
2750                         }
2751                         ig = IntGroupNewWithPoints(nframes, 1, -1);
2752                         MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, natoms, vbuf, 0, NULL);
2753                         IntGroupRelease(ig);
2754                         nframes++;
2755                         if (n1 == 0)
2756                                 optimizing = 1;  /*  Flag to skip reading the VEC group  */
2757                         else
2758                                 optimizing = 0;
2759                         continue;
2760                 } else if (strstr(buf, "E(UHF)") != NULL || (strstr(buf, "E(RHF)") != NULL && (n1 = 1)) || (strstr(buf, "E(ROHF)") != NULL && (n1 = 2))) {
2761                         if (mol->bset == NULL) {
2762                                 i = MoleculeAllocateBasisSetRecord(mol, n1, 0, 0);
2763                                 if (i != 0) {
2764                                         snprintf(errbuf, errbufsize, "Line %d: cannot allocate basis set internal buffer", lineNumber);
2765                                         return 8;
2766                                 }
2767                         }
2768                 } else if (strncmp(buf, " $VEC", 5) == 0) {
2769                         Double *coeffs;
2770                         /*  Read the vec group  */
2771                         if (mol->bset == NULL)
2772                                 continue;  /*  Just ignore  */
2773                         if (optimizing)
2774                                 continue;  /*  Ignore VEC group during optimization  */
2775                         coeffs = (Double *)calloc(sizeof(Double), mol->bset->ncomps);
2776                         if (coeffs == NULL) {
2777                                 snprintf(errbuf, errbufsize, "Line %d: low memory during $VEC", lineNumber);
2778                                 return 9;
2779                         }
2780                         i = k = 0;
2781                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2782                                 len = strlen(buf);
2783                                 if (strncmp(buf, " $END", 5) == 0)
2784                                         break;
2785                                 while ((j = 5 + (k % 5) * 15) <= len && buf[j] != 0 && buf[j] != '\n') {
2786                                         strncpy(sval, buf + j, 15);
2787                                         sval[15] = 0;
2788                                         coeffs[k] = strtod(sval, NULL);
2789                                         k++;
2790                                         if ((k % 5) == 0)
2791                                                 break;
2792                                 }
2793                                 if (k < mol->bset->ncomps)
2794                                         continue;
2795                                 j = MoleculeSetMOCoefficients(mol, i, -1000000, k, coeffs);
2796                                 if (j != 0) {
2797                                         snprintf(errbuf, errbufsize, "Line %d: cannot set coefficients for MO %d", lineNumber, i + 1);
2798                                         free(coeffs);
2799                                         return 10;
2800                                 }
2801                                 i++;
2802                                 k = 0;
2803                         }
2804                         if (status < 0)
2805                                 break;
2806                         continue;
2807                 } else if ((strstr(buf, "ELECTRIC POTENTIAL") != NULL || strstr(buf, "ELECTROSTATIC POTENTIAL") != NULL) && strstr(buf, "ELPOTT") != NULL) {
2808                         i = 0;
2809                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2810                                 Elpot *ep;
2811                                 if (strstr(buf, "TOTAL NUMBER OF GRID POINTS") != NULL)
2812                                         continue;
2813                                 if (sscanf(buf, "%d %lf %lf %lf %lf", &ival[0], &dval[0], &dval[1], &dval[2], &dval[3]) < 5)
2814                                         break;
2815                                 ep = AssignArray(&mol->elpots, &mol->nelpots, sizeof(Elpot), i, NULL);
2816                                 ep->pos.x = dval[0];
2817                                 ep->pos.y = dval[1];
2818                                 ep->pos.z = dval[2];
2819                                 ep->esp = dval[3];
2820                                 i++;
2821                         }
2822                         if (status > 0)
2823                                 goto redo;  /*  This section has no end line, so the last line should be processed again  */
2824                         else break;    /*  End of file encountered or interrupted */
2825                 }  /*  TODO: read MOLPLT info if present  */
2826         }
2827         if (status < 0) {
2828                 snprintf(errbuf, errbufsize, "User interrupt at line %d", lineNumber);
2829                 return 11;
2830         }
2831         if (vbuf != NULL)
2832                 free(vbuf);
2833         if (newmol && mol->nbonds == 0) {
2834                 /*  Guess bonds  */
2835                 Int nbonds, *bonds;
2836                 MoleculeGuessBonds(mol, 1.2, &nbonds, &bonds);
2837                 if (nbonds > 0) {
2838                         MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds);
2839                         free(bonds);
2840                 }
2841         }
2842         return 0;
2843 }
2844
2845 int
2846 MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
2847 {
2848         int retval;
2849         if (ftype == NULL || *ftype == 0) {
2850                 const char *cp;
2851                 cp = strrchr(fname, '.');
2852                 if (cp != NULL)
2853                         ftype = cp + 1;
2854                 else {
2855                         cp = guessMoleculeType(fname);
2856                         if (strcmp(cp, "???") != 0)
2857                                 ftype = cp;
2858                 }
2859         }
2860         if (strcasecmp(ftype, "pdb") == 0) {
2861                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
2862         }
2863         if (retval != 0) {
2864                 /*  Try all formats once again  */
2865                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
2866         }
2867         return retval;
2868 }
2869
2870 int
2871 MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
2872 {
2873         FILE *fp;
2874         char buf[1024];
2875         char *p;
2876         int lineNumber;
2877         int i, j, new_unit, retval;
2878         Atom *ap;
2879         Int ibuf[12];
2880         Int entries = 0;
2881         retval = 0;
2882         if (errbuf == NULL) {
2883                 errbuf = buf;
2884                 errbufsize = 1024;
2885         }
2886         errbuf[0] = 0;
2887         fp = fopen(fname, "rb");
2888         if (fp == NULL) {
2889                 snprintf(errbuf, errbufsize, "Cannot open file");
2890                 return -1;
2891         }
2892 /*      flockfile(fp); */
2893         if (mp->natoms == 0)
2894                 new_unit = 1;
2895         else new_unit = 0;
2896         lineNumber = 0;
2897         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2898                 if (strncmp(buf, "END", 3) == 0)
2899                         break;
2900                 if (strncmp(buf, "HETATM", 6) == 0 || strncmp(buf, "ATOM", 4) == 0) {
2901                         struct {
2902                                 Int serial, intCharge, resSeq;
2903                                 Vector r;
2904                                 Double occ, temp;
2905                                 char segName[5], resName[4], atomName[5], resSeqStr[5], atomType[3], element[3], occStr[6];
2906                         } w;
2907                         memset(&w, 0, sizeof(w));
2908                         ReadFormat(buf, "x6 I5 x1 S4 x1 S3 x1 x1 S4 x1 x3 F8 F8 F8 S6 F6 x6 S4 S2 I2",
2909                                 &w.serial, w.atomName, w.resName, w.resSeqStr, &w.r.x, &w.r.y, &w.r.z,
2910                                 w.occStr, &w.temp, w.segName, w.element, &w.intCharge);
2911                         if (w.atomName[0] == 0) {
2912                                 continue;  /*  Atom name is empty  */
2913                         }
2914                         /*  A workaround for residue number >= 10000 (XPLOR style)  */
2915                         if (w.resSeqStr[0] >= 'A' && w.resSeqStr[0] <= 'Z') {
2916                                 w.resSeq = (w.resSeqStr[0] - 'A' + 10) * 1000 + atoi(w.resSeqStr + 1);
2917                         } else {
2918                                 w.resSeq = atoi(w.resSeqStr);
2919                         }
2920                         if (w.element[0] == 0) {
2921                                 /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
2922                                 for (p = w.atomName; *p != 0; p++) {
2923                                         if (isalpha(*p) && *p != '_') {
2924                                                 w.element[0] = toupper(*p);
2925                                                 if (isalpha(p[1]) && p[1] != '_') {
2926                                                         w.element[1] = toupper(p[1]);
2927                                                         w.element[2] = 0;
2928                                                 } else {
2929                                                         w.element[1] = 0;
2930                                                 }
2931                                                 break;
2932                                         }
2933                                 }
2934                         }
2935                         if (w.occStr[0] == 0)
2936                                 w.occ = 1.0;
2937                         else
2938                                 w.occ = atof(w.occStr);
2939                         if (w.serial <= 0) {
2940                                 snprintf(errbuf, errbufsize, "line %d: non-positive atom number %d", lineNumber, w.serial);
2941                                 retval = 1;
2942                                 goto abort;
2943                         }
2944                         w.serial--;  /*  The internal atom number is 0-based  */
2945                         if (w.serial >= mp->natoms) {
2946                                 if (new_unit) {
2947                                         ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, w.serial, NULL);
2948                                 } else {
2949                                         snprintf(errbuf, errbufsize, "line %d: the atom number %d does not exist in the structure file", lineNumber, w.serial+1);
2950                                         retval = 1;
2951                                         goto abort;
2952                                 }
2953                         }
2954                         ap = ATOM_AT_INDEX(mp->atoms, w.serial);
2955                         ap->r = w.r;
2956                         ap->occupancy = w.occ;
2957                         ap->tempFactor = w.temp;
2958                         if (new_unit) {
2959                                 if (w.segName[0] == 0)
2960                                         strncpy(w.segName, "MAIN", 4);
2961                                 strncpy(ap->segName, w.segName, 4);
2962                                 ap->resSeq = w.resSeq;
2963                                 strncpy(ap->resName, w.resName, 4);
2964                                 strncpy(ap->aname, w.atomName, 4);
2965                                 strncpy(ap->element, w.element, 2);
2966                                 ap->intCharge = w.intCharge;
2967                                 if (ap->resSeq > 0) {
2968                                         if (ap->resSeq < mp->nresidues) {
2969                                                 /*  Update the resName according to residues[]  */
2970                                                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
2971                                         } else {
2972                                                 /*  Register the resName to residues[]  */
2973                                                 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, w.resName);
2974                                         }
2975                                 } else {
2976                                         ap->resSeq = 0;
2977                                         strcpy(ap->resName, "XXX");
2978                                         if (mp->nresidues == 0)
2979                                                 AssignArray(&mp->residues, &mp->nresidues, 4, 0, ap->resName);
2980                                 }
2981                                 i = ElementToInt(ap->element);
2982                                 if (i >= 0)
2983                                         ap->weight = gElementParameters[i].weight;
2984                         }
2985                         entries++;
2986                 } else if (strncmp(buf, "CONECT", 6) == 0 && new_unit) {
2987                         i = ReadFormat(buf, "x6 I5I5I5I5I5I5I5I5I5I5I5I5",
2988                                 ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
2989                                 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7,
2990                                 ibuf + 8, ibuf + 9, ibuf + 10, ibuf + 11);
2991                         if (i >= 2) {
2992                                 Int bbuf[25];
2993                                 int bi;
2994                                 for (j = 0; j < i; j++) {
2995                                         if (ibuf[j] < 0 || ibuf[j] > mp->natoms) {
2996                                                 snprintf(errbuf, errbufsize, "line %d: The CONECT record contains non-existent atom %d", lineNumber, ibuf[j]);
2997                                                 retval = 1;
2998                                                 goto abort;
2999                                         } else if (ibuf[j] == 0)
3000                                                 break;
3001                                 }
3002                                 i = j;
3003                                 if (i < 2)
3004                                         continue;
3005                                 for (j = 1, bi = 0; j < i; j++) {
3006                                         if (ibuf[0] < ibuf[j]) {
3007                                                 bbuf[bi * 2] = ibuf[0] - 1;
3008                                                 bbuf[bi * 2 + 1] = ibuf[j] - 1;
3009                                                 bi++;
3010                                         }
3011                                 }
3012                                 if (bi == 0)
3013                                         continue;
3014                                 bbuf[bi * 2] = -1;
3015                                 retval = MoleculeAddBonds(mp, bi, bbuf);
3016                                 if (retval < 0) {
3017                                         snprintf(errbuf, errbufsize, "line %d: bad bond specification", lineNumber);
3018                                         retval = 1;
3019                                         goto abort;
3020                                 }
3021                         }
3022                 }
3023         }
3024 /*      funlockfile(fp); */
3025         fclose(fp);
3026         if (new_unit) {
3027                 /*  Renumber atoms if some atom number is unoccupied  */
3028                 int *old2new, oldidx, newidx;
3029                 old2new = (int *)calloc(sizeof(int), mp->natoms);
3030                 if (old2new == NULL) {
3031                         snprintf(errbuf, errbufsize, "Out of memory");
3032                         retval = 1;
3033                         goto abort;
3034                 }
3035                 for (oldidx = newidx = 0; oldidx < mp->natoms; oldidx++) {
3036                         ap = ATOM_AT_INDEX(mp->atoms, oldidx);
3037                         if (ap->aname[0] != 0) {
3038                                 old2new[oldidx] = newidx;
3039                                 if (oldidx > newidx)
3040                                         memmove(ATOM_AT_INDEX(mp->atoms, newidx), ap, gSizeOfAtomRecord);
3041                                 newidx++;
3042                         }
3043                 }
3044                 mp->natoms = newidx;
3045                 if (oldidx > newidx) {
3046                         /*  Renumber the connects and bonds  */
3047                         for (i = 0; i < mp->natoms; i++) {
3048                                 ap = ATOM_AT_INDEX(mp->atoms, i);
3049                                 for (j = 0; j < ap->nconnects; j++) {
3050                                         ap->connects[j] = old2new[ap->connects[j]];
3051                                 }
3052                         }
3053                         for (i = 0; i < mp->nbonds * 2; i++) {
3054                                 mp->bonds[i] = old2new[mp->bonds[i]];
3055                         }
3056                 }
3057                 retval = MoleculeRebuildTablesFromConnects(mp);
3058                 if (retval != 0) {
3059                         /*  This error may not happen  */
3060                         snprintf(errbuf, errbufsize, "Cannot build angle/dihedral/improper tables");
3061                         retval = 1;
3062                         goto abort;
3063                 }
3064
3065         }
3066         mp->nframes = -1;  /*  Should be recalculated later  */
3067         if (entries == 0)
3068                 return 1;  /*  No atoms  */
3069         return 0;
3070         abort:
3071         if (fp != NULL) {
3072         /*      funlockfile(fp); */
3073                 fclose(fp);
3074         }
3075         if (entries == 0)
3076                 return 1;  /*  Maybe different format?  */
3077         return retval;
3078 }
3079
3080 int
3081 MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3082 {
3083         DcdRecord dcd;
3084         SFloat32 *xp, *yp, *zp;
3085         Vector *vp;
3086         IntGroup *ig;
3087         int n;
3088         errbuf[0] = 0;
3089         if (mp == NULL || mp->natoms == 0) {
3090                 snprintf(errbuf, errbufsize, "Molecule is empty");
3091                 return 1;
3092         }
3093         n = DcdOpen(fname, &dcd);
3094         if (n != 0) {
3095                 switch (n) {
3096                         case -2: snprintf(errbuf, errbufsize, "Cannot open file"); break;
3097                         case 1:  snprintf(errbuf, errbufsize, "Premature EOF encountered"); break;
3098                         case 2:  snprintf(errbuf, errbufsize, "Bad block length of the first section"); break;
3099                         case 3:  snprintf(errbuf, errbufsize, "\"CORD\" signature is missing"); break;
3100                         case 4:  snprintf(errbuf, errbufsize, "Bad termination of the first section"); break;
3101                         case 5:  snprintf(errbuf, errbufsize, "The title section is not correct"); break;
3102                         case 6:  snprintf(errbuf, errbufsize, "The atom number section is not correct"); break;
3103                         default: snprintf(errbuf, errbufsize, "Read error in dcd file"); break;
3104                 }
3105         } else {
3106                 if (dcd.natoms == 0)
3107                         snprintf(errbuf, errbufsize, "No atoms were found in the dcd file");
3108                 else if (dcd.nframes == 0)
3109                         snprintf(errbuf, errbufsize, "No frames were found in the dcd file");
3110         }
3111         if (errbuf[0] != 0) {
3112                 if (n == 0)
3113                         DcdClose(&dcd);
3114                 return 1;
3115         }
3116
3117         vp = (Vector *)calloc(sizeof(Vector), mp->natoms * dcd.nframes);
3118         xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3119         yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3120         zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3121         ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mp), dcd.nframes, -1);
3122         if (vp == NULL || xp == NULL || yp == NULL || zp == NULL || ig == NULL) {
3123                 snprintf(errbuf, errbufsize, "Cannot allocate memory");
3124                 if (vp) free(vp);
3125                 if (xp) free(xp);
3126                 if (yp) free(yp);
3127                 if (zp) free(zp);
3128                 if (ig) IntGroupRelease(ig);
3129                 return 1;
3130         }
3131         for (n = 0; n < dcd.nframes; n++) {
3132                 int i;
3133                 Vector *vpp;
3134                 if (DcdReadFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
3135                         snprintf(errbuf, errbufsize, "Read error in dcd file");
3136                         goto exit;
3137                 }
3138                 for (i = 0, vpp = &vp[n * mp->natoms]; i < dcd.natoms && i < mp->natoms; i++, vpp++) {
3139                         vpp->x = xp[i];
3140                         vpp->y = yp[i];
3141                         vpp->z = zp[i];
3142                 }
3143         }
3144         /*  TODO: implement frame-specific cells  */
3145         if (MoleculeInsertFrames(mp, ig, vp, NULL) < 0)
3146                 snprintf(errbuf, errbufsize, "Cannot insert frames");
3147         if (dcd.with_unitcell) {
3148                 Vector ax, ay, az, orig;
3149                 char flags[3] = {1, 1, 1};
3150                 ax.x = dcd.globalcell[0]; ax.y = ax.z = 0;
3151                 ay.y = dcd.globalcell[1]; ay.x = ay.z = 0;
3152                 az.z = dcd.globalcell[2]; az.x = az.y = 0;
3153                 orig.x = dcd.globalcell[3];
3154                 orig.y = dcd.globalcell[4];
3155                 orig.z = dcd.globalcell[5];
3156                 if (MoleculeSetPeriodicBox(mp, &ax, &ay, &az, &orig, flags) != 0) {
3157                         snprintf(errbuf, errbufsize, "Cannot set unit cell");
3158                         goto exit;
3159                 }
3160         }
3161         mp->startStep = dcd.nstart;
3162         mp->stepsPerFrame = dcd.ninterval;
3163         mp->psPerStep = dcd.delta;
3164 exit:
3165         DcdClose(&dcd);
3166         free(vp);
3167         free(xp);
3168         free(yp);
3169         free(zp);
3170         IntGroupRelease(ig);
3171         if (errbuf[0] == 0)
3172                 return 0;
3173         else return 1;
3174 }
3175
3176 int
3177 MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3178 {
3179         FILE *fp;
3180         char buf[1024];
3181         int lineNumber;
3182         int i, retval;
3183         Vector v[3], vv;
3184         double d[3];
3185         int n, flag;
3186         char flags[3];
3187         fp = fopen(fname, "rb");
3188         if (fp == NULL) {
3189                 snprintf(errbuf, errbufsize, "Cannot open file");
3190                 return -1;
3191         }
3192         errbuf[0] = 0;
3193         lineNumber = 0;
3194         retval = 0;
3195         flags[0] = flags[1] = flags[2] = 0;
3196         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3197                 if (strncmp(buf, "Bounding box:", 13) == 0) {
3198                         for (i = 0; i < 3; i++) {
3199                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3200                                         snprintf(errbuf, errbufsize, "line %d: missing %d component of the bounding box", lineNumber, i + 1);
3201                                         retval = 1;
3202                                         goto abort;
3203                                 }
3204                                 n = sscanf(buf, "%lf %lf %lf %d", &d[0], &d[1], &d[2], &flag);
3205                                 if (n < 3) {
3206                                         vv.x = vv.y = vv.z = 0.0;
3207                                         switch (i) {
3208                                                 case 0: vv.x = d[0]; break;
3209                                                 case 1: vv.y = d[0]; break;
3210                                                 case 2: vv.z = d[0]; break;
3211                                         }
3212                                         if (n == 1 || (n == 2 && d[1] != 0.0))
3213                                                 flags[i] = 1;
3214                                 } else {
3215                                         vv.x = d[0];
3216                                         vv.y = d[1];
3217                                         vv.z = d[2];
3218                                         if (n == 4)
3219                                                 flags[i] = (flag != 0);
3220                                         else
3221                                                 flags[i] = (VecLength2(vv) != 0);
3222                                 }
3223                                 v[i] = vv;
3224                         }
3225                         if (mp->cell != NULL)
3226                                 vv = mp->cell->origin;
3227                         else
3228                                 vv.x = vv.y = vv.z = 0.0;
3229                         MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags);
3230                 } else if (strncmp(buf, "Bounding box origin:", 20) == 0) {
3231                         if (mp->cell != NULL) {
3232                                 v[0] = mp->cell->axes[0];
3233                                 v[1] = mp->cell->axes[1];
3234                                 v[2] = mp->cell->axes[2];
3235                                 memmove(flags, mp->cell->flags, 3);
3236                         } else {
3237                                 v[0].x = 1.0; v[0].y = v[0].z = 0.0;
3238                                 v[1].y = 1.0; v[1].x = v[1].z = 0.0;
3239                                 v[2].z = 1.0; v[2].x = v[2].y = 0.0;
3240                                 flags[0] = flags[1] = flags[2] = 1.0;
3241                         }
3242                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0 || (n = sscanf(buf, "%lf %lf %lf", &d[0], &d[1], &d[2]) < 3)) {
3243                                 snprintf(errbuf, errbufsize, "line %d: wrong format for the bounding box origin", lineNumber);
3244                                 retval = 1;
3245                                 goto abort;
3246                         }
3247                         vv.x = d[0];
3248                         vv.y = d[1];
3249                         vv.z = d[2];
3250                         MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags);
3251                 }
3252         }
3253         fclose(fp);
3254         return 0;
3255 abort:
3256         if (fp != NULL)
3257                 fclose(fp);
3258         return retval;
3259 }
3260                         
3261 int
3262 MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
3263 {
3264         int retval;
3265         if (ftype == NULL || *ftype == 0) {
3266                 const char *cp;
3267                 cp = strrchr(fname, '.');
3268                 if (cp != NULL)
3269                         ftype = cp + 1;
3270                 else {
3271                         cp = guessMoleculeType(fname);
3272                         if (strcmp(cp, "???") != 0)
3273                                 ftype = cp;
3274                 }
3275         }
3276         if (strcasecmp(ftype, "psf") == 0) {
3277                 retval = MoleculeWriteToPsfFile(mp, fname, errbuf, errbufsize);
3278         } else if (strcasecmp(ftype, "pdb") == 0) {
3279                 retval = MoleculeWriteToPdbFile(mp, fname, errbuf, errbufsize);
3280         } else if (strcasecmp(ftype, "tep") == 0) {
3281                 retval = MoleculeWriteToTepFile(mp, fname, errbuf, errbufsize);
3282         } else {
3283                 snprintf(errbuf, errbufsize, "The file format should be specified");
3284                 retval = 1;
3285         }
3286         if (retval == 0)
3287                 MoleculeSetPath(mp, fname);
3288         return retval;
3289 }
3290
3291 int
3292 MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3293 {
3294         FILE *fp;
3295         int i, j, k, n1, n2, n3;
3296         Atom *ap;
3297         char bufs[6][8];
3298
3299         fp = fopen(fname, "wb");
3300         if (fp == NULL) {
3301                 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
3302                 return 1;
3303         }
3304         errbuf[0] = 0;
3305
3306         fprintf(fp, "!:atoms\n");
3307         fprintf(fp, "! idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge\n");
3308         n1 = n2 = n3 = 0;
3309         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3310                 strncpy(bufs[0], ap->segName, 4);
3311                 strncpy(bufs[1], ap->resName, 4);
3312                 strncpy(bufs[2], ap->aname, 4);
3313                 AtomTypeDecodeToString(ap->type, bufs[3]);
3314                 strncpy(bufs[4], ap->element, 4);
3315                 for (j = 0; j < 5; j++) {
3316                         bufs[j][4] = 0;
3317                         if (bufs[j][0] == 0) {
3318                                 bufs[j][0] = '_';
3319                                 bufs[j][1] = 0;
3320                         }
3321                         for (k = 0; k < 4; k++) {
3322                                 if (bufs[j][k] > 0 && bufs[j][k] < ' ')
3323                                         bufs[j][k] = '_';
3324                         }
3325                 }
3326                 if (SYMOP_ALIVE(ap->symop))
3327                         n1++;
3328                 if (ap->fix_force != 0)
3329                         n2++;
3330                 if (ap->mm_exclude || ap->periodic_exclude)
3331                         n3++;
3332                 fprintf(fp, "%d %s %d %s %s %s %.5f %.5f %s %d %f %f %d\n", i, bufs[0], ap->resSeq, bufs[1], bufs[2], bufs[3], ap->charge, ap->weight, bufs[4], ap->atomicNumber, ap->occupancy, ap->tempFactor, ap->intCharge);
3333         }
3334         fprintf(fp, "\n");
3335         
3336         if (n1 > 0) {
3337                 fprintf(fp, "!:atoms_symop\n");
3338                 fprintf(fp, "! idx symop symbase\n");
3339                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3340                         int n;
3341                         n = ap->symop.sym * 1000000 + ap->symop.dx * 10000 + ap->symop.dy * 100 + ap->symop.dz;
3342                         fprintf(fp, "%d %d %d\n", i, n, ap->symbase);
3343                 }
3344                 fprintf(fp, "\n");
3345         }
3346         
3347         if (n2 > 0) {
3348                 fprintf(fp, "!:atoms_fix\n");
3349                 fprintf(fp, "! idx fix_force fix_pos\n");
3350                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3351                         fprintf(fp, "%d %f %f %f %f\n", i, ap->fix_force, ap->fix_pos.x, ap->fix_pos.y, ap->fix_pos.z);
3352                 }
3353                 fprintf(fp, "\n");
3354         }
3355         
3356         if (n3 > 0) {
3357                 fprintf(fp, "!:mm_exclude\n");
3358                 fprintf(fp, "! idx mm_exclude periodic_exclude\n");
3359                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3360                         fprintf(fp, "%d %d %d\n", i, ap->mm_exclude, ap->periodic_exclude);
3361                 }
3362                 fprintf(fp, "\n");
3363         }
3364         
3365         if ((n1 = MoleculeGetNumberOfFrames(mp)) > 0)
3366                 n2 = mp->cframe;
3367         else
3368                 n2 = 0;
3369         for (i = 0; (i == n2 || i < n1); i++) {
3370                 fprintf(fp, "!:positions ; frame %d\n", i);
3371                 fprintf(fp, "! idx x y z\n");
3372                 for (j = 0, ap = mp->atoms; j < mp->natoms; j++, ap = ATOM_NEXT(ap)) {
3373                         Vector *vp;
3374                         if (i != n2 && i < ap->nframes)
3375                                 vp = ap->frames + i;
3376                         else
3377                                 vp = &(ap->r);
3378                         fprintf(fp, "%d %.8f %.8f %.8f\n", j, vp->x, vp->y, vp->z);
3379                 }
3380                 fprintf(fp, "\n");
3381         }
3382         
3383         if (mp->nbonds > 0) {
3384                 fprintf(fp, "!:bonds\n");
3385                 fprintf(fp, "! from1 to1 from2 to2 from3 to3 from4 to4\n");
3386                 for (i = 0; i < mp->nbonds; i++) {
3387                         fprintf(fp, "%d %d%c", mp->bonds[i * 2], mp->bonds[i * 2 + 1], (i % 4 == 3 || i == mp->nbonds - 1 ? '\n' : ' '));
3388                 }
3389                 fprintf(fp, "\n");
3390         }
3391         
3392         if (mp->nangles > 0) {
3393                 fprintf(fp, "!:angles\n");
3394                 fprintf(fp, "! a1 b1 c1 a2 b2 c2 a3 b3 c3\n");
3395                 for (i = 0; i < mp->nangles; i++) {
3396                         fprintf(fp, "%d %d %d%c", mp->angles[i * 3], mp->angles[i * 3 + 1], mp->angles[i * 3 + 2], (i % 3 == 2 || i == mp->nangles - 1 ? '\n' : ' '));
3397                 }
3398                 fprintf(fp, "\n");
3399         }
3400         
3401         if (mp->ndihedrals > 0) {
3402                 fprintf(fp, "!:dihedrals\n");
3403                 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3404                 for (i = 0; i < mp->ndihedrals; i++) {
3405                         fprintf(fp, "%d %d %d %d%c", mp->dihedrals[i * 4], mp->dihedrals[i * 4 + 1], mp->dihedrals[i * 4 + 2], mp->dihedrals[i * 4 + 3], (i % 2 == 1 || i == mp->ndihedrals - 1 ? '\n' : ' '));
3406                 }
3407                 fprintf(fp, "\n");
3408         }
3409         
3410         if (mp->nimpropers > 0) {
3411                 fprintf(fp, "!:impropers\n");
3412                 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3413                 for (i = 0; i < mp->nimpropers; i++) {
3414                         fprintf(fp, "%d %d %d %d%c", mp->impropers[i * 4], mp->impropers[i * 4 + 1], mp->impropers[i * 4 + 2], mp->impropers[i * 4 + 3], (i % 2 == 1 || i == mp->nimpropers - 1 ? '\n' : ' '));
3415                 }
3416                 fprintf(fp, "\n");
3417         }
3418         
3419         if (mp->cell != NULL) {
3420                 fprintf(fp, "!:periodic_box\n");
3421                 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz; fa fb fc\n");
3422                 for (i = 0; i < 3; i++)
3423                         fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->axes[i].x, mp->cell->axes[i].y, mp->cell->axes[i].z);
3424                 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->origin.x, mp->cell->origin.y, mp->cell->origin.z);
3425                 fprintf(fp, "%d %d %d\n", mp->cell->flags[0], mp->cell->flags[1], mp->cell->flags[2]);
3426                 fprintf(fp, "\n");
3427
3428                 fprintf(fp, "!:xtalcell\n");
3429                 fprintf(fp, "! a b c alpha beta gamma; this info is redundant, periodic_box is used instead\n");
3430                 fprintf(fp, "%f %f %f %f %f %f\n", mp->cell->cell[0], mp->cell->cell[1], mp->cell->cell[2], mp->cell->cell[3], mp->cell->cell[4], mp->cell->cell[5]);
3431                 fprintf(fp, "\n");
3432         }
3433         
3434         if (mp->nsyms > 0) {
3435                 fprintf(fp, "!:symmetry_operations\n");
3436                 fprintf(fp, "! a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3\n");
3437                 for (i = 0; i < mp->nsyms; i++) {
3438                         Transform *tp = mp->syms + i;
3439                         for (j = 0; j < 12; j++)
3440                                 fprintf(fp, "%11.6f%c", (*tp)[j], (j % 3 == 2 ? '\n' : ' '));
3441                 }
3442                 fprintf(fp, "\n");
3443         }
3444         
3445         if (mp->arena != NULL) {
3446                 MDArena *arena = mp->arena;
3447                 fprintf(fp, "!:md_parameters\n");
3448                 fprintf(fp, "log_file %s\n", arena->log_result_name);
3449                 fprintf(fp, "coord_file %s\n", arena->coord_result_name);
3450                 fprintf(fp, "vel_file %s\n", arena->vel_result_name);
3451                 fprintf(fp, "force_file %s\n", arena->force_result_name);
3452                 fprintf(fp, "debug_file %s\n", arena->debug_result_name);
3453                 fprintf(fp, "debug_output_level %d\n", arena->debug_output_level);
3454                 fprintf(fp, "step %d\n", arena->step);
3455                 fprintf(fp, "coord_output_freq %d\n", arena->coord_output_freq);
3456                 fprintf(fp, "energy_output_freq %d\n", arena->energy_output_freq);
3457                 fprintf(fp, "coord_frame %d\n", arena->coord_result_frame);
3458                 fprintf(fp, "timestep %g\n", arena->timestep);
3459                 fprintf(fp, "cutoff %g\n", arena->cutoff);
3460                 fprintf(fp, "electro_cutoff %g\n", arena->electro_cutoff);
3461                 fprintf(fp, "pairlist_distance %g\n", arena->pairlist_distance);
3462                 fprintf(fp, "temperature %g\n", arena->temperature);
3463                 fprintf(fp, "andersen_freq %d\n", arena->andersen_thermo_freq);
3464                 fprintf(fp, "andersen_coupling %g\n", arena->andersen_thermo_coupling);
3465                 fprintf(fp, "random_seed %d\n", arena->random_seed);
3466                 fprintf(fp, "dielectric %g\n", arena->dielectric);
3467                 fprintf(fp, "gradient_convergence %g\n", arena->gradient_convergence);
3468                 fprintf(fp, "coordinate_convergence %g\n", arena->coordinate_convergence);
3469                 fprintf(fp, "use_xplor_shift %d\n", arena->use_xplor_shift);
3470                 fprintf(fp, "scale14_vdw %g\n", arena->scale14_vdw);
3471                 fprintf(fp, "scale14_elect %g\n", arena->scale14_elect);
3472                 fprintf(fp, "relocate_center %d\n", arena->relocate_center);
3473                 fprintf(fp, "surface_probe_radius %g\n", arena->probe_radius);
3474                 fprintf(fp, "surface_tension %g\n", arena->surface_tension);
3475                 fprintf(fp, "surface_potential_freq %d\n", arena->surface_potential_freq);
3476                 fprintf(fp, "use_graphite %d\n", arena->use_graphite);
3477                 fprintf(fp, "alchemical_lambda %g\n", arena->alchem_lambda);
3478                 fprintf(fp, "alchemical_delta_lambda %g\n", arena->alchem_dlambda);
3479                 if (arena->nalchem_flags > 0) {
3480                         fprintf(fp, "alchem_flags %d", arena->nalchem_flags);
3481                         for (i = 0; i < arena->nalchem_flags; i++) {
3482                                 if (i % 60 == 0)
3483                                         fputc('\n', fp);
3484                                 else if (i % 10 == 0)
3485                                         fputc(' ', fp);
3486                                 fputc('0' + arena->alchem_flags[i], fp);
3487                         }
3488                         fputc('\n', fp);
3489                 }
3490                 if (arena->pressure != NULL) {
3491                         Double *dp;
3492                         fprintf(fp, "pressure_freq %d\n", arena->pressure->freq);
3493                         fprintf(fp, "pressure_coupling %g\n", arena->pressure->coupling);
3494                         dp = arena->pressure->apply;
3495                         fprintf(fp, "pressure %g %g %g %g %g %g %g %g %g\n", dp[0], dp[1], dp[2], dp[3], dp[4], dp[5], dp[6], dp[7], dp[8]);
3496                         dp = arena->pressure->cell_flexibility;
3497                         fprintf(fp, "pressure_cell_flexibility %g %g %g %g %g %g %g %g\n", dp[0], dp[1], dp[2], dp[3], dp[4], dp[5], dp[6], dp[7]);
3498                         fprintf(fp, "pressure_fluctuate_cell_origin %g\n", arena->pressure->fluctuate_cell_origin);
3499                         fprintf(fp, "pressure_fluctuate_cell_orientation %g\n", arena->pressure->fluctuate_cell_orientation);
3500                 }
3501                 fprintf(fp, "\n");
3502
3503                 if (mp->par != NULL) {
3504                         Parameter *par = mp->par;
3505                         fprintf(fp, "!:parameters\n");
3506                         ParameterAppendToFile(par, fp);
3507                         fprintf(fp, "\n");
3508                 }
3509                 
3510                 fprintf(fp, "!:velocity\n");
3511                 fprintf(fp, "! idx vx vy vz\n");
3512                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3513                         fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->v.x, ap->v.y, ap->v.z);
3514                 }
3515                 fprintf(fp, "\n");
3516
3517                 fprintf(fp, "!:force\n");
3518                 fprintf(fp, "! idx fx fy fz\n");
3519                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3520                         fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->f.x, ap->f.y, ap->f.z);
3521                 }
3522                 fprintf(fp, "\n");
3523         }
3524         
3525         if (mp->mview != NULL) {
3526                 float f[4];
3527                 if (mp->mview->track != NULL) {
3528                         fprintf(fp, "!:trackball\n");
3529                         fprintf(fp, "! scale; trx try trz; theta_deg x y z\n");
3530                         f[0] = TrackballGetScale(mp->mview->track);
3531                         fprintf(fp, "%f\n", f[0]);
3532                         TrackballGetTranslate(mp->mview->track, f);
3533                         fprintf(fp, "%f %f %f\n", f[0], f[1], f[2]);
3534                         TrackballGetRotate(mp->mview->track, f);
3535                         fprintf(fp, "%f %f %f %f\n", f[0], f[1], f[2], f[3]);
3536                         fprintf(fp, "\n");
3537                 }
3538                 fprintf(fp, "!:view\n");
3539                 fprintf(fp, "show_unit_cell %d\n", mp->mview->showUnitCell);
3540                 fprintf(fp, "show_periodic_box %d\n", mp->mview->showPeriodicBox);
3541                 fprintf(fp, "show_expanded_atoms %d\n", mp->mview->showExpandedAtoms);
3542                 fprintf(fp, "show_ellipsoids %d\n", mp->mview->showEllipsoids);
3543                 fprintf(fp, "show_hydrogens %d\n", mp->mview->showHydrogens);
3544                 fprintf(fp, "show_dummy_atoms %d\n", mp->mview->showDummyAtoms);
3545                 fprintf(fp, "show_rotation_center %d\n", mp->mview->showRotationCenter);
3546                 fprintf(fp, "show_graphite_flag %d\n", mp->mview->showGraphiteFlag);
3547                 fprintf(fp, "show_graphite %d\n", mp->mview->showGraphite);
3548                 fprintf(fp, "show_periodic_image_flag %d\n", mp->mview->showPeriodicImageFlag);
3549                 fprintf(fp, "show_periodic_image %d %d %d %d %d %d\n",
3550                                 mp->mview->showPeriodicImage[0], mp->mview->showPeriodicImage[1],
3551                                 mp->mview->showPeriodicImage[2], mp->mview->showPeriodicImage[3],
3552                                 mp->mview->showPeriodicImage[4], mp->mview->showPeriodicImage[5]);
3553                 fprintf(fp, "\n");
3554         }
3555
3556         fclose(fp);
3557         return 0;
3558 }
3559
3560 int
3561 MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3562 {
3563         FILE *fp;
3564         int i;
3565         Atom *ap;
3566         fp = fopen(fname, "wb");
3567         if (fp == NULL) {
3568                 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
3569                 return 1;
3570         }
3571         errbuf[0] = 0;
3572         fprintf(fp, "PSF\n\n");
3573         fprintf(fp, "       1 !NTITLE\n");
3574         fprintf(fp, " REMARKS FILENAME=\n");
3575         fprintf(fp, "\n");
3576         
3577         /*  Atoms  */
3578         fprintf(fp, "%8d !NATOM\n", mp->natoms);
3579         for (i = 0; i < mp->natoms; i++) {
3580                 const char *fmt;
3581                 ap = ATOM_AT_INDEX(mp->atoms, i);
3582                 fprintf(fp, "%8d ", i + 1);
3583                 if (ap->resSeq >= 10000) {
3584                         fmt = "%-3.3s %-5d ";
3585                 } else {
3586                         fmt = "%-4.4s %-4d ";
3587                 }
3588                 fprintf(fp, fmt, ap->segName, ap->resSeq);
3589                 fprintf(fp, "%-3.3s  %-4.4s %-4.4s   %12.6f  %8.4f           0\n",
3590                         ap->resName, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->charge, ap->weight);
3591         }
3592         fprintf(fp, "\n");
3593         
3594         /*  Bonds  */
3595         fprintf(fp, "%8d !NBOND: bonds\n", mp->nbonds);
3596         for (i = 0; i < mp->nbonds * 2; i++) {
3597                 fprintf(fp, "%8d", mp->bonds[i] + 1);
3598                 if (i % 8 == 7)
3599                         fprintf(fp, "\n");
3600         }
3601         if (i % 8 != 0)
3602                 fprintf(fp, "\n");
3603         fprintf(fp, "\n");
3604         
3605         /*  Angles  */
3606         fprintf(fp, "%8d !NTHETA: angles\n", mp->nangles);
3607         for (i = 0; i < mp->nangles * 3; i++) {
3608                 fprintf(fp, "%8d", mp->angles[i] + 1);
3609                 if (i % 9 == 8)
3610                         fprintf(fp, "\n");
3611         }
3612         if (i % 9 != 0)
3613                 fprintf(fp, "\n");
3614         fprintf(fp, "\n");
3615         
3616         /*  Dihedrals  */
3617         fprintf(fp, "%8d !NPHI: dihedrals\n", mp->ndihedrals);
3618         for (i = 0; i < mp->ndihedrals * 4; i++) {
3619                 fprintf(fp, "%8d", mp->dihedrals[i] + 1);
3620                 if (i % 8 == 7)
3621                         fprintf(fp, "\n");
3622         }
3623         if (i % 8 != 0)
3624                 fprintf(fp, "\n");
3625         fprintf(fp, "\n");
3626         
3627         /*  Dihedrals  */
3628         fprintf(fp, "%8d !NIMPHI: impropers\n", mp->nimpropers);
3629         for (i = 0; i < mp->nimpropers * 4; i++) {
3630                 fprintf(fp, "%8d", mp->impropers[i] + 1);
3631                 if (i % 8 == 7)
3632                         fprintf(fp, "\n");
3633         }
3634         if (i % 8 != 0)
3635                 fprintf(fp, "\n");
3636         fprintf(fp, "\n");
3637         
3638         fprintf(fp, "%8d !NDON: donors\n\n", 0);
3639         fprintf(fp, "%8d !NACC: acceptors\n\n", 0);
3640         fprintf(fp, "%8d !NNB: non-bonding exclusions\n\n", 0);
3641         for (i = 0; i < mp->natoms; i++) {
3642                 fprintf(fp, "%8d", 0);
3643                 if (i % 8 == 7)
3644                         fprintf(fp, "\n");
3645         }
3646         if (i % 8 != 0)
3647                 fprintf(fp, "\n");
3648         fprintf(fp, "\n");
3649         fprintf(fp, "%8d !NGRP: groups\n", 1);
3650         fprintf(fp, "       0       0       0\n");
3651         fprintf(fp, "\n");
3652         
3653         i = strlen(fname);
3654         if (i > 5 && strcmp(fname + i - 5, ".psfx") == 0) {
3655                 /*  Extended psf (with coordinates and other info)  */
3656                 fprintf(fp, "%8d !COORD: coordinates\n", mp->natoms);
3657                 for (i = 0; i < mp->natoms; i++) {
3658                         Vector r;
3659                         ap = ATOM_AT_INDEX(mp->atoms, i);
3660                         r = ap->r;
3661                         fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->aname);
3662                 }
3663                 fprintf(fp, "\n");
3664 #if 0
3665                 if (mp->nframes > 0) {
3666                         int fn;  /*  Frame number  */
3667                         for (fn = 0; fn < ap->nframes; fn++) {
3668                                 fprintf(fp, "%8d !COORD: coordinates for frame %d\n", mp->natoms, fn);
3669                                 for (i = 0; i < mp->natoms; i++) {
3670                                         Vector r;
3671                                         ap = ATOM_AT_INDEX(mp->atoms, i);
3672                                         if (ap->frames == NULL || fn >= ap->nframes)
3673                                                 r = ap->r;
3674                                         else
3675                                                 r = ap->frames[fn];
3676                                         fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->name);
3677                                 }
3678                                 fprintf(fp, "\n");
3679                         }
3680                 }
3681 #endif
3682         }
3683                 
3684         fclose(fp);
3685         return 0;
3686 }
3687
3688 int
3689 MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3690 {
3691         FILE *fp;
3692         int i, j;
3693         Atom *ap;
3694         fp = fopen(fname, "wb");
3695         if (fp == NULL) {
3696                 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
3697                 return 1;
3698         }
3699         errbuf[0] = 0;
3700         for (i = 0; i < mp->natoms; i++) {
3701                 char buf[6];
3702                 ap = ATOM_AT_INDEX(mp->atoms, i);
3703                 if (ap->resSeq >= 10000) {
3704                         snprintf(buf, sizeof buf, "%c%03d", 'A' + (ap->resSeq - 10000) / 1000, ap->resSeq % 1000);
3705                 } else {
3706                         snprintf(buf, sizeof buf, "%4d", ap->resSeq);
3707                 }
3708                 fprintf(fp, "ATOM  %5d %-4.4s%1.1s%-3.3s %1.1s%4.4s%1.1s   "
3709                                         "%8.3f%8.3f%8.3f %5.2f %5.2f      "
3710                                         "%-4.4s%-2.2s%-2d\n",
3711                         i + 1, ap->aname, " ", ap->resName, " ", buf, " ",
3712                         ap->r.x, ap->r.y, ap->r.z, ap->occupancy, ap->tempFactor,
3713                         ap->segName, ap->element, ap->intCharge);
3714         }
3715         for (i = 0; i < mp->natoms; i++) {
3716                 ap = ATOM_AT_INDEX(mp->atoms, i);
3717                 for (j = 0; j < ap->nconnects; j++) {
3718                         if (j % 4 == 0) {
3719                                 if (j > 0)
3720                                         fprintf(fp, "\n");
3721                                 fprintf(fp, "CONECT%5d", i + 1);
3722                         }
3723                         fprintf(fp, "%5d", ap->connects[j] + 1);
3724                 }
3725                 if (j > 0)
3726                         fprintf(fp, "\n");
3727         }
3728         fprintf(fp, "END\n");
3729         fclose(fp);
3730         return 0;
3731 }
3732
3733 int
3734 MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3735 {
3736         DcdRecord dcd;
3737         SFloat32 *xp, *yp, *zp;
3738         int n;
3739         errbuf[0] = 0;
3740         if (mp == NULL || mp->natoms == 0) {
3741                 snprintf(errbuf, errbufsize, "Molecule is empty");
3742                 return 1;
3743         }
3744         memset(&dcd, 0, sizeof(dcd));
3745         dcd.natoms = mp->natoms;
3746         dcd.nframes = MoleculeGetNumberOfFrames(mp);
3747         if (dcd.nframes == 0) {
3748                 snprintf(errbuf, errbufsize, "no frame is present");
3749                 return 1;
3750         }
3751         dcd.nstart = mp->startStep;
3752         dcd.ninterval = mp->stepsPerFrame;
3753         if (dcd.ninterval == 0)
3754                 dcd.ninterval = 1;
3755         dcd.nend = dcd.nstart + (dcd.nframes - 1) * dcd.ninterval;
3756         if (mp->cell != NULL) {
3757                 dcd.with_unitcell = 1;
3758                 dcd.globalcell[0] = VecLength(mp->cell->axes[0]);
3759                 dcd.globalcell[1] = VecLength(mp->cell->axes[1]);
3760                 dcd.globalcell[2] = VecLength(mp->cell->axes[2]);
3761                 dcd.globalcell[3] = mp->cell->origin.x;
3762                 dcd.globalcell[4] = mp->cell->origin.y;
3763                 dcd.globalcell[5] = mp->cell->origin.z;
3764         }
3765         dcd.delta = mp->psPerStep;
3766         if (dcd.delta == 0.0)
3767                 dcd.delta = 1.0;
3768         n = DcdCreate(fname, &dcd);
3769         if (n != 0) {
3770                 if (n < 0)
3771                         snprintf(errbuf, errbufsize, "Cannot create dcd file");
3772                 else
3773                         snprintf(errbuf, errbufsize, "Cannot write dcd header");
3774                 DcdClose(&dcd);
3775                 return 1;
3776         }
3777         
3778         xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3779         yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3780         zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3781         if (xp == NULL || yp == NULL || zp == NULL) {
3782                 snprintf(errbuf, errbufsize, "Cannot allocate memory");
3783                 if (xp) free(xp);
3784                 if (yp) free(yp);
3785                 if (zp) free(zp);
3786                 DcdClose(&dcd);
3787                 return 1;
3788         }
3789         for (n = 0; n < dcd.nframes; n++) {
3790                 int i;
3791                 Atom *ap;
3792                 for (i = 0, ap = mp->atoms; i < dcd.natoms && mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3793                         Vector r;
3794                         if (ap->frames == NULL || n >= ap->nframes)
3795                                 r = ap->r;
3796                         else
3797                                 r = ap->frames[n];
3798                         xp[i] = r.x;
3799                         yp[i] = r.y;
3800                         zp[i] = r.z;
3801                 }
3802                 if (i < dcd.natoms) {
3803                         size_t sz = (dcd.natoms - i) * sizeof(SFloat32);
3804                         memset(xp + i, 0, sz);
3805                         memset(yp + i, 0, sz);
3806                         memset(zp + i, 0, sz);
3807                 }
3808                 if (DcdWriteFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
3809                         snprintf(errbuf, errbufsize, "Write error in dcd file");
3810                         goto exit;
3811                 }
3812         }
3813         
3814 exit:
3815         DcdClose(&dcd);
3816         free(xp);
3817         free(yp);
3818         free(zp);
3819         if (errbuf[0] == 0)
3820                 return 0;
3821         else return 1;
3822 }
3823
3824 int
3825 MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3826 {
3827         FILE *fp;
3828         int i;
3829         Vector v;
3830         errbuf[0] = 0;
3831         fp = fopen(fname, "wb");
3832         if (fp == NULL) {
3833                 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
3834                 return 1;
3835         }
3836         if (mp->cell != NULL) {
3837                 fprintf(fp, "Bounding box:\n");
3838                 for (i = 0; i < 3; i++) {
3839                         v = mp->cell->axes[i];
3840                         fprintf(fp, "%.3f %.3f %.3f %d\n", v.x, v.y, v.z, mp->cell->flags[i]);
3841                 }
3842                 fprintf(fp, "Bounding box origin:\n");
3843                 v = mp->cell->origin;
3844                 fprintf(fp, "%.3f %.3f %.3f\n", v.x, v.y, v.z);
3845         }
3846         fclose(fp);
3847         return 0;
3848 }
3849                 
3850  static int
3851 sCompareByElement(const void *ap, const void *bp)
3852 {
3853         return ((*(Atom **)bp)->atomicNumber - (*(Atom **)ap)->atomicNumber);
3854 }
3855
3856 static int
3857 sMakeAdc(int n, int base, Symop symop)
3858 {
3859         int an, sym;
3860         if (SYMOP_ALIVE(symop)) {
3861                 an = base;
3862                 sym = (symop.dx + 5) * 10000 + (symop.dy + 5) * 1000 + (symop.dz + 5) * 100 + symop.sym + 1;
3863         } else {
3864                 an = n;
3865                 sym = 55501;
3866         }
3867         return (an + 1) * 100000 + sym;
3868 }
3869
3870 static int
3871 sCompareAdc(const void *ap, const void *bp)
3872 {
3873         int n = *((Int *)ap) % 100000 - *((Int *)bp) % 100000;
3874         if (n == 0)
3875                 n = *((Int *)ap) / 100000 - *((Int *)bp) / 100000;
3876         return n;
3877 }
3878
3879 static void
3880 sOutputAtomListInstructions(FILE *fp, int natoms, Atom *atoms)
3881 {
3882         int i, j, k, an, sym;
3883         Atom *ap;
3884         Int *adc;
3885         adc = (Int *)malloc(sizeof(Int) * natoms);
3886         if (adc == NULL)
3887                 return;
3888         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
3889                 if (ap->exflags & kAtomHiddenFlag)
3890                         continue;
3891                 adc[i] = sMakeAdc(i, ap->symbase, ap->symop);
3892         }
3893         mergesort(adc, natoms, sizeof(Int), sCompareAdc);
3894         
3895         /*  Create the atom list  */
3896         an = sym = -1;
3897         for (i = j = k = 0; i < natoms; i++) {
3898                 int an1 = adc[i] / 100000;
3899                 int sym1 = adc[i] % 100000;
3900                 if (sym == sym1 && an1 == an + 1) {
3901                         /*  Continuous  */
3902                         an = an1;
3903                         k++;
3904                         continue;
3905                 }
3906                 if (k > 0)
3907                         /*  Output the last atom with a minus sign  */
3908                         adc[j++] = -(an * 100000 + sym);
3909                 /*  Output this atom  */
3910                 adc[j++] = adc[i];
3911                 an = an1;
3912                 sym = sym1;
3913                 k = 0;
3914         }
3915         if (k > 0)
3916                 adc[j++] = -(an * 100000 + sym);
3917         
3918         /*  Create the instruction cards  */
3919         for (i = k = 0; i < j; i++) {
3920                 if (k == 0)
3921                         fprintf(fp, "      401");
3922                 fprintf(fp, "%9d", adc[i]);
3923                 k++;
3924                 if (i == j - 1 || k == 6 || (k == 5 && i < j - 2 && adc[i + 2] < 0)) {
3925                         fprintf(fp, "\n");
3926                         k = 0;
3927                 }
3928         }
3929         free(adc);
3930 }
3931
3932 static int
3933 sEllipsoidType(int an)
3934 {
3935         return (an >= 18 ? 3 : (an >= 2 && an != 6 ? 2 : (an > 0 ? 1 : 0)));
3936 }
3937
3938 static void
3939 sOutputAtomTypeInstructions(FILE *fp, int natoms, Atom *atoms)
3940 {
3941         int i;
3942         Atom *ap;
3943         int etype, elast, istart, ilast, n1, n2;
3944         elast = istart = ilast = -1;
3945         for (i = 0, ap = atoms; i <= natoms; i++, ap++) {
3946                 if (i < natoms) {
3947                         if (SYMOP_ALIVE(ap->symop))
3948                                 continue;
3949                         if (ap->exflags & kAtomHiddenFlag)
3950                                 continue;
3951                         etype = sEllipsoidType(ap->atomicNumber);
3952                         if (elast < 0) {
3953                                 istart = ilast = i;
3954                                 elast = etype;
3955                                 continue;
3956                         } else if (elast == etype && ilast == i - 1) {
3957                                 ilast++;
3958                                 continue;
3959                         }
3960                 }
3961                 /*  Output the instruction card for the 'last' block of atoms  */
3962                 switch (etype) {
3963                         case 2:
3964                                 n1 = 4; n2 = 0; break;
3965                         case 3:
3966                                 n1 = 4; n2 = 5; break;
3967                         default:
3968                                 n1 = 1; n2 = 0; break;
3969                 }
3970                 fprintf(fp, "  1   715 %8d        0 %8d        0    0.100    0.000    0.000\n", n1, n2);
3971                 fprintf(fp, "                           %9d%9d\n", istart + 1, ilast + 1);
3972                 elast = etype;
3973                 ilast = istart = i;
3974         }
3975 }
3976
3977 static int
3978 sCompareBondType(const void *ap, const void *bp)
3979 {
3980         /*  Descending order  */
3981         return *((int *)bp) - *((int *)ap);
3982 }
3983
3984 static void
3985 sOutputBondInstructions(FILE *fp, int natoms, Atom *atoms, int overlap_correction)
3986 {
3987         Atom *ap, *ap2;
3988         char buf[96];
3989         int i, j, n[5], an, count, n1, n2, k;
3990         Int nexbonds;
3991         Int *exbonds;
3992         static const float sBondRad[4] = {0.060, 0.060, 0.060, 0.040};
3993         static const int sBondShade[4] = {5, 3, 1, 1};
3994
3995         n[0] = n[1] = n[2] = n[3] = 0;  /*  Start index of 3rd row atoms (and higher), 2nd row, 1st row, and H */
3996         n[4] = natoms;
3997         for (i = natoms - 1, ap = atoms + i; i >= 0; i--, ap--) {
3998                 an = ap->atomicNumber;
3999                 if (an < 2)
4000                         n[3] = i;
4001                 if (an < 10)
4002                         n[2] = i;
4003                 if (an < 18)
4004                         n[1] = i;
4005         }
4006         nexbonds = 0;
4007         exbonds = NULL;
4008         count = 0;
4009
4010         if (overlap_correction)
4011                 strcpy(buf, "  2  1001    0.000\n");
4012         else
4013                 strcpy(buf, "  2   812\n");
4014         
4015         for (i = 0; i < 4; i++) {
4016                 for (j = i; j < 4; j++) {
4017                         /*  Examine bonds between "group i" and "group j"  */
4018                         Vector dr;
4019                         double d;
4020                         double min_bond = 10000.0;     /*  Minimum distance between bound atoms  */
4021                         double min_nonbond = 10000.0;  /*  Minimum distance between non-bound atoms  */
4022                         double max_bond = -10000.0;    /*  Maximum distance between bound atoms  */
4023                         int count_exbond = 0;          /*  Number of explicit bonds in this group  */
4024                         for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
4025                                 for (n2 = n[j], ap2 = atoms + n2; n2 < n[j + 1]; n2++, ap2++) {
4026                                         if (n1 == n2)
4027                                                 continue;
4028                                         VecSub(dr, ap->r, ap2->r);
4029                                         d = VecLength(dr);
4030                                         for (k = ap->nconnects - 1; k >= 0; k--) {
4031                                                 if (ap->connects[k] == n2)
4032                                                         break;
4033                                         }
4034                                         if (k >= 0) {
4035                                                 /*  n1 and n2 are bound  */
4036                                                 if (d < min_bond)
4037                                                         min_bond = d;
4038                                                 if (d > max_bond)
4039                                                         max_bond = d;
4040                                         } else {
4041                                                 /*  n1 and n2 are not bound  */
4042                                                 if (d < min_nonbond)
4043                                                         min_nonbond = d;
4044                                         }
4045                                 }
4046                         }
4047                         if (min_bond == 10000.0)
4048                                 continue;  /*  No bonds between these groups  */
4049                         min_bond *= 0.9;
4050                         if (max_bond + 0.002 < min_nonbond)
4051                                 max_bond += 0.002;
4052                         else {
4053                                 max_bond = min_nonbond - 0.002;
4054                                 /*  Some bonds may be omitted, so scan all bonds again  */
4055                                 for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
4056                                         for (k = ap->nconnects - 1; k >= 0; k--) {
4057                                                 n2 = ap->connects[k];
4058                                                 if (n2 < n[j] || n2 >= n[j + 1])
4059                                                         continue;
4060                                                 ap2 = atoms + n2;
4061                                                 VecSub(dr, ap->r, ap2->r);
4062                                                 d = VecLength(dr);
4063                                                 if (d > max_bond) {
4064                                                         /*  This bond should be explicitly defined  */
4065                                                         Int adc1, adc2;
4066                                                         if (count_exbond == 0) {
4067                                                                 adc1 = -(i + 1);  /*  Bond type  */
4068                                                                 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4069                                                         }
4070                                                         adc1 = sMakeAdc(n1, ap->symbase, ap->symop);
4071                                                         adc2 = sMakeAdc(n2, ap2->symbase, ap2->symop);
4072                                                         AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4073                                                         AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc2);
4074                                                         count_exbond++;
4075                                                 }
4076                                         }
4077                                 }
4078                         }
4079                         /*  Output the last instruction card  */
4080                         fputs(buf, fp);
4081                         /*  Make a new trailer card  */
4082                         snprintf(buf, sizeof(buf), "  2      %3d%3d%3d%3d%3d%6.3f%6.3f%6.3f\n", n[i]+1, n[i+1], n[j]+1, n[j+1], sBondShade[i], min_bond, max_bond, sBondRad[i]);
4083                         count++;
4084                 }
4085         }
4086         if (count > 0) {
4087                 /*  Output the last trailer card  */
4088                 buf[2] = ' ';
4089                 fputs(buf, fp);
4090         }
4091         if (nexbonds > 0) {
4092                 if (count == 0 && overlap_correction) {
4093                         /*  1001 card is not yet written, so write it  */
4094                         buf[2] = ' ';
4095                         fputs(buf, fp);
4096                 }
4097                 snprintf(buf, sizeof(buf), "  1   %3d", (overlap_correction ? 821 : 811));
4098                 k = -exbonds[0] - 1;  /*  Bond type for the first block  */
4099                 i = 1;  /*  Index for exbonds[]  */
4100                 j = 0;  /*  Count in this block  */
4101                 while (i <= nexbonds) {
4102                         if (j >= 29 || i == nexbonds || exbonds[i] < 0) {
4103                                 /*  End of block  */
4104                                 buf[2] = '2';
4105                                 fputs(buf, fp);
4106                                 /*  The trailer card  */
4107                                 fprintf(fp, "                     %3d            %6.3f\n", sBondShade[k], sBondRad[k]);
4108                                 if (i == nexbonds)
4109                                         break;
4110                                 if (exbonds[i] < 0)
4111                                         k = -exbonds[i++] - 1;  /*  The new bond type  */
4112                                 j = 0;
4113                         } else if (j > 0 && j % 3 == 0) {
4114                                 buf[2] = '1';
4115                                 fputs(buf, fp);
4116                         }
4117                         n1 = exbonds[i++];
4118                         n2 = exbonds[i++];
4119                         snprintf(buf + 9 + (j % 3) * 18, sizeof(buf) - 9 - (j % 3) * 18, "%9d%9d\n", n1, n2);
4120                         j++;
4121                 }
4122                 free(exbonds);
4123         }
4124 }
4125         
4126 #if 0
4127 {
4128         /*  Explicit bond table, sorted by bond type  */
4129         for (i = j = 0; i < mp->nbonds; i++) {
4130                 n1 = mp->bonds[i * 2];
4131                 n2 = mp->bonds[i * 2 + 1];
4132                 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
4133                 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
4134                 if ((ap1->exflags & kAtomHiddenFlag) || (ap2->exflags & kAtomHiddenFlag))
4135                         continue;
4136                 if (ap1->atomicNumber > 18 || ap2->atomicNumber > 18) {
4137                         type = 3;
4138                 } else if (ap1->atomicNumber > 1 && ap1->atomicNumber > 1) {
4139                         type = 2;
4140                 } else {
4141                         type = 1;
4142                 }
4143                 ip[j * 3] = type;
4144                 ip[j * 3 + 1] = sMakeAdc(n1, ap1->symbase, ap1->symop);
4145                 ip[j * 3 + 2] = sMakeAdc(n2, ap2->symbase, ap2->symop);
4146                 j++;
4147         }
4148         mergesort(ip, j, sizeof(int) * 3, sCompareBondType);
4149         
4150         /*  Output instruction cards  */
4151         strcpy(buf, "  1   811");
4152         for (i = n1 = 0; i < j; i++) {
4153                 n2 = (n1 % 3) * 18 + 9;
4154                 snprintf(buf + n2, 80 - n2, "%9d%9d\n", ip[i * 3 + 1], ip[i * 3 + 2]);
4155                 if (i == j - 1 || n1 >= 29 || ip[i * 3] != ip[i * 3 + 3]) {
4156                         /*  End of this instruction  */
4157                         buf[2] = '2';
4158                         fputs(buf, fp);
4159                         switch (ip[i * 3]) {
4160                                 case 3: rad = 0.06; nshades = 5; break;
4161                                 case 2: rad = 0.06; nshades = 1; break;
4162                                 default: rad = 0.04; nshades = 1; break;
4163                         }
4164                         fprintf(fp, "                     %3d            %6.3f\n", nshades, rad);
4165                         strcpy(buf, "  1   811");
4166                         n1 = 0;
4167                         continue;
4168                 } else if (n1 % 3 == 2) {
4169                         fputs(buf, fp);
4170                         strcpy(buf, "  1      ");
4171                 }
4172                 n1++;
4173         }
4174         free(ip);
4175 }
4176 #endif
4177
4178 int
4179 MoleculeWriteToTepFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
4180 {
4181         FILE *fp;
4182         int i, j, natoms, *ip;
4183         Atom *ap, *atoms, **app;
4184         Double *cp;
4185         
4186         errbuf[0] = 0;
4187
4188         /*  Create sorted array of atoms  */
4189         natoms = mp->natoms;
4190         atoms = (Atom *)calloc(sizeof(Atom), natoms);
4191         app = (Atom **)calloc(sizeof(Atom *), natoms);
4192         ip = (int *)calloc(sizeof(int), natoms);
4193         if (atoms == NULL || app == NULL || ip == NULL) {
4194                 snprintf(errbuf, errbufsize, "Cannot allocate memory");
4195                 return 1;
4196         }
4197         /*  Sort the atom pointer by atomic number  */
4198         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
4199                 app[i] = ap;
4200         mergesort(app, natoms, sizeof(Atom *), sCompareByElement);
4201         for (i = 0; i < natoms; i++) {
4202                 /*  ip[old_index] is new_index  */
4203                 ip[app[i] - mp->atoms] = i;
4204         }
4205         /*  Move the atom record to atoms[]  */
4206         /*  aniso and frames share the memory with the original  */
4207         /*  The 'v' member contains crystallographic coordinates  */
4208         /*  The connection table and symbase are renumbered  */
4209         /*  Hidden flags are modified to reflect the visibility in the MainView  */
4210         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4211                 memmove(ap, app[i], gSizeOfAtomRecord);
4212                 MoleculeCartesianToXtal(mp, &(ap->v), &(ap->r));
4213                 for (j = ap->nconnects - 1; j >= 0; j--) {
4214                         ap->connects[j] = ip[ap->connects[j]];
4215                 }
4216                 if (SYMOP_ALIVE(ap->symop))
4217                         ap->symbase = ip[ap->symbase];
4218                 if (MainView_isAtomHidden(mp->mview, i)) {
4219                         ap->exflags |= kAtomHiddenFlag;
4220                 } else {
4221                         ap->exflags &= ~kAtomHiddenFlag;
4222                 }
4223         }
4224         free(ip);
4225         free(app);
4226         
4227         fp = fopen(fname, "wb");
4228         if (fp == NULL) {
4229                 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
4230                 return 1;
4231         }
4232         errbuf[0] = 0;
4233
4234         /*  Title line  */
4235         fprintf(fp, "Generated by MyMolView\n");
4236         
4237         /*  XtalCell  */
4238         if (mp->cell != NULL) {
4239                 cp = mp->cell->cell;
4240         } else {
4241                 static Double sUnit[] = {1, 1, 1, 90, 90, 90};
4242                 cp = sUnit;
4243         }
4244         fprintf(fp, "%9.3f%9.3f%9.3f%9.3f%9.3f%9.3f\n", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
4245         
4246         /*  Symmetry operations  */
4247         if (mp->nsyms > 0) {
4248                 for (i = 0; i < mp->nsyms; i++) {
4249                         cp = mp->syms[i];
4250                         fprintf(fp, "%c%14g%3g%3g%3g%15g%3g%3g%3g%15g%3g%3g%3g\n", (i == mp->nsyms - 1 ? '1' : ' '), cp[9], cp[0], cp[1], cp[2], cp[10], cp[3], cp[4], cp[5], cp[11], cp[6], cp[7], cp[8]);
4251                 }
4252         } else {
4253                 fprintf(fp, "1             0  1  0  0              0  0  1  0              0  0  0  1\n");
4254         }
4255         
4256         /*  Atoms  */
4257         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4258                 /*  The 'v' field contains crystallographic coordinates  */
4259                 fprintf(fp, " %4.4s%22s%9.4f%9.4f%9.4f%9d\n", ap->aname, "", ap->v.x, ap->v.y, ap->v.z, 0);
4260                 if (ap->aniso != NULL) {
4261                         cp = ap->aniso->bij;
4262                         fprintf(fp, " %8.5f%9.6f%9.6f%9.6f%9.6f%9.6f%9d\n", cp[0], cp[1], cp[2], cp[3], cp[5], cp[4], 0);
4263                 } else {
4264                         Double temp = ap->tempFactor;
4265                         if (temp <= 0)
4266                                 temp = 1.2;
4267                         fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", temp, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4268                 }
4269         }
4270         /*  Special points  */
4271         {
4272                 Vector camera, lookat, up, xvec, yvec, zvec;
4273                 MainView_getCamera(mp->mview, &camera, &lookat, &up);
4274                 VecSub(zvec, lookat, camera);
4275                 VecCross(xvec, zvec, up);
4276                 NormalizeVec(&xvec, &xvec);
4277                 NormalizeVec(&yvec, &up);
4278                 VecInc(xvec, lookat);
4279                 VecInc(yvec, lookat);
4280                 MoleculeCartesianToXtal(mp, &lookat, &lookat);
4281                 MoleculeCartesianToXtal(mp, &xvec, &xvec);
4282                 MoleculeCartesianToXtal(mp, &yvec, &yvec);
4283                 fprintf(fp, " ORGN                      %9g%9g%9g        0\n", 0.0, 0.0, 0.0);
4284                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4285                 fprintf(fp, " CNTR                      %9g%9g%9g        0\n", lookat.x, lookat.y, lookat.z);
4286                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4287                 fprintf(fp, " X                         %9g%9g%9g        0\n", xvec.x, xvec.y, xvec.z);
4288                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4289                 fprintf(fp, " Y                         %9g%9g%9g        0\n", yvec.x, yvec.y, yvec.z);
4290                 fprintf(fp, "1%8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4291         }
4292         
4293         /*  Instructions  */
4294         fprintf(fp, "      201\n");
4295         fprintf(fp, "      205       12\n");
4296         fprintf(fp, "      301      6.6      6.6        0      0.8\n");
4297         sOutputAtomListInstructions(fp, natoms, atoms);
4298         fprintf(fp, "      501%4d55501%4d55501%4d55501%4d55501%4d55501                 1\n", natoms + 2, natoms + 2, natoms + 3, natoms + 2, natoms + 4);
4299         fprintf(fp, "      502        1      0.0        2      0.0        3      0.0\n");
4300         fprintf(fp, "      604                               1.538\n");
4301
4302         sOutputBondInstructions(fp, natoms, atoms, 1);
4303         sOutputAtomTypeInstructions(fp, natoms, atoms);
4304         sOutputBondInstructions(fp, natoms, atoms, 0);
4305
4306         fprintf(fp, "      202\n");
4307         fprintf(fp, "  0    -1\n");
4308         fclose(fp);
4309         return 0;
4310 }
4311
4312 void
4313 MoleculeDump(Molecule *mol)
4314 {
4315         int i, j;
4316         Atom *ap;
4317         for (i = 0; i < mol->natoms; i++) {
4318                 char buf1[8];
4319                 ap = ATOM_AT_INDEX(mol->atoms, i);
4320                 snprintf(buf1, sizeof buf1, "%3.4s.%d", ap->resName, ap->resSeq);
4321                 fprintf(stderr, "%4d %-7s %-4.6s %-4.6s %-2.2s %7.3f %7.3f %7.3f %6.3f [", i, buf1, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->element, ap->r.x, ap->r.y, ap->r.z, ap->charge);
4322                 for (j = 0; j < ap->nconnects; j++) {
4323                         fprintf(stderr, "%s%d", (j > 0 ? "," : ""), ap->connects[j]);
4324                 }
4325                 fprintf(stderr, "]\n");
4326         }
4327 }
4328
4329 #pragma mark ====== MD support (including modification of Molecule) ======
4330
4331 /*  Call md_prepare for the MDArena. If MDArena has not been created, a new arena is created.
4332         If something goes wrong, returns 1 (for missing parameters) or -1 (more serious error).
4333     If retmsg is not NULL, a message describing the problem is returned there. This message
4334     must be free'd by the caller.  */
4335 int
4336 MoleculePrepareMDArena(Molecule *mol, int check_only, char **retmsg)
4337 {
4338         const char *msg;
4339         Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
4340         Int missing = 0;
4341         IntGroup *ig1, *ig2, *ig3;
4342         MDArena *arena = mol->arena;
4343
4344         if (arena == NULL) {
4345                 md_arena_new(mol);
4346                 arena = mol->arena;
4347         } else if (arena->xmol != mol)
4348                 md_arena_set_molecule(arena, mol);
4349
4350         arena->is_initialized = 0;
4351         
4352         /*  Rebuild the tables  */
4353         ig1 = ig2 = ig3 = NULL;
4354         nangles = MoleculeFindMissingAngles(mol, &angles);
4355         ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
4356         nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
4357         if (nangles > 0) {
4358                 ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
4359                 MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
4360                 free(angles);
4361                 IntGroupRelease(ig1);
4362         }
4363         if (ndihedrals > 0) {
4364                 ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
4365                 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
4366                 free(dihedrals);
4367                 IntGroupRelease(ig2);
4368         }
4369         if (nimpropers > 0) {
4370                 ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
4371                 MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
4372                 free(impropers);
4373                 IntGroupRelease(ig3);
4374         }
4375         
4376         /*  Prepare parameters and internal information  */
4377         msg = md_prepare(arena, check_only);
4378         
4379         /*  Some parameters are missing?  */
4380         if (msg != NULL) {
4381                 if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
4382                         missing = 1;
4383                 else {
4384                         if (retmsg != NULL)
4385                                 asprintf(retmsg, "cannot initialize for MD: %s", msg);
4386                         return -1;
4387                 }
4388         }
4389         
4390         /*  The local parameter list is updated  */
4391         {
4392                 Int parType, idx;
4393                 if (mol->par == NULL)
4394                         mol->par = ParameterNew();
4395                 for (parType = kFirstParType; parType <= kLastParType; parType++) {
4396                         /*  Delete global and undefined parameters  */
4397                         UnionPar *up, *upbuf;
4398                         Int nparams, count;
4399                         ig1 = IntGroupNew();
4400                         for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
4401                                 if (up->bond.src != 0)
4402                                         IntGroupAdd(ig1, idx, 1);
4403                         }
4404                         if (IntGroupGetCount(ig1) > 0)
4405                                 MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
4406                         IntGroupRelease(ig1);
4407                         /*  Copy global and undefined parameters from arena and insert to mol->par  */
4408                         nparams = ParameterGetCountForType(arena->par, parType);
4409                         if (nparams == 0)
4410                                 continue;
4411                         upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
4412                         ig1 = IntGroupNew();
4413                         ig2 = IntGroupNew();
4414                         for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
4415                                 if (up->bond.src > 0)
4416                                         IntGroupAdd(ig1, idx, 1); /* Global parameter */
4417                                 else if (up->bond.src < 0)
4418                                         IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
4419                         }
4420                         if ((count = IntGroupGetCount(ig1)) > 0) {
4421                                 /*  Insert global parameters (at the top)  */
4422                                 ParameterCopy(arena->par, parType, upbuf, ig1);
4423                                 ig3 = IntGroupNewWithPoints(0, count, -1);
4424                                 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
4425                                 IntGroupRelease(ig3);
4426                         }
4427                         if ((count = IntGroupGetCount(ig2)) > 0) {
4428                                 /*  Insert undefined parameters (at the bottom)  */
4429                                 ParameterCopy(arena->par, parType, upbuf, ig2);
4430                                 idx = ParameterGetCountForType(mol->par, parType);
4431                                 ig3 = IntGroupNewWithPoints(idx, count, -1);
4432                                 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
4433                                 IntGroupRelease(ig3);
4434                         }
4435                         IntGroupRelease(ig2);
4436                         IntGroupRelease(ig1);
4437                         free(upbuf);
4438                 }
4439                 mol->needsMDRebuild = 0;  /*  We know the "modified" parameters are consistent with the MDArena  */
4440         }
4441         
4442         if (missing) {
4443                 if (retmsg != NULL)
4444                         *retmsg = strdup(msg);
4445                 return 1;
4446         } else return 0;
4447 }
4448
4449 #pragma mark ====== Serialize ======
4450
4451 Molecule *
4452 MoleculeDeserialize(const char *data, Int length, Int *timep)
4453 {
4454         Molecule *mp;
4455         Parameter *par;
4456 /*      int result; */
4457
4458         mp = MoleculeNew();
4459         if (mp == NULL)
4460                 goto out_of_memory;
4461         par = ParameterNew();
4462         if (par == NULL)
4463                 goto out_of_memory;
4464
4465         while (length >= 12) {
4466                 const char *ptr = data + 8 + sizeof(Int);
4467                 int len = *((const Int *)(data + 8));
4468                 int i, j, n;
4469                 if (strcmp(data, "ATOM") == 0) {
4470                         n = len / gSizeOfAtomRecord;
4471                         NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, n);
4472                         memmove(mp->atoms, ptr, len);
4473                 } else if (strcmp(data, "ANISO") == 0) {
4474                         Atom *ap;
4475                         n = len / (sizeof(Int) + sizeof(Aniso));
4476                         for (i = 0; i < n; i++) {
4477                                 j = *((const Int *)ptr);
4478                                 if (j < 0 || j >= mp->natoms)
4479                                         goto bad_format;
4480                                 ap = ATOM_AT_INDEX(mp->atoms, j);
4481                                 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
4482                                 if (ap->aniso == NULL)
4483                                         goto out_of_memory;
4484                                 *(ap->aniso) = *((Aniso *)(ptr + sizeof(Int)));
4485                                 ptr += sizeof(Int) + sizeof(Aniso);
4486                         }
4487                 } else if (strcmp(data, "FRAME") == 0) {
4488                         Atom *ap;
4489                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4490                                 if (ap->nframes == 0)
4491                                         continue;
4492                                 ap->frames = (Vector *)malloc(sizeof(Vector) * ap->nframes);
4493                                 if (ap->frames == NULL)
4494                                         goto out_of_memory;
4495                                 memmove(ap->frames, ptr, sizeof(Vector) * ap->nframes);
4496                                 ptr += sizeof(Vector) * ap->nframes;
4497                         }
4498                 } else if (strcmp(data, "BOND") == 0) {
4499                         n = len / (sizeof(Int) * 2);
4500                         NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, n);
4501                         memmove(mp->bonds, ptr, len);
4502                 } else if (strcmp(data, "ANGLE") == 0) {
4503                         n = len / (sizeof(Int) * 3);
4504                         NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, n);
4505                         memmove(mp->angles, ptr, len);
4506                 } else if (strcmp(data, "DIHED") == 0) {
4507                         n = len / (sizeof(Int) * 4);
4508                         NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, n);
4509                         memmove(mp->dihedrals, ptr, len);
4510                 } else if (strcmp(data, "IMPROP") == 0) {
4511                         n = len / (sizeof(Int) * 4);
4512                         NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, n);
4513                         memmove(mp->impropers, ptr, len);
4514                 } else if (strcmp(data, "RESIDUE") == 0) {
4515                         n = len / 4;
4516                         NewArray(&mp->residues, &mp->nresidues, 4, n);
4517                         memmove(mp->residues, ptr, len);
4518                 } else if (strcmp(data, "CELL") == 0) {
4519                         mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
4520                         if (mp->cell == NULL)
4521                                 goto out_of_memory;
4522                         memmove(mp->cell, ptr, sizeof(XtalCell));
4523                 } else if (strcmp(data, "SYMOP") == 0) {
4524                         n = len / sizeof(Transform);
4525                         NewArray(&mp->syms, &mp->nsyms, sizeof(Transform), n);
4526                         memmove(mp->syms, ptr, len);
4527                 } else if (strcmp(data, "EXATOM") == 0) {
4528                         n = len / sizeof(ExAtom);
4529                         NewArray(&mp->exatoms, &mp->nexatoms, sizeof(ExAtom), n);
4530                         memmove(mp->exatoms, ptr, len);
4531                 } else if (strcmp(data, "EXBOND") == 0) {
4532                         n = len / (sizeof(Int) * 2);
4533                         NewArray(&mp->exbonds, &mp->nexbonds, sizeof(Int) * 2, n);
4534                         memmove(mp->exbonds, ptr, len);
4535                 } else if (strcmp(data, "TIME") == 0) {
4536                         if (timep != NULL)
4537                                 *timep = *((Int *)ptr);
4538                 } else if (strcmp(data, "BONDPAR") == 0) {
4539                         mp->par = par;
4540                         n = len / sizeof(BondPar);
4541                         NewArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), n);
4542                         memmove(par->bondPars, ptr, len);
4543                 } else if (strcmp(data, "ANGPAR") == 0) {
4544                         mp->par = par;
4545                         n = len / sizeof(AnglePar);
4546                         NewArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), n);
4547                         memmove(par->anglePars, ptr, len);
4548                 } else if (strcmp(data, "DIHEPAR") == 0) {
4549                         mp->par = par;
4550                         n = len / sizeof(TorsionPar);
4551                         NewArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), n);
4552                         memmove(par->dihedralPars, ptr, len);
4553                 } else if (strcmp(data, "IMPRPAR") == 0) {
4554                         mp->par = par;
4555                         n = len / sizeof(TorsionPar);
4556                         NewArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), n);
4557                         memmove(par->improperPars, ptr, len);
4558                 } else if (strcmp(data, "VDWPAR") == 0) {
4559                         mp->par = par;
4560                         n = len / sizeof(VdwPar);
4561                         NewArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), n);
4562                         memmove(par->vdwPars, ptr, len);
4563                 } else if (strcmp(data, "VDWPPAR") == 0) {
4564                         mp->par = par;
4565                         n = len / sizeof(VdwPairPar);
4566                         NewArray(&par->vdwpPars, &par->nvdwpPars, sizeof(VdwPairPar), n);
4567                         memmove(par->vdwpPars, ptr, len);
4568                 } else if (strcmp(data, "VCUTPAR") == 0) {
4569                         mp->par = par;
4570                         n = len / sizeof(VdwCutoffPar);
4571                         NewArray(&par->vdwCutoffPars, &par->nvdwCutoffPars, sizeof(VdwCutoffPar), n);
4572                         memmove(par->vdwCutoffPars, ptr, len);
4573                 }
4574                 len += 8 + sizeof(Int);
4575                 data += len;
4576                 length -= len;
4577         }
4578         if (mp->par == NULL)
4579                 ParameterRelease(par);
4580 /*      result = MoleculeRebuildTablesFromConnects(mp);
4581         if (result != 0)
4582                 goto bad_format; */
4583         return mp;
4584         
4585   out_of_memory:
4586         Panic("Low memory while deserializing molecule data");
4587         return NULL; /* Not reached */
4588
4589   bad_format:
4590         Panic("internal error: bad format during deserializing molecule data");
4591         return NULL; /* Not reached */
4592 }
4593
4594 char *
4595 MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep)
4596 {
4597         char *ptr, *p;
4598         int len, len_all, i, naniso, nframes;
4599
4600         /*  Array of atoms  */
4601         len = 8 + sizeof(Int) + gSizeOfAtomRecord * mp->natoms;
4602         ptr = (char *)malloc(len);
4603         if (ptr == NULL)
4604                 goto out_of_memory;
4605         memmove(ptr, "ATOM\0\0\0\0", 8);
4606         *((Int *)(ptr + 8)) = gSizeOfAtomRecord * mp->natoms;
4607         p = ptr + 8 + sizeof(Int);
4608         memmove(p, mp->atoms, gSizeOfAtomRecord * mp->natoms);
4609         naniso = nframes = 0;
4610         for (i = 0; i < mp->natoms; i++) {
4611                 Atom *ap = ATOM_AT_INDEX(p, i);
4612                 if (ap->aniso != NULL) {
4613                         naniso++;
4614                         ap->aniso = NULL;
4615                 }
4616                 if (ap->frames != NULL) {
4617                         nframes += ap->nframes;
4618                         ap->frames = NULL;
4619                 }
4620         }
4621         len_all = len;
4622
4623         /*  Array of aniso  */
4624         if (naniso > 0) {
4625                 len = 8 + sizeof(Int) + (sizeof(Int) + sizeof(Aniso)) * naniso;
4626                 ptr = (char *)realloc(ptr, len_all + len);
4627                 if (ptr == NULL)
4628                         goto out_of_memory;
4629                 p = ptr + len_all;
4630                 memmove(p, "ANISO\0\0\0", 8);
4631                 *((Int *)(p + 8)) = (sizeof(Int) + sizeof(Aniso)) * naniso;
4632                 p += 8 + sizeof(Int);
4633                 for (i = 0; i < mp->natoms; i++) {
4634                         Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
4635                         if (ap->aniso != NULL) {
4636                                 *((Int *)p) = i;
4637                                 *((Aniso *)(p + sizeof(Int))) = *(ap->aniso);
4638                                 p += sizeof(Int) + sizeof(Aniso);
4639                         }
4640                 }
4641                 len_all += len;
4642         }
4643         
4644         /*  Array of frames  */
4645         if (nframes > 0) {
4646                 len = 8 + sizeof(Int) + sizeof(Vector) * nframes;
4647                 ptr = (char *)realloc(ptr, len_all + len);
4648                 if (ptr == NULL)
4649                         goto out_of_memory;
4650                 p = ptr + len_all;
4651                 memmove(p, "FRAME\0\0\0", 8);
4652                 *((Int *)(p + 8)) = sizeof(Vector) * nframes;
4653                 p += 8 + sizeof(Int);
4654                 for (i = 0; i < mp->natoms; i++) {
4655                         Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
4656                         if (ap->frames != NULL) {
4657                                 memmove(p, ap->frames, sizeof(Vector) * ap->nframes);
4658                                 p += sizeof(Vector) * ap->nframes;
4659                         }
4660                 }
4661                 len_all += len;
4662         }
4663         
4664         /*  Bonds, angles, dihedrals, impropers  */
4665         if (mp->nbonds > 0) {
4666                 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nbonds;
4667                 ptr = (char *)realloc(ptr, len_all + len);
4668                 if (ptr == NULL)
4669                         goto out_of_memory;
4670                 p = ptr + len_all;
4671                 memmove(p, "BOND\0\0\0\0", 8);
4672                 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nbonds;
4673                 p += 8 + sizeof(Int);
4674                 memmove(p, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
4675                 len_all += len;
4676         }
4677         if (mp->nangles > 0) {
4678                 len = 8 + sizeof(Int) + sizeof(Int) * 3 * mp->nangles;
4679                 ptr = (char *)realloc(ptr, len_all + len);
4680                 if (ptr == NULL)
4681                         goto out_of_memory;
4682                 p = ptr + len_all;
4683                 memmove(p, "ANGLE\0\0\0", 8);
4684                 *((Int *)(p + 8)) = sizeof(Int) * 3 * mp->nangles;
4685                 p += 8 + sizeof(Int);
4686                 memmove(p, mp->angles, sizeof(Int) * 3 * mp->nangles);
4687                 len_all += len;
4688         }
4689         if (mp->ndihedrals > 0) {
4690                 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->ndihedrals;
4691                 ptr = (char *)realloc(ptr, len_all + len);
4692                 if (ptr == NULL)
4693                         goto out_of_memory;
4694                 p = ptr + len_all;
4695                 memmove(p, "DIHED\0\0\0", 8);
4696                 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->ndihedrals;
4697                 p += 8 + sizeof(Int);
4698                 memmove(p, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
4699                 len_all += len;
4700         }
4701         if (mp->nimpropers > 0) {
4702                 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->nimpropers;
4703                 ptr = (char *)realloc(ptr, len_all + len);
4704                 if (ptr == NULL)
4705                         goto out_of_memory;
4706                 p = ptr + len_all;
4707                 memmove(p, "IMPROP\0\0", 8);
4708                 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->nimpropers;
4709                 p += 8 + sizeof(Int);
4710                 memmove(p, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
4711                 len_all += len;
4712         }
4713         
4714         /*  Array of residues  */
4715         if (mp->nresidues > 0) {
4716                 len = 8 + sizeof(Int) + 4 * mp->nresidues;
4717                 ptr = (char *)realloc(ptr, len_all + len);
4718                 if (ptr == NULL)
4719                         goto out_of_memory;
4720                 p = ptr + len_all;
4721                 memmove(p, "RESIDUE\0", 8);
4722                 *((Int *)(p + 8)) = 4 * mp->nresidues;
4723                 p += 8 + sizeof(Int);
4724                 memmove(p, mp->residues, 4 * mp->nresidues);
4725                 len_all += len;
4726         }
4727
4728         /*  Unit cell  */
4729         if (mp->cell != NULL) {
4730                 len = 8 + sizeof(Int) + sizeof(XtalCell);
4731                 ptr = (char *)realloc(ptr, len_all + len);
4732                 if (ptr == NULL)
4733                         goto out_of_memory;
4734                 p = ptr + len_all;
4735                 memmove(p, "CELL\0\0\0\0", 8);
4736                 *((Int *)(p + 8)) = sizeof(XtalCell);
4737                 p += 8 + sizeof(Int);
4738                 memmove(p, mp->cell, sizeof(XtalCell));
4739                 len_all += len;
4740         }
4741         
4742         /*  Symmetry operations  */
4743         if (mp->nsyms > 0) {
4744                 len = 8 + sizeof(Int) + sizeof(Transform) * mp->nsyms;
4745                 ptr = (char *)realloc(ptr, len_all + len);
4746                 if (ptr == NULL)
4747                         goto out_of_memory;
4748                 p = ptr + len_all;
4749                 memmove(p, "SYMOP\0\0\0", 8);
4750                 *((Int *)(p + 8)) = sizeof(Transform) * mp->nsyms;
4751                 p += 8 + sizeof(Int);
4752                 memmove(p, mp->syms, sizeof(Transform) * mp->nsyms);
4753                 len_all += len;
4754         }
4755         
4756         /*  Expanded atoms  */
4757         if (mp->nexatoms > 0) {
4758                 len = 8 + sizeof(Int) + sizeof(ExAtom) * mp->nexatoms;
4759                 ptr = (char *)realloc(ptr, len_all + len);
4760                 if (ptr == NULL)
4761                         goto out_of_memory;
4762                 p = ptr + len_all;
4763                 memmove(p, "EXATOM\0\0", 8);
4764                 *((Int *)(p + 8)) = sizeof(ExAtom) * mp->nexatoms;
4765                 p += 8 + sizeof(Int);
4766                 memmove(p, mp->exatoms, sizeof(ExAtom) * mp->nexatoms);
4767                 for (i = 0; i < mp->nexatoms; i++) {
4768                         /*  Clear label id  */
4769                         ((ExAtom *)p)[i].labelid = 0;
4770                 }
4771                 len_all += len;
4772         }
4773         
4774         /*  Expanded bonds  */
4775         if (mp->nexbonds > 0) {
4776                 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nexbonds;
4777                 ptr = (char *)realloc(ptr, len_all + len);
4778                 if (ptr == NULL)
4779                         goto out_of_memory;
4780                 p = ptr + len_all;
4781                 memmove(p, "EXBOND\0\0", 8);
4782                 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nexbonds;
4783                 p += 8 + sizeof(Int);
4784                 memmove(p, mp->exbonds, sizeof(Int) * 2 * mp->nexbonds);
4785                 len_all += len;
4786         }
4787         
4788         /*  Parameters  */
4789         if (mp->par != NULL) {
4790                 int type;
4791                 for (type = kFirstParType; type <= kLastParType; type++) {
4792                         const char *parname;
4793                         Int parsize, parcount;
4794                         void *parptr;
4795                         switch (type) {
4796                                 case kBondParType:
4797                                         parname = "BONDPAR\0";
4798                                         parsize = sizeof(BondPar);
4799                                         parcount = mp->par->nbondPars;
4800                                         parptr = mp->par->bondPars;
4801                                         break;
4802                                 case kAngleParType:
4803                                         parname = "ANGPAR\0\0";
4804                                         parsize = sizeof(AnglePar);
4805                                         parcount = mp->par->nanglePars;
4806                                         parptr = mp->par->anglePars;
4807                                         break;
4808                                 case kDihedralParType:
4809                                         parname = "DIHEPAR\0";
4810                                         parsize = sizeof(TorsionPar);
4811                                         parcount = mp->par->ndihedralPars;
4812                                         parptr = mp->par->dihedralPars;
4813                                         break;
4814                                 case kImproperParType:
4815                                         parname = "IMPRPAR\0";
4816                                         parsize = sizeof(TorsionPar);
4817                                         parcount = mp->par->nimproperPars;
4818                                         parptr = mp->par->improperPars;
4819                                         break;
4820                                 case kVdwParType:
4821                                         parname = "VDWPAR\0\0";
4822                                         parsize = sizeof(VdwPar);
4823                                         parcount = mp->par->nvdwPars;
4824                                         parptr = mp->par->vdwPars;
4825                                         break;
4826                                 case kVdwPairParType:
4827                                         parname = "VDWPPAR\0";
4828                                         parsize = sizeof(VdwPairPar);
4829                                         parcount = mp->par->nvdwpPars;
4830                                         parptr = mp->par->vdwpPars;
4831                                         break;
4832                                 case kVdwCutoffParType:
4833                                         parname = "VCUTPAR\0";
4834                                         parsize = sizeof(VdwCutoffPar);
4835                                         parcount = mp->par->nvdwCutoffPars;
4836                                         parptr = mp->par->vdwCutoffPars;
4837                                         break;
4838                                 default:
4839                                         continue;
4840                         }
4841                         if (parcount > 0) {
4842                                 len = 8 + sizeof(Int) + parsize * parcount;
4843                                 ptr = (char *)realloc(ptr, len_all + len);
4844                                 if (ptr == NULL)
4845                                         goto out_of_memory;
4846                                 p = ptr + len_all;
4847                                 memmove(p, parname, 8);
4848                                 *((Int *)(p + 8)) = parsize * parcount;
4849                                 p += 8 + sizeof(Int);
4850                                 memmove(p, parptr, parsize * parcount);
4851                                 len_all += len;
4852                         }
4853                 }
4854         }
4855         
4856         /*  Time stamp  */
4857         {
4858                 time_t tm = time(NULL);
4859                 len = 8 + sizeof(Int) + sizeof(Int);
4860                 ptr = (char *)realloc(ptr, len_all + len);
4861                 if (ptr == NULL)
4862                         goto out_of_memory;
4863                 p = ptr + len_all;
4864                 memmove(p, "TIME\0\0\0\0", 8);
4865                 *((Int *)(p + 8)) = sizeof(Int);
4866                 p += 8 + sizeof(Int);
4867                 *((Int *)p) = (Int)tm;
4868                 len_all += len;
4869                 if (timep != NULL)
4870                         *timep = (Int)tm;
4871         }
4872         
4873         if (outLength != NULL)
4874                 *outLength = len_all;
4875         return ptr;
4876
4877   out_of_memory:
4878     Panic("Low memory while serializing a molecule data");
4879         return NULL; /* Not reached */  
4880 }
4881
4882 #pragma mark ====== Search for bonds, angles, dihedrals, impropers ======
4883
4884 static IntGroup *
4885 sMoleculeSearchIncludingAtoms(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
4886 {
4887         int i, j;
4888         Int *ip;
4889         IntGroup *gp = NULL;
4890         if (atomgroup == NULL)
4891                 return NULL;
4892         for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
4893                 for (j = 0; j < nsize; j++) {
4894                         if (IntGroupLookup(atomgroup, ip[j], NULL) != 0) {
4895                                 if (gp == NULL)
4896                                         gp = IntGroupNew();
4897                                 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
4898                                         Panic("Low memory while searching %s", msg);
4899                                 break;
4900                         }
4901                 }
4902         }
4903         return gp;
4904 }
4905
4906 IntGroup *
4907 MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
4908 {
4909         if (mp == NULL)
4910                 return NULL;
4911         return sMoleculeSearchIncludingAtoms(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
4912 }
4913
4914 IntGroup *
4915 MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
4916 {
4917         if (mp == NULL)
4918                 return NULL;
4919         return sMoleculeSearchIncludingAtoms(mp->nangles, mp->angles, 3, atomgroup, "angles");
4920 }
4921
4922 IntGroup *
4923 MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
4924 {
4925         if (mp == NULL)
4926                 return NULL;
4927         return sMoleculeSearchIncludingAtoms(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
4928 }
4929
4930 IntGroup *
4931 MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
4932 {
4933         if (mp == NULL)
4934                 return NULL;
4935         return sMoleculeSearchIncludingAtoms(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
4936 }
4937
4938 static IntGroup *
4939 sMoleculeSearchAcrossAtomGroup(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
4940 {
4941         int i, j;
4942         Int *ip;
4943         IntGroup *gp = NULL;
4944         if (atomgroup == NULL)
4945                 return NULL;
4946         for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
4947                 int k = -1;
4948                 for (j = 0; j < nsize; j++) {
4949                         int kk;
4950                         kk = (IntGroupLookup(atomgroup, ip[j], NULL) != 0);
4951                         if (k < 0)
4952                                 k = kk;
4953                         else if (k != kk) {
4954                                 /*  This bond etc. crosses the atom group border  */
4955                                 if (gp == NULL)
4956                                         gp = IntGroupNew();
4957                                 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
4958                                         Panic("Low memory while searching %s", msg);
4959                                 break;
4960                         }
4961                 }
4962         }
4963         return gp;
4964 }
4965
4966 IntGroup *
4967 MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
4968 {
4969         if (mp == NULL)
4970                 return NULL;
4971         return sMoleculeSearchAcrossAtomGroup(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
4972 }
4973
4974 IntGroup *
4975 MoleculeSearchAnglesAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
4976 {
4977         if (mp == NULL)
4978                 return NULL;
4979         return sMoleculeSearchAcrossAtomGroup(mp->nangles, mp->angles, 3, atomgroup, "angles");
4980 }
4981
4982 IntGroup *
4983 MoleculeSearchDihedralsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
4984 {
4985         if (mp == NULL)
4986                 return NULL;
4987         return sMoleculeSearchAcrossAtomGroup(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
4988 }
4989
4990 IntGroup *
4991 MoleculeSearchImpropersAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
4992 {
4993         if (mp == NULL)
4994                 return NULL;
4995         return sMoleculeSearchAcrossAtomGroup(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
4996 }
4997
4998 /*  Guess the bonds from the coordinates  */
4999 int
5000 MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds)
5001 {
5002         int i, j, n1, n2;
5003         Int nbonds, *bonds;
5004         Atom *ap, *bp;
5005         Vector r1, r2, dr;
5006         Double a1, a2, alim;
5007         Int newbond[2];
5008         ElementPar *p = gElementParameters;
5009         nbonds = 0;
5010         bonds = NULL;
5011         for (i = 0; i < mp->natoms; i++) {
5012                 ap = ATOM_AT_INDEX(mp->atoms, i);
5013                 n1 = ap->atomicNumber;
5014                 if (n1 >= 0 && n1 < gCountElementParameters)
5015                         a1 = p[n1].radius;
5016                 else a1 = p[6].radius;
5017                 r1 = ap->r;
5018         /*      if (mp->is_xtal_coord)
5019                         TransformVec(&r1, mp->cell->tr, &r1); */
5020                 for (j = 0; j < i; j++) {
5021                         bp = ATOM_AT_INDEX(mp->atoms, j);
5022                         n2 = bp->atomicNumber;
5023                         if (n2 >= 0 && n2 < gCountElementParameters)
5024                                 a2 = p[n2].radius;
5025                         else a2 = p[6].radius;
5026                         r2 = bp->r;
5027                 /*      if (mp->is_xtal_coord)
5028                                 TransformVec(&r2, mp->cell->tr, &r2); */
5029                         VecSub(dr, r1, r2);
5030                         alim = limit * (a1 + a2);
5031                         if (VecLength2(dr) < alim * alim) {
5032                                 newbond[0] = i;
5033                                 newbond[1] = j;
5034                         /*      MoleculeAddBonds(mp, 1, newbonds); */
5035                                 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5036                         }
5037                 }
5038         }
5039         if (nbonds > 0) {
5040                 newbond[0] = kInvalidIndex;
5041                 newbond[1] = 0;
5042                 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5043                 nbonds--;
5044         }
5045         if (outNbonds != NULL)
5046                 *outNbonds = nbonds;
5047         if (outBonds != NULL)
5048                 *outBonds = bonds;
5049         return 0;
5050 }
5051
5052 /*  Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information  */
5053 int
5054 MoleculeRebuildTablesFromConnects(Molecule *mp)
5055 {
5056         int i, j, k, retval;
5057         Atom *ap;
5058         Int ibuf[6];
5059         
5060         __MoleculeLock(mp);
5061
5062         /*  Find bonds   */
5063         if (mp->nbonds == 0) {
5064                 for (i = 0; i < mp->natoms; i++) {
5065                         ap = ATOM_AT_INDEX(mp->atoms, i);
5066                         for (j = 0; j < ap->nconnects; j++) {
5067                                 k = ap->connects[j];
5068                                 if (i >= k)
5069                                         continue;
5070                                 ibuf[0] = i;
5071                                 ibuf[1] = k;
5072                                 /*  MoleculeAddBonds() should not be used, because it assumes connects[] and
5073                                     bonds are already in sync  */
5074                                 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
5075                         /*      retval = MoleculeAddBonds(mp, 1, ibuf);
5076                                 if (retval != 0)
5077                                         goto abort; */
5078                         }
5079                 }
5080         }
5081         
5082         /*  Find angles  */
5083         if (mp->nangles == 0) {
5084                 for (i = 0; i < mp->natoms; i++) {
5085                         ap = ATOM_AT_INDEX(mp->atoms, i);
5086                         for (j = 0; j < ap->nconnects; j++) {
5087                                 for (k = j + 1; k < ap->nconnects; k++) {
5088                                         ibuf[0] = ap->connects[j];
5089                                         ibuf[1] = i;
5090                                         ibuf[2] = ap->connects[k];
5091                                         ibuf[3] = -1;
5092                                         retval = MoleculeAddAngles(mp, ibuf, NULL);
5093                                         if (retval < 0)
5094                                                 goto abort;
5095                                 }
5096                         }
5097                 }
5098         }
5099         
5100         /*  Find dihedrals  */
5101         if (mp->ndihedrals == 0) {
5102                 for (i = 0; i < mp->natoms; i++) {
5103                         ap = ATOM_AT_INDEX(mp->atoms, i);
5104                         for (j = 0; j < ap->nconnects; j++) {
5105                                 int jj, kk, mm, m;
5106                                 Atom *apjj;
5107                                 jj = ap->connects[j];
5108                                 if (i >= jj)
5109                                         continue;
5110                                 apjj = ATOM_AT_INDEX(mp->atoms, jj);
5111                                 for (k = 0; k < ap->nconnects; k++) {
5112                                         if (k == j)
5113                                                 continue;
5114                                         kk = ap->connects[k];
5115                                         for (m = 0; m < apjj->nconnects; m++) {
5116                                                 mm = apjj->connects[m];
5117                                                 if (mm == i || mm == kk)
5118                                                         continue;
5119                                                 ibuf[0] = kk;
5120                                                 ibuf[1] = i;
5121                                                 ibuf[2] = jj;
5122                                                 ibuf[3] = mm;
5123                                                 ibuf[4] = -1;
5124                                                 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
5125                                                 if (retval < 0)
5126                                                         goto abort;
5127                                         }
5128                                 }
5129                         }
5130                 }
5131         }
5132         
5133         /*  Find impropers  */
5134         if (mp->nimpropers == 0) {
5135                 for (i = 0; i < mp->natoms; i++) {
5136                         int i1, i2, i4, n1, n2, n4;
5137                         ap = ATOM_AT_INDEX(mp->atoms, i);
5138                         for (i1 = 0; i1 < ap->nconnects; i1++) {
5139                                 n1 = ap->connects[i1];
5140                                 for (i2 = i1 + 1; i2 < ap->nconnects; i2++) {
5141                                         n2 = ap->connects[i2];
5142                                         for (i4 = i2 + 1; i4 < ap->nconnects; i4++) {
5143                                                 n4 = ap->connects[i4];
5144                                                 ibuf[0] = n1;
5145                                                 ibuf[1] = n2;
5146                                                 ibuf[2] = i;
5147                                                 ibuf[3] = n4;
5148                                                 ibuf[4] = -1;
5149                                                 retval = MoleculeAddImpropers(mp, ibuf, NULL);
5150                                                 if (retval < 0)
5151                                                         goto abort;
5152                                         }
5153                                 }
5154                         }
5155                 }
5156         }
5157
5158         mp->needsMDRebuild = 1;
5159         __MoleculeUnlock(mp);
5160         return 0;
5161
5162   abort:
5163         __MoleculeUnlock(mp);
5164         return retval;
5165 }
5166
5167 #pragma mark ====== Atom names ======
5168
5169 /*  Look for the n1-th atom in resno-th residue (n1 is 0-based)  */
5170 int
5171 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
5172 {
5173         int i, j, lasti;
5174         Atom *ap;
5175         if (mp == NULL || mp->natoms == 0)
5176                 return -1;
5177         lasti = -1;
5178         for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5179                 if (ap->resSeq == resno) {
5180                         lasti = i;
5181                         if (j++ == n1)
5182                                 return i;
5183                 }
5184         }
5185         if (n1 == -1)
5186                 return lasti; /* max */
5187         return -1;
5188 }
5189
5190 int
5191 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
5192 {
5193     int n;
5194     char *p;
5195         n = strtol(s, &p, 0);
5196         if (p > s) {
5197                 while (isspace(*p))
5198                         p++;
5199                 if (*p == 0) {
5200                   resName[0] = 0;
5201                   *resSeq = -1;
5202                   atomName[0] = 0;
5203                   return n;
5204                 }
5205         }
5206
5207         if ((p = strchr(s, ':')) != NULL) {
5208                 /*  Residue is specified  */
5209                 char *pp;
5210                 if ((pp = strchr(s, '.')) != NULL && pp < p) {
5211                         /*  Residue number is also specified  */
5212                         char *ppp;
5213                         n = pp - s;
5214                         *resSeq = strtol(pp + 1, &ppp, 0);
5215                         if (ppp == pp + 1)
5216                                 return -2;  /*  Bad format  */
5217                         while (isspace(*ppp))
5218                                 ppp++;
5219                         if (ppp != p)
5220                                 return -2;  /*  Bad format  */
5221                 } else {
5222                         *resSeq = -1;
5223                         /*  Check whether the "residue name" is an integer  */
5224                         n = strtol(s, &pp, 0);
5225                         if (pp > s) {
5226                                 while (isspace(*pp))
5227                                         pp++;
5228                                 if (*pp == 0 || *pp == ':') {
5229                                         *resSeq = n;
5230                                         if (*resSeq < 0)
5231                                                 return -2;  /*  Bad format  */
5232                                 }
5233                         }
5234                         if (*resSeq >= 0)
5235                                 n = 0;
5236                         else
5237                                 n = p - s;
5238                 }
5239                 if (n >= sizeof(resName))
5240                         n = sizeof(resName) - 1;
5241                 strncpy(resName, s, n);
5242                 resName[n] = 0;
5243                 p++;
5244         } else {
5245                 resName[0] = 0;
5246                 *resSeq = -1;
5247                 p = (char *)s;
5248         }
5249         strncpy(atomName, p, 4);
5250         atomName[4] = 0;
5251         return 0;
5252 }
5253
5254 /*  Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer  */
5255 int
5256 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
5257 {
5258         char resName[6];
5259         int resSeq, n;
5260         char atomName[6];
5261         /*      char *p; */
5262
5263         n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
5264         if (atomName[0] == 0) {
5265           if (n >= mp->natoms)
5266             n = -1;  /* Out of range */
5267           return n;
5268         }
5269         for (n = 0; n < mp->natoms; n++) {
5270                 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
5271                 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
5272                         && (resSeq < 0 || ap->resSeq == resSeq)
5273                         && strncmp(atomName, ap->aname, 4) == 0) {
5274                         return n;
5275                 }
5276         }
5277         return -1;  /*  Not found  */
5278 }
5279
5280 int
5281 MoleculeAreAtomsConnected(Molecule *mp, int n1, int n2)
5282 {
5283         Atom *ap;
5284         int i;
5285         if (mp == NULL || n1 < 0 || n1 >= mp->natoms || n2 < 0 || n2 >= mp->natoms)
5286                 return 0;
5287         ap = ATOM_AT_INDEX(mp->atoms, n1);
5288         for (i = 0; i < ap->nconnects; i++)
5289                 if (ap->connects[i] == n2)
5290                         return 1;
5291         return 0;
5292 }
5293
5294
5295 void
5296 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
5297 {
5298         Atom *ap;
5299         int n;
5300         if (mp == NULL || index < 0 || index >= mp->natoms) {
5301                 buf[0] = 0;
5302                 return;
5303         }
5304         ap = mp->atoms + index;
5305         if (ap->resSeq != 0) {
5306                 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
5307                 buf += n;
5308                 bufsize -= n;
5309         }
5310         snprintf(buf, bufsize, "%.4s", ap->aname);
5311 }
5312
5313 #pragma mark ====== Selection ======
5314
5315 static void
5316 sMoleculeNotifyChangeSelection(Molecule *mp)
5317 {
5318         /*  TODO: Finer control of notification types may be necessary  */
5319         MoleculeCallback_notifyModification(mp, 0);
5320 }
5321
5322 void
5323 MoleculeSetSelection(Molecule *mp, IntGroup *select)
5324 {
5325         if (mp == NULL)
5326                 return;
5327         if (select != NULL)
5328                 IntGroupRetain(select);
5329         if (mp->selection != NULL)
5330                 IntGroupRelease(mp->selection);
5331         mp->selection = select;
5332         sMoleculeNotifyChangeSelection(mp);
5333 }
5334
5335 IntGroup *
5336 MoleculeGetSelection(Molecule *mp)
5337 {
5338         if (mp == NULL)
5339                 return NULL;
5340         else return mp->selection;
5341 }
5342
5343 void
5344 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
5345 {
5346         if (mp->selection == NULL)
5347                 mp->selection = IntGroupNew();
5348         if (!extending)
5349                 IntGroupClear(mp->selection);
5350         IntGroupAdd(mp->selection, n1, 1);
5351         sMoleculeNotifyChangeSelection(mp);
5352 }
5353
5354 void
5355 MoleculeUnselectAtom(Molecule *mp, int n1)
5356 {
5357         if (mp->selection != NULL)
5358                 IntGroupRemove(mp->selection, n1, 1);
5359         sMoleculeNotifyChangeSelection(mp);
5360 }
5361
5362 void
5363 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
5364 {
5365         if (mp->selection == NULL)
5366                 mp->selection = IntGroupNew();
5367         IntGroupReverse(mp->selection, n1, 1);
5368         sMoleculeNotifyChangeSelection(mp);
5369 }
5370
5371 int
5372 MoleculeIsAtomSelected(Molecule *mp, int n1)
5373 {
5374         if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
5375                 return 1;
5376         else return 0;
5377 }
5378
5379 int
5380 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
5381 {
5382         if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
5383                 return 1;
5384         else return 0;
5385 }
5386
5387 IntGroup *
5388 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
5389 {
5390         int status;
5391         IntGroup *remain, *ig1, *ig2;
5392         ig1 = ig2 = NULL;
5393         remain = IntGroupNewFromIntGroup(remove);
5394         if (remain == NULL)
5395                 status = -1;
5396         else
5397                 status = IntGroupReverse(remain, 0, mp->natoms);
5398         if (status == 0) {
5399                 ig1 = IntGroupNew();
5400                 if (ig1 == NULL)
5401                         status = -1;
5402                 else
5403                         status = IntGroupDifference(selection, remove, ig1);
5404         }
5405         if (status == 0) {
5406                 ig2 = IntGroupNew();
5407                 if (ig2 == NULL)
5408                         status = -1;
5409                 else
5410                         status = IntGroupDeconvolute(ig1, remain, ig2);
5411         }
5412         if (remain != NULL)
5413                 IntGroupRelease(remain);
5414         if (ig1 != NULL)
5415                 IntGroupRelease(ig1);
5416         if (status == 0)
5417                 return ig2;
5418         else {
5419                 if (ig2 != NULL)
5420                         IntGroupRelease(ig2);
5421                 return NULL;
5422         }
5423 }
5424
5425 #pragma mark ====== Atom Equivalence ======
5426
5427 struct sEqList {
5428         int i[2];
5429         struct sEqList *next;
5430         struct sEqList *link;
5431 };
5432
5433 static struct sEqList *sListBase = NULL;
5434 static struct sEqList *sListFree = NULL;
5435
5436 static struct sEqList *
5437 sAllocEqList(void)
5438 {
5439         struct sEqList *lp;
5440         if (sListFree != NULL) {
5441                 lp = sListFree;
5442                 sListFree = lp->next;
5443                 lp->i[0] = lp->i[1] = 0;
5444                 lp->next = NULL;
5445                 return lp;
5446         }
5447         lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
5448         lp->link = sListBase;
5449         sListBase = lp;
5450         return lp;
5451 }
5452
5453 static void
5454 sFreeEqList(struct sEqList *list)
5455 {
5456         list->next = sListFree;
5457         sListFree = list;
5458 }
5459
5460 static void
5461 sDeallocateEqLists(void)
5462 {
5463         struct sEqList *lp, *lp_link;
5464         for (lp = sListBase; lp != NULL; lp = lp_link) {
5465                 lp_link = lp->link;
5466                 free(lp);
5467         }
5468         sListBase = NULL;
5469         sListFree = NULL;
5470 }
5471
5472 static int
5473 sExistInEqList(int i, int idx, struct sEqList *list)
5474 {
5475         while (list != NULL) {
5476                 if (list->i[idx] == i)
5477                         return 1;
5478                 list = list->next;
5479         }
5480         return 0;
5481 }
5482
5483 static struct sEqList *
5484 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
5485 {
5486         Atom *api, *apj;
5487         struct sEqList *list1, *list2;
5488         int ii, jj, ni, nj;
5489         api = ATOM_AT_INDEX(mol->atoms, i);
5490         apj = ATOM_AT_INDEX(mol->atoms, j);
5491         if (api->atomicNumber != apj->atomicNumber)
5492                 return NULL;
5493         list1 = sAllocEqList();
5494         if (list1 == NULL)
5495                 return NULL;
5496         list1->i[0] = i;
5497         list1->i[1] = j;
5498         list1->next = list;
5499         if (i == j || (db[i] != NULL && db[i] == db[j]))
5500                 return list1;
5501         for (ni = 0; ni < api->nconnects; ni++) {
5502                 ii = api->connects[ni];
5503                 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
5504                         continue;
5505                 if (sExistInEqList(ii, 0, list1))
5506                         continue;
5507                 list2 = NULL;
5508                 for (nj = 0; nj < apj->nconnects; nj++) {
5509                         jj = apj->connects[nj];
5510                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
5511                                 continue;
5512                         if (sExistInEqList(jj, 1, list1))
5513                                 continue;
5514                         list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
5515                         if (list2 != NULL)
5516                                 break;
5517                 }
5518                 if (list2 == NULL) {
5519                         sFreeEqList(list1);
5520                         return NULL;    /*  No equivalent to ii  */
5521                 }
5522                 list1 = list2;      /*  ii is OK, try next  */
5523         }
5524         return list1;
5525 }
5526
5527 int
5528 sDBInclude(Int *ip, int i)
5529 {
5530         int j;
5531         if (ip == NULL)
5532                 return -1;
5533         for (j = ip[0] - 1; j >= 0; j--) {
5534                 if (ip[j] == i)
5535                         return j;
5536         }
5537         return -1;
5538 }
5539
5540 Int *
5541 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
5542 {
5543         Int **db;  /*  List of equivalents for each atom  */
5544         Int *ip, *result;
5545         Atom *api, *apj, *apk;
5546         int i, j, k, ii, jj, kk;
5547         if (mol == NULL || mol->natoms == 0)
5548                 return NULL;
5549         db = (Int **)calloc(sizeof(Int *), mol->natoms);
5550
5551         /*  Find the equivalent univalent atoms  */
5552         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
5553                 if (api->nconnects < 2)
5554                         continue;
5555                 for (j = 0; j < api->nconnects; j++) {
5556                         Int ibuf[ATOMS_MAX_CONNECTS], n;
5557                         memset(ibuf, 0, sizeof(ibuf));
5558                         n = 0;
5559                         jj = api->connects[j];
5560                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
5561                                 continue;
5562                         ibuf[n++] = jj;
5563                         apj = ATOM_AT_INDEX(mol->atoms, jj);
5564                         if (apj->nconnects != 1 || db[jj] != NULL)
5565                                 continue;
5566                         for (k = j + 1; k < api->nconnects; k++) {
5567                                 kk = api->connects[k];
5568                                 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
5569                                         continue;
5570                                 apk = ATOM_AT_INDEX(mol->atoms, kk);
5571                                 if (apk->nconnects != 1 || db[kk] != NULL)
5572                                         continue;
5573                                 if (apj->atomicNumber == apk->atomicNumber)
5574                                         ibuf[n++] = kk;
5575                         }
5576                         if (n > 1) {
5577                                 ip = (Int *)calloc(sizeof(Int), n + 1);
5578                                 if (ip == NULL)
5579                                         return NULL;
5580                                 ip[0] = n;
5581                                 memmove(ip + 1, ibuf, sizeof(Int) * n);
5582                                 for (k = 0; k < n; k++)
5583                                         db[ip[k + 1]] = ip;
5584                         }
5585                 }
5586         }
5587         
5588         /*  Try matching (i,j) pair  */
5589         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
5590                 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
5591                         continue;
5592                 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
5593                         struct sEqList *list;
5594                         if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
5595                                 continue;
5596                         if (api->atomicNumber != apj->atomicNumber)
5597                                 continue;  /*  Different elements do not match  */
5598                         if (db[i] != NULL && db[i] == db[j])
5599                                 continue;  /*  Already equivalent  */
5600                         list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
5601                         if (list == NULL)
5602                                 continue;  /*  (i,j) do not match  */
5603                         while (list != NULL) {
5604                                 ii = list->i[0];
5605                                 jj = list->i[1];
5606                                 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
5607                                         /*  Merge db[ii] and db[jj]  */
5608                                         k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
5609                                         ip = (Int *)calloc(sizeof(Int), k + 1);
5610                                         if (ip == NULL)
5611                                                 return NULL;  /*  Out of memory  */
5612                                         if (db[ii] == NULL) {
5613                                                 ip[1] = ii;
5614                                                 k = 2;
5615                                         } else {
5616                                                 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
5617                                                 k = db[ii][0] + 1;
5618                                         }
5619                                         if (db[jj] == NULL) {
5620                                                 ip[k++] = jj;
5621                                         } else {
5622                                                 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
5623                                                 k += db[jj][0];
5624                                         }
5625                                         ip[0] = k - 1;
5626                                         /*  Free old ones  */
5627                                         if (db[ii] != NULL)
5628                                                 free(db[ii]);
5629                                         if (db[jj] != NULL)
5630                                                 free(db[jj]);
5631                                         for (k = 0; k < ip[0]; k++)
5632                                                 db[ip[k + 1]] = ip;
5633                                         if (0) {
5634                                                 /*  For debug  */
5635                                                 printf("(%d,%d) matched: ", ii, jj);
5636                                                 for (k = 0; k < ip[0]; k++) {
5637                                                         printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
5638                                                 }
5639                                                 printf("]\n");
5640                                         }
5641                                 }
5642                                 list = list->next;
5643                         }
5644                 }
5645         }
5646         
5647         /*  Record the equivalent atoms with the lowest index for each atom  */
5648         result = (Int *)calloc(sizeof(Int), mol->natoms);
5649         for (i = 0; i < mol->natoms; i++)
5650                 result[i] = -1;
5651         for (i = 0; i < mol->natoms; i++) {
5652                 if (result[i] >= 0 || (ip = db[i]) == NULL)
5653                         continue;
5654                 k = mol->natoms;
5655                 for (j = 0; j < ip[0]; j++) {
5656                         kk = ip[j + 1];
5657                         if (kk < k)
5658                                 k = kk;
5659                 }
5660                 for (j = 0; j < ip[0]; j++) {
5661                         result[ip[j + 1]] = k;
5662                         db[ip[j + 1]] = NULL;
5663                 }
5664                 free(ip);
5665         }
5666         sDeallocateEqLists();
5667         return result;
5668 }
5669
5670 #pragma mark ====== Symmetry expansion ======
5671
5672 int
5673 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
5674 {
5675         if (mp == NULL)
5676                 return 1;
5677         if (symop.sym >= mp->nsyms)
5678                 return 2;
5679         if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
5680                 TransformVec(vpout, mp->cell->rtr, vpin);
5681                 TransformVec(vpout, mp->syms[symop.sym], vpout);
5682                 vpout->x += symop.dx;
5683                 vpout->y += symop.dy;
5684                 vpout->z += symop.dz;
5685                 TransformVec(vpout, mp->cell->tr, vpout);
5686         } else {
5687                 TransformVec(vpout, mp->syms[symop.sym], vpin);
5688                 vpout->x += symop.dx;
5689                 vpout->y += symop.dy;
5690                 vpout->z += symop.dz;
5691         }
5692         return 0;
5693 }
5694
5695 /*  TODO: Decide whether the symmetry operations are expressed in internal coordinates or cartesian coordinates. */
5696 int
5697 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group)
5698 {
5699         int i, n, n0, n1, count, *table;
5700         Atom *ap;
5701         IntGroupIterator iter;
5702 /*      int debug = 0; */
5703         if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
5704                 return -1;
5705         if (symop.sym >= mp->nsyms)
5706                 return -2;
5707 /*      fprintf(stderr, "symop = {%d %d %d %d}\n", symop.sym, symop.dx, symop.dy, symop.dz); */
5708
5709         /*  Create atoms, with avoiding duplicates  */
5710         n0 = n1 = mp->natoms;
5711         table = (int *)malloc(sizeof(int) * n0);
5712         if (table == NULL)
5713                 return -3;
5714         for (i = 0; i < n0; i++)
5715                 table[i] = -1;
5716         IntGroupIteratorInit(group, &iter);
5717         __MoleculeLock(mp);
5718         for (i = 0; i < count; i++) {
5719                 int n2;
5720                 Atom *ap2;
5721                 Vector nr, dr;
5722                 n = IntGroupIteratorNext(&iter);
5723                 ap = ATOM_AT_INDEX(mp->atoms, n);
5724                 if (SYMOP_ALIVE(ap->symop)) {
5725                         /*  Skip if the atom is expanded  */
5726                         continue;
5727                 }
5728                 /*  Is this expansion already present?  */
5729                 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
5730                         if (ap2->symbase == n && SYMOP_EQUAL(symop, ap2->symop))
5731                                 break;
5732                 }
5733                 if (n2 < n0) {
5734                         /*  If yes, then skip it  */
5735                         continue;
5736                 }
5737                 /*  Is the expanded position coincides with itself?  */
5738                 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
5739                 VecSub(dr, ap->r, nr);
5740                 if (VecLength2(dr) < 1e-6) {
5741                         /*  If yes, then this atom is included but no new atom is created  */
5742                         table[n] = n;
5743                 } else {
5744                         /*  Create a new atom  */
5745                         Atom newAtom;
5746                         AtomDuplicate(&newAtom, ap);
5747                         MoleculeCreateAnAtom(mp, &newAtom, -1);
5748                         AtomClean(&newAtom);
5749                         ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
5750                         ap2->r = nr;
5751                         ap2->symbase = n;
5752                         ap2->symop = symop;
5753                         ap2->symop.alive = (symop.dx != 0 || symop.dy != 0 || symop.dz != 0 || symop.sym != 0);
5754                         table[n] = n1;  /*  The index of the new atom  */
5755                         n1++;
5756                 }
5757         }
5758         IntGroupIteratorRelease(&iter);
5759
5760         /*  Create bonds  */
5761         for (i = 0; i < n0; i++) {
5762                 int b[2];
5763                 b[0] = table[i];
5764                 if (b[0] < 0 || b[0] == i)
5765                         continue;
5766                 ap = ATOM_AT_INDEX(mp->atoms, i);
5767                 for (n = 0; n < ap->nconnects; n++) {
5768                         b[1] = table[ap->connects[n]];
5769                         if (b[1] < 0)
5770                                 continue;
5771                         if (b[1] > n0 && b[0] > b[1])
5772                                 continue;
5773                         MoleculeAddBonds(mp, 1, b);
5774                 }
5775         }
5776         mp->needsMDRebuild = 1;
5777         __MoleculeUnlock(mp);
5778         free(table);
5779         return n1 - n0;  /*  The number of added atoms  */
5780 }
5781
5782 /*  Recalculate the coordinates of symmetry expanded atoms.
5783         Returns the number of affected atoms.
5784     If group is non-NULL, only the expanded atoms whose base atoms are in the
5785     given group are considered.
5786         If groupout and vpout are non-NULL, the indices of the affected atoms
5787         and the original positions are returned (for undo operation).
5788         The pointers returned in *groupout and *vpout must be released and 
5789         free()'ed by the caller  */
5790 int
5791 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
5792 {
5793         int i, count;
5794         Atom *ap, *bp;
5795         if (mp == NULL || mp->natoms == 0 || mp->nsyms == 0)
5796                 return 0;
5797         if (groupout != NULL && vpout != NULL) {
5798                 *groupout = IntGroupNew();
5799                 if (*groupout == NULL)
5800                         return -1;
5801                 *vpout = (Vector *)malloc(sizeof(Vector) * mp->natoms);
5802                 if (*vpout == NULL) {
5803                         IntGroupRelease(*groupout);
5804                         return -1;
5805                 }
5806         } else groupout = NULL; /* To simplify test for validity of groupout/vpout */
5807         
5808         __MoleculeLock(mp);
5809         count = 0;
5810         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5811                 Vector nr, dr;
5812                 if (!SYMOP_ALIVE(ap->symop))
5813                         continue;
5814                 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
5815                         continue;
5816                 bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
5817                 MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
5818                 VecSub(dr, nr, ap->r);
5819                 if (VecLength2(dr) < 1e-20)
5820                         continue;
5821                 if (groupout != NULL) {
5822                         (*vpout)[count] = ap->r;
5823                         IntGroupAdd(*groupout, i, 1);
5824                 }
5825                 ap->r = nr;
5826                 count++;
5827         }
5828         mp->needsMDCopyCoordinates = 1;
5829         __MoleculeUnlock(mp);
5830         if (groupout != NULL) {
5831                 if (count == 0) {
5832                         free(*vpout);
5833                         *vpout = NULL;
5834                         IntGroupRelease(*groupout);
5835                         *groupout = NULL;
5836                 } else {
5837                         *vpout = (Vector *)realloc(*vpout, sizeof(Vector) * count);
5838                 }
5839         }
5840         return count;
5841 }
5842
5843 #pragma mark ====== Show/hide atoms ======
5844
5845 static void
5846 sMoleculeNotifyChangeAppearance(Molecule *mp)
5847 {
5848         /*  TODO: Finer control of notification types may be necessary  */
5849         MoleculeCallback_notifyModification(mp, 0);
5850 }
5851
5852
5853 static void
5854 sMoleculeUnselectHiddenAtoms(Molecule *mp)
5855 {
5856         int i;
5857         if (mp == NULL || mp->selection == NULL)
5858                 return;
5859         for (i = 0; i < mp->natoms; i++) {
5860                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
5861                 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
5862                         IntGroupRemove(mp->selection, i, 1);
5863         }
5864         sMoleculeNotifyChangeAppearance(mp);
5865 }
5866
5867 int
5868 MoleculeShowAllAtoms(Molecule *mp)
5869 {
5870         int i;
5871         if (mp == NULL)
5872                 return 0;
5873         for (i = 0; i < mp->natoms; i++) {
5874                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
5875                 ap->exflags &= ~kAtomHiddenFlag;
5876         }
5877         sMoleculeNotifyChangeAppearance(mp);
5878         return 1;
5879 }
5880
5881 int
5882 MoleculeShowReverse(Molecule *mp)
5883 {
5884         int i;
5885         if (mp == NULL)
5886                 return 0;
5887         for (i = 0; i < mp->natoms; i++) {
5888                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
5889                 ap->exflags ^= kAtomHiddenFlag;
5890         }
5891         sMoleculeUnselectHiddenAtoms(mp);
5892         sMoleculeNotifyChangeAppearance(mp);
5893         return 1;
5894 }
5895
5896 int
5897 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
5898 {
5899         int i;
5900         if (mp == NULL || ig == NULL)
5901                 return 0;
5902         for (i = 0; i < mp->natoms; i++) {
5903                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
5904                 if (ap->exflags & kAtomHiddenFlag)
5905                         continue;  /*  Already hidden  */
5906                 if (IntGroupLookupPoint(ig, i) >= 0)
5907                         ap->exflags |= kAtomHiddenFlag;
5908         }
5909         sMoleculeUnselectHiddenAtoms(mp);
5910         sMoleculeNotifyChangeAppearance(mp);
5911         return 1;
5912 }
5913
5914 #pragma mark ====== Reversible Editing ======
5915
5916 /*
5917 static void
5918 sMoleculeNotifyModification(Molecule *mp)
5919 {
5920         **  TODO: Finer control of notification types may be necessary  **
5921         MoleculeCallback_notifyModification(mp, 0);
5922 }
5923 */
5924
5925 /*  Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup  */
5926 int
5927 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
5928 {
5929         int n1, n2, n3, i;
5930         if (where == NULL) {
5931                 /*  Append the new objects at the end  */
5932                 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
5933                 return 0;
5934         }
5935         n1 = IntGroupGetCount(where);  /*  Position to get new object  */
5936         n2 = nobjs;                    /*  Position to get old object  */
5937         n3 = n1 + n2;                  /*  Position to place new/old object  */
5938         for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
5939                 int start = IntGroupGetStartPoint(where, i);
5940                 int end = IntGroupGetEndPoint(where, i);
5941                 if (end < n3) {
5942                         /*  old[end-(n3-n2)..n2-1] is moved to old[end..n3-1]  */
5943                         memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
5944                         n2 = end - (n3 - n2);
5945                         n3 = end;
5946                 }
5947                 /*  new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1]  */
5948                 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
5949                 n3 -= end - start;
5950                 n1 -= end - start;
5951         }
5952         return 0;
5953 }
5954
5955 /*  Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
5956 int
5957 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
5958 {
5959         int n1, n2, n3, start, end, i;
5960         if (objs == NULL || where == NULL)
5961                 return 1;  /*  Bad argument  */
5962         n1 = 0;  /*  Position to move remaining elements to */
5963         n2 = 0;  /*  Position to move remaining elements from  */
5964         n3 = 0;  /*  Position to move removed elements to  */
5965         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
5966                 end = IntGroupGetEndPoint(where, i);
5967                 if (n2 < start) {
5968                         /*  Move (start - n2) elements from objs[n2] to objs[n1]  */
5969                         if (n1 < n2)
5970                                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
5971                         n1 += start - n2;
5972                         n2 = start;
5973                 }
5974                 /*  Move (end - start) elements from objs[n2] to clip[n3]  */
5975                 if (clip != NULL)
5976                         memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
5977                 n3 += (end - start);
5978                 n2 += (end - start);
5979         }
5980         /*  Move (nobjs - n2) elements from objs[n2] to objs[n1]  */
5981         if (nobjs > n2)
5982                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
5983         return 0;
5984 }
5985
5986 /*  Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
5987 int
5988 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
5989 {
5990         int n1, start, end, i;
5991         if (objs == NULL || where == NULL)
5992                 return 1;  /*  Bad argument  */
5993         n1 = 0;  /*  Position to move removed elements to  */
5994         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
5995                 end = IntGroupGetEndPoint(where, i);
5996                 /*  Copy (end - start) elements from objs[start] to clip[n1]  */
5997                 if (clip != NULL)
5998                         memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
5999                 n1 += (end - start);
6000         }
6001         return 0;
6002 }
6003
6004 /*  Create a new atom with no bonding information. ap must _not_ be inside the given molecule
6005    (Use AtomDuplicate() first) */
6006 int
6007 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
6008 {
6009     Atom *ap1, *api;
6010         int i;
6011         if (mp == NULL || ap == NULL || mp->noModifyTopology)
6012                 return -1;
6013         __MoleculeLock(mp);
6014         if (pos < 0 || pos >= mp->natoms)
6015                 pos = mp->natoms;
6016         ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
6017         if (ap1 == NULL)
6018                 goto error;  /*  Out of memory  */
6019         ap1 = ATOM_AT_INDEX(mp->atoms, pos);
6020         if (pos < mp->natoms - 1) {
6021                 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6022         }
6023         if (AtomDuplicate(ap1, ap) == NULL) {
6024                 /*  Cannot duplicate: restore the original state  */
6025                 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6026                 mp->natoms--;
6027                 goto error;
6028         }
6029         ap1->nconnects = 0;
6030         if (ap1->resSeq >= mp->nresidues)
6031                 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
6032         if (ap1->resName[0] == 0)
6033           strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
6034         if (ap1->segName[0] == 0)
6035           strncpy(ap1->segName, "MAIN", 4);
6036         if (pos < mp->natoms - 1) {
6037                 /*  Renumber the connect table, bonds, angles, etc. */
6038                 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
6039                         int j;
6040                         for (j = 0; j < api->nconnects; j++) {
6041                                 if (api->connects[j] >= pos)
6042                                         api->connects[j]++;
6043                         }
6044                 }
6045                 for (i = 0; i < mp->nbonds * 2; i++) {
6046                         if (mp->bonds[i] >= pos)
6047                                 mp->bonds[i]++;
6048                 }
6049                 for (i = 0; i < mp->nangles * 3; i++) {
6050                         if (mp->angles[i] >= pos)
6051                                 mp->angles[i]++;
6052                 }
6053                 for (i = 0; i < mp->ndihedrals * 4; i++) {
6054                         if (mp->dihedrals[i] >= pos)
6055                                 mp->dihedrals[i]++;
6056                 }
6057                 for (i = 0; i < mp->nimpropers * 4; i++) {
6058                         if (mp->impropers[i] >= pos)
6059                                 mp->impropers[i]++;
6060                 }
6061         }
6062         mp->nframes = -1;  /*  Should be recalculated later  */
6063         MoleculeIncrementModifyCount(mp);
6064         mp->needsMDRebuild = 1;
6065         __MoleculeUnlock(mp);
6066         return pos;
6067 error:
6068         __MoleculeUnlock(mp);
6069         return -1;
6070 }
6071
6072 /*  Merge two molecules. We use this procedure for all add-atom operations.  */
6073 /*  resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
6074 int
6075 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, int resSeqOffset)
6076 {
6077         int nsrc, ndst;
6078         int i, j, n1, n2, n3, n4;
6079         Int *new2old, *old2new;
6080         Atom *ap;
6081         if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
6082                 return 0;  /*  Do nothing  */
6083
6084         if (dst->noModifyTopology)
6085                 return 1;  /*  Prohibited operation  */
6086
6087         if (where != NULL && IntGroupGetCount(where) != src->natoms)
6088                 return 1;  /*  Bad parameter  */
6089
6090         __MoleculeLock(dst);
6091         nsrc = src->natoms;
6092         ndst = dst->natoms;
6093         if (resSeqOffset < 0)
6094                 resSeqOffset = 0;
6095
6096         /*  Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
6097             and ndst..ndst+nsrc-1 are for atoms in src.  */ 
6098         new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
6099         if (new2old == NULL)
6100                 goto panic;
6101         old2new = new2old + ndst + nsrc;
6102         n1 = 0;  /*  dst index  */
6103         n2 = 0;  /*  src index  */
6104         n3 = 0;  /*  "merged" index  */
6105         i = 0;
6106         while (n1 < ndst || n2 < nsrc) {
6107                 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
6108                         n4 = ndst - n1;
6109                 else n4 -= n3;
6110                 /*  n4 elements from dst[n1] will go to merged[n3]  */
6111                 for (j = 0; j < n4; j++) {
6112                         old2new[n1 + j] = n3 + j;
6113                         new2old[n3 + j] = n1 + j;
6114                 }
6115                 n3 += n4;
6116                 n1 += n4;
6117                 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
6118                         n4 = nsrc - n2;
6119                 /*  n4 elements from src[n2] will go to merged[n3]  */
6120                 for (j = 0; j < n4; j++) {
6121                         old2new[ndst + n2 + j] = n3 + j;
6122                         new2old[n3 + j] = ndst + n2 + j;
6123                 }
6124                 n3 += n4;
6125                 n2 += n4;
6126                 i++;
6127         }
6128
6129         /*  Expand the destination array  */
6130         if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
6131                 goto panic;
6132
6133         /*  Move the atoms  */
6134         if (where == NULL) {
6135                 /*  Duplicate atoms to the end of the destination array  */
6136                 for (i = 0; i < nsrc; i++) {
6137                         if (AtomDuplicate(ATOM_AT_INDEX(dst->atoms, ndst + i), ATOM_AT_INDEX(src->atoms, i)) == NULL)
6138                                 goto panic;
6139                 }
6140         //      memmove(ATOM_AT_INDEX(dst->atoms, ndst), src->atoms, gSizeOfAtomRecord * nsrc);
6141         } else {
6142                 /*  Duplicate to a temporary storage and then insert  */
6143                 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
6144                 if (tempatoms == NULL)
6145                         goto panic;
6146                 for (i = 0; i < nsrc; i++) {
6147                         if (AtomDuplicate(ATOM_AT_INDEX(tempatoms, i), ATOM_AT_INDEX(src->atoms, i)) == NULL)
6148                                 goto panic;
6149                 }
6150                 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
6151                         goto panic;
6152                 free(tempatoms);
6153         }
6154         dst->natoms = ndst + nsrc;
6155
6156         /*  Renumber the atom indices in connect[] and symbase, and modify the residue numbers  */
6157         for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
6158                 if (new2old[i] < ndst) {
6159                         /*  This atom is from dst  */
6160                         n1 = 0;
6161                 } else {
6162                         /*  This atom is from src  */
6163                         n1 = ndst;  /*  Offset to the internal number  */
6164                         if (ap->resSeq != 0)
6165                                 ap->resSeq += resSeqOffset;  /*  Modify residue number  */
6166                 }
6167                 for (j = 0; j < ap->nconnects; j++)
6168                         ap->connects[j] = old2new[ap->connects[j] + n1];
6169                 if (SYMOP_ALIVE(ap->symop))
6170                         ap->symbase = old2new[ap->symbase + n1];
6171         }
6172         
6173         /*  Move the bonds, angles, dihedrals, impropers  */
6174         for (i = 0; i < 4; i++) {
6175                 Int *nitems, *nitems_src;
6176                 Int **items, **items_src;
6177                 Int nsize;  /*  Number of Ints in one element  */
6178                 switch (i) {
6179                         case 0:
6180                                 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
6181                         case 1:
6182                                 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
6183                         case 2:
6184                                 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
6185                         case 3:
6186                                 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
6187                 }
6188                 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
6189                 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
6190                 /*  Keep the old number of entries in dst, because it is updated by AssignArray()  */
6191                 n1 = *nitems;
6192                 /*  Also keep the old number of entries in src, in case src and dst point the same molecule  */
6193                 n2 = *nitems_src;
6194                 /*  Expand the array  */
6195                 if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
6196                         goto panic;
6197                 /*  Copy the items  */
6198                 memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
6199                 /*  Renumber  */
6200                 for (j = 0; j < n1 * nsize; j++)
6201                         (*items)[j] = old2new[(*items)[j]];
6202                 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
6203                         (*items)[j] = old2new[(*items)[j] + ndst];
6204         }
6205         
6206         /*  Merge parameters  */
6207         if (src->par != NULL) {
6208                 UnionPar *up1, *up2;
6209                 int type;
6210                 IntGroup *ig;
6211                 ig = IntGroupNew();
6212                 if (dst->par == NULL)
6213                         dst->par = ParameterNew();
6214                 for (type = kFirstParType; type <= kLastParType; type++) {
6215                         n1 = ParameterGetCountForType(src->par, type);
6216                         n2 = ParameterGetCountForType(dst->par, type);
6217                         if (n1 == 0)
6218                                 continue;
6219                         for (i = 0; i < n1; i++) {
6220                                 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
6221                                 for (j = 0; j < n2; j++) {
6222                                         up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
6223                                         if (ParameterCompare(up1, up2, type))
6224                                                 break;
6225                                 }
6226                                 if (j >= n2)
6227                                         /*  This is an unknown parameter; should be copied  */
6228                                         IntGroupAdd(ig, i, 1);
6229                         }
6230                         n1 = IntGroupGetCount(ig);
6231                         if (n1 == 0)
6232                                 continue;
6233                         up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
6234                         if (up1 == NULL)
6235                                 goto panic;
6236                         /*  Copy parameters and renumber indices if necessary  */
6237                         for (i = 0; i < n1; i++) {
6238                                 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
6239                                 if (up2 == NULL)
6240                                         continue;
6241                                 up1[i] = *up2;
6242                                 ParameterRenumberAtoms(type, up1 + i, nsrc, old2new + ndst);
6243                         }
6244                         /*  Merge parameters  */
6245                         IntGroupClear(ig);
6246                         IntGroupAdd(ig, n2, n1);
6247                         if (ParameterInsert(dst->par, type, up1, ig) < n1)
6248                                 goto panic;
6249                         IntGroupClear(ig);
6250                         free(up1);
6251                 }
6252         }
6253         
6254         /*  Copy the residues if necessary  */
6255         /*  src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
6256             However, 1+resSeqOffset should not overwrite the existing residue in dst;
6257                 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1].  */
6258         n1 = dst->nresidues;
6259         if (1 + resSeqOffset < n1) {
6260                 n2 = n1;
6261         } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
6262         if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
6263                 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
6264                         goto panic;
6265                 memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
6266         }
6267
6268         MoleculeCleanUpResidueTable(dst);
6269         
6270         free(new2old);
6271         dst->nframes = -1;  /*  Should be recalculated later  */
6272
6273         MoleculeIncrementModifyCount(dst);
6274         dst->needsMDRebuild = 1;
6275         __MoleculeUnlock(dst);
6276         return 0;
6277
6278   panic:
6279         __MoleculeUnlock(dst);
6280     Panic("Low memory while adding atoms");
6281         return 1;  /*  Not reached  */
6282 }
6283
6284 static int
6285 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag)
6286 {
6287         int nsrc, ndst, nsrcnew;
6288         int i, j, n1, n2, n3, n4;
6289         Int *new2old, *old2new;
6290         IntGroup *move_g, *del_g;
6291         Molecule *dst;
6292         Atom *ap, *dst_ap;
6293         
6294         if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
6295                 /*  Do nothing  */
6296                 if (dstp != NULL)
6297                         *dstp = NULL;
6298                 return 0;
6299         }
6300         
6301         if (src->noModifyTopology && moveFlag)
6302                 return 1;  /*  Prohibit editing  */
6303
6304         if ((ndst = IntGroupGetCount(where)) > src->natoms)
6305                 return 1;  /*  Bad parameter  */
6306
6307         __MoleculeLock(src);
6308         
6309         nsrc = src->natoms;
6310         nsrcnew = nsrc - ndst;
6311         if (resSeqOffset < 0)
6312                 resSeqOffset = 0;
6313
6314         /*  Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
6315             and nsrcnew..nsrc-1 are for atoms moved into dst.  */ 
6316         new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
6317         if (new2old == NULL)
6318                 goto panic;
6319         old2new = new2old + nsrc;
6320         n1 = 0;  /*  src index  */
6321         n2 = 0;  /*  dst index  */
6322         n3 = 0;  /*  src index after "unmerge"  */
6323         i = 0;
6324         while (n1 < nsrc || n2 < ndst) {
6325                 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
6326                         n4 = nsrc - n1;
6327                 else n4 -= n1;
6328                 /*  n4 elements from src[n1] will go to unmerged[n3]  */
6329                 for (j = 0; j < n4; j++) {
6330                         old2new[n1 + j] = n3 + j;
6331                         new2old[n3 + j] = n1 + j;
6332                 }
6333                 n3 += n4;
6334                 n1 += n4;
6335                 if ((n4 = IntGroupGetInterval(where, i)) < 0)
6336                         n4 = nsrc - n1;
6337                 /*  n4 elements from src[n1] will go to dst[n2]  */
6338                 for (j = 0; j < n4; j++) {
6339                         old2new[n1 + j] = nsrcnew + n2 + j;
6340                         new2old[nsrcnew + n2 + j] = n1 + j;
6341                 }
6342                 n1 += n4;
6343                 n2 += n4;
6344                 i++;
6345         }
6346
6347         /*  Make sure that no bond between the two fragments exists  */
6348 /*      for (i = 0; i < src->nbonds; i++) {
6349                 n1 = old2new[src->bonds[i * 2]];
6350                 n2 = old2new[src->bonds[i * 2 + 1]];
6351                 if ((n1 < nsrcnew && n2 >= nsrcnew) || (n1 >= nsrcnew && n2 < nsrcnew)) {
6352                         free(new2old);
6353                         return 2;
6354                 }
6355         } */
6356         
6357         /*  Make a new molecule  */
6358         if (dstp != NULL) {
6359                 dst = MoleculeNew();
6360                 if (dst == NULL)
6361                         goto panic;
6362                 /*  Expand the destination array  */
6363                 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
6364                         goto panic;
6365                 dst_ap = dst->atoms;
6366         } else {
6367                 dst = NULL;
6368                 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
6369                 if (dst_ap == NULL)
6370                         goto panic;
6371         }
6372         
6373         /*  Move the atoms  */
6374         if (moveFlag) {
6375                 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
6376                         goto panic;
6377                 src->natoms = nsrcnew;
6378                 if (dst == NULL) {
6379                         /*  The atom record must be deallocated correctly  */
6380                         for (i = 0; i < ndst; i++)
6381                                 AtomClean(ATOM_AT_INDEX(dst_ap, i));
6382                 }
6383         } else {
6384                 if (dst != NULL) {
6385                         for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
6386                                 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
6387                 }
6388 #if 0
6389                 if (sCopyElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
6390                         goto panic;
6391                 if (dst != NULL) {
6392                         /*  The atom record must be deep-copied correctly  */
6393                         for (i = 0; i < ndst; i++) {
6394                                 if (AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, i)) == NULL)
6395                                         goto panic;
6396                         }
6397                 }
6398 #endif
6399         }
6400         
6401         if (dst == NULL) {
6402                 /*  The dummy destination array is no longer needed  */
6403                 free(dst_ap);
6404                 dst_ap = NULL;
6405         }
6406         
6407         /*  Renumber the atom indices in connect[]  */
6408         if (moveFlag) {
6409                 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
6410                         for (j = n1 = 0; j < ap->nconnects; j++) {
6411                                 n2 = old2new[ap->connects[j]];
6412                                 if (n2 < nsrcnew)
6413                                         ap->connects[n1++] = n2;
6414                         }
6415                         ap->nconnects = n1;
6416                 }
6417         }
6418         
6419         /*  Renumber the atom indices in connect[] and the residue indices  */
6420         if (dst != NULL) {
6421                 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
6422                         if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
6423                                 ap->resSeq -= resSeqOffset;
6424                         else ap->resSeq = 0;
6425                         for (j = n1 = 0; j < ap->nconnects; j++) {
6426                                 n2 = old2new[ap->connects[j]] - nsrcnew;
6427                                 if (n2 >= 0)
6428                                         ap->connects[n1++] = n2;
6429                         }
6430                         ap->nconnects = n1;
6431                 }
6432         }
6433
6434         /*  Separate the bonds, angles, dihedrals, impropers  */
6435         /*  TODO: Improper torsions should also be copied!  */
6436         move_g = IntGroupNew();
6437         del_g = IntGroupNew();
6438         if (move_g == NULL || del_g == NULL)
6439                 goto panic;
6440         for (i = 0; i < 4; i++) {
6441                 Int *nitems, *nitems_dst;
6442                 Int **items, **items_dst;
6443                 Int nsize;  /*  Number of Ints in one element  */
6444                 unsigned char *counts;
6445                 switch (i) {
6446                         case 0:
6447                                 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
6448                         case 1:
6449                                 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
6450                         case 2:
6451                                 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
6452                         case 3:
6453                                 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
6454                         default:
6455                                 nitems = NULL; items = NULL; nsize = 0; break;  /*  Not reached  */
6456                 }
6457                 if (dst != NULL) {
6458                         nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
6459                         items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
6460                 } else {
6461                         nitems_dst = NULL;
6462                         items_dst = NULL;
6463                 }
6464                 counts = (unsigned char *)calloc(1, *nitems);
6465                 /*  Find the entries that should be moved to dst  */
6466                 n2 = 0;
6467                 for (j = 0; j < *nitems * nsize; j++) {
6468                         n1 = old2new[(*items)[j]];
6469                         if (n1 >= nsrcnew)
6470                                 counts[j / nsize]++; /* Count the atom belonging to dst */ 
6471                 /*      if (n1 >= nsrcnew) {
6472                                 n1 -= nsrcnew;
6473                                 if (j % nsize == 0) {
6474                                         if (IntGroupAdd(sep, j / nsize, 1) != 0)
6475                                                 goto panic;
6476                                         n2++;
6477                                 }
6478                         }
6479                         (*items)[j] = n1; */
6480                 }
6481                 for (j = n2 = n3 = 0; j < *nitems; j++) {
6482                         if (counts[j] > 0) {
6483                                 /*  Remove from src  */
6484                                 n2++;
6485                                 if (IntGroupAdd(del_g, j, 1) != 0)
6486                                         goto panic;
6487                                 if (counts[j] == nsize) {
6488                                         /*  Move to dst  */
6489                                         n3++;
6490                                         if (IntGroupAdd(move_g, j, 1) != 0)
6491                                                 goto panic;
6492                                 }
6493                         }
6494                 }
6495                 if (n2 > 0) {
6496                         /*  Expand the destination array  */
6497                         if (items_dst != NULL && n3 > 0) {
6498                                 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
6499                                         goto panic;
6500                                 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
6501                                         goto panic;
6502                         }
6503                         /*  Remove from src  */
6504                         if (moveFlag) {
6505                                 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
6506                                         goto panic;
6507                                 (*nitems) -= n2;
6508                         }
6509                 }
6510                 /*  Renumber the entries  */
6511                 if (moveFlag) {
6512                         for (j = 0; j < *nitems * nsize; j++) {
6513                                 (*items)[j] = old2new[(*items)[j]];
6514                         }
6515                 }
6516                 if (items_dst != NULL) {
6517                         for (j = 0; j < *nitems_dst * nsize; j++) {
6518                                 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
6519                         }
6520                 }
6521                 free(counts);
6522                 IntGroupClear(move_g);
6523                 IntGroupClear(del_g);
6524         }
6525         IntGroupRelease(move_g);
6526         IntGroupRelease(del_g);
6527         
6528         /*  Copy the residues  */
6529         if (dst != NULL) {
6530                 /*  src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset)  */
6531                 n1 = src->nresidues - resSeqOffset;  /*  This will be dst->nresidues (if >0)  */
6532                 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
6533                         goto panic;
6534                 if (n1 > 1) {
6535                         memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
6536                 }
6537         }
6538
6539         /*  Copy the parameters  */
6540         if (dst != NULL && src->par != NULL) {
6541                 UnionPar *up, *upary;
6542                 if (dst->par == NULL)
6543                         dst->par = ParameterNew();
6544                 for (i = 0; i < nsrc; i++) {
6545                         old2new[i] -= nsrcnew;  /*  new indices for atoms in dst; otherwise negative numbers  */
6546                 }
6547                 move_g = IntGroupNew();
6548                 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
6549                         n2 = ParameterGetCountForType(src->par, n1);
6550                         if (n2 == 0)
6551                                 continue;
6552                         /*  Find parameters to be copied to dst  */
6553                         for (i = 0; i < n2; i++) {
6554                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
6555                                 for (j = 0, ap = dst->atoms; j < dst->natoms; j++, ap = ATOM_NEXT(ap)) {
6556                                         if (ParameterDoesContainAtom(n1, up, new2old[j + nsrcnew], kParameterLookupNoWildcard) || ParameterDoesContainAtom(n1, up, ap->type, kParameterLookupNoWildcard)) {
6557                                                 IntGroupAdd(move_g, i, 1);
6558                                                 break;
6559                                         }
6560                                 }
6561                         }
6562                         n2 = IntGroupGetCount(move_g);
6563                         if (n2 == 0)
6564                                 continue;
6565                         upary = (UnionPar *)calloc(sizeof(UnionPar), n2);
6566                         if (upary == NULL)
6567                                 goto panic;
6568                         /*  Copy parameters and renumber indices if necessary  */
6569                         for (i = 0; i < n2; i++) {
6570                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, IntGroupGetNthPoint(move_g, i));
6571                                 upary[i] = *up;
6572                                 ParameterRenumberAtoms(n1, upary + i, nsrc, old2new);
6573                         }
6574                         IntGroupClear(move_g);
6575                         IntGroupAdd(move_g, ParameterGetCountForType(dst->par, n1), n2);
6576                         /*  Insert new parameters  */
6577                         if (ParameterInsert(dst->par, n1, upary, move_g) < n2)
6578                                 goto panic;
6579                         IntGroupClear(move_g);
6580                         free(upary);
6581                 }
6582                 for (i = 0; i < nsrc; i++) {
6583                         old2new[i] += nsrcnew;  /*  Restore indices  */
6584                 }
6585                 IntGroupRelease(move_g);
6586         }
6587         
6588         /*  Clean up  */
6589         MoleculeCleanUpResidueTable(src);
6590         if (dst != NULL)
6591                 MoleculeCleanUpResidueTable(dst);
6592         free(new2old);
6593
6594         src->nframes = -1;  /*  Should be recalculated later  */
6595         if (dst != NULL)
6596                 dst->nframes = -1;  /*  Should be recalculated later  */
6597
6598         
6599         if (dstp != NULL)
6600                 *dstp = dst;
6601
6602         MoleculeIncrementModifyCount(src);
6603         src->needsMDRebuild = 1;
6604         __MoleculeUnlock(src);
6605         
6606         return 0;
6607
6608   panic:
6609         __MoleculeUnlock(src);
6610 /*    Panic("Low memory while removing atoms"); */
6611         return -1;
6612 }
6613
6614 /*  Separate molecule into two parts. The atoms specified by 'where' are moved
6615     from src to a new molecule, which is returned as *dstp. Dstp can be NULL, 
6616         in which case the moved atoms are discarded.  */
6617 int
6618 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset)
6619 {
6620         return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1);
6621 }
6622
6623 /*  Extract atoms from a given molecule into two parts. The atoms specified by 
6624         'where' are copied from src to a new molecule, which is returned as *dstp.
6625     If dummyFlag is non-zero, then the atoms that are not included in the group 
6626         but are connected to any atoms in the group are converted to "dummy" atoms 
6627         (i.e. with element "Du" and names beginning with an underscore) and included 
6628         in the new molecule object.  */
6629 int
6630 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
6631 {
6632         int retval;
6633
6634         /*  Extract the fragment  */
6635         retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0);
6636         if (retval != 0)
6637                 return retval;
6638
6639         if (dummyFlag) {
6640
6641                 /*  Search bonds crossing the molecule border  */
6642                 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
6643                 if (ig != NULL) {
6644                         IntGroupIterator iter;
6645                         Int i, idx;
6646                         idx = 1;
6647                         IntGroupIteratorInit(ig, &iter);
6648                         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6649                                 /*  The atoms at the border  */
6650                                 Int n1, n2, nn[3];
6651                                 Atom a, *ap;
6652                                 n1 = src->bonds[i*2];
6653                                 n2 = src->bonds[i*2+1];
6654                                 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
6655                                         int w = n1;
6656                                         n1 = n2;
6657                                         n2 = w;
6658                                         if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
6659                                                 continue;  /*  Actually this is an internal error  */
6660                                 }
6661                                 /*  n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule  */
6662                                 /*  Create a new dummy atom with the same segment/residue info with n1
6663                                     and the same position as n2  */
6664                                 ap = ATOM_AT_INDEX(src->atoms, n1);
6665                                 memset(&a, 0, gSizeOfAtomRecord);
6666                                 a.segSeq = ap->segSeq;
6667                                 memmove(a.segName, ap->segName, 4);
6668                                 a.resSeq = ap->resSeq;
6669                                 memmove(a.resName, ap->resName, 4);
6670                                 ElementToString(0, a.element);  /*  "Du"  */
6671                                 snprintf(a.aname, 4, "_%d", idx++);
6672                                 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
6673                                 /*  Add the dummy atom to the new molecule; nn[1] is the index
6674                                     of the new dummy atom in the new molecule  */
6675                                 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
6676                                 /*  Connect nn1 and nn2  */
6677                                 nn[2] = kInvalidIndex;
6678                                 MoleculeAddBonds(*dstp, 1, nn);
6679                         }
6680                         IntGroupIteratorRelease(&iter);
6681                         IntGroupRelease(ig);
6682                 }
6683         }
6684         
6685         return 0;
6686 }
6687
6688 int
6689 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds)
6690 {
6691         int i, j, n1, n2, n;
6692         Atom *ap;
6693         Int *bonds_tmp;
6694
6695         if (mp == NULL || bonds == NULL || nbonds <= 0)
6696                 return 0;
6697         if (mp->noModifyTopology)
6698                 return -4;  /*  Prohibited operation  */
6699
6700         /*  Check the bonds  */
6701         bonds_tmp = (Int *)malloc(sizeof(Int) * nbonds * 2);
6702         if (bonds_tmp == NULL)
6703                 return -4;  /*  Out of memory  */
6704         n = 0;
6705         for (i = 0; i < nbonds; i++) {
6706                 n1 = bonds[i * 2];
6707                 n2 = bonds[i * 2 + 1];
6708                 if (n1 < 0 || n1 >= mp->natoms || n2 < 0 || n2 >= mp->natoms)
6709                         return -1;  /*  Bad bond specification  */
6710                 if (n1 == n2)
6711                         return -5;
6712                 ap = ATOM_AT_INDEX(mp->atoms, n1);
6713                 if (ap->nconnects >= ATOMS_MAX_CONNECTS - 1 || ATOM_AT_INDEX(mp->atoms, n2)->nconnects >= ATOMS_MAX_CONNECTS - 1)
6714                         return -2;  /*  Too many bonds  */
6715                 /*  Check duplicates  */
6716                 for (j = 0; j < ap->nconnects; j++) {
6717                         if (ap->connects[j] == n2)
6718                                 break;
6719                 }
6720                 if (j == ap->nconnects) {
6721                         bonds_tmp[n * 2] = n1;
6722                         bonds_tmp[n * 2 + 1] = n2;
6723                         n++;
6724                 }
6725         }
6726         if (n == 0) {
6727                 /*  No bonds to add  */
6728                 free(bonds_tmp);
6729                 return 0;
6730         }
6731         
6732         __MoleculeLock(mp);
6733
6734         /*  Add connects[]  */
6735         for (i = 0; i < n; i++) {
6736                 n1 = bonds_tmp[i * 2];
6737                 n2 = bonds_tmp[i * 2 + 1];
6738                 ap = ATOM_AT_INDEX(mp->atoms, n1);
6739                 ap->connects[ap->nconnects++] = n2;
6740                 ap = ATOM_AT_INDEX(mp->atoms, n2);
6741                 ap->connects[ap->nconnects++] = n1;
6742         }
6743         
6744         /*  Expand the array and insert  */
6745         n1 = mp->nbonds;
6746 /*      if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, mp->nbonds + nb - 1, NULL) == NULL
6747         || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nb, sizeof(Int) * 2, where) != 0) */
6748         if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, mp->nbonds + n - 1, NULL) == NULL)
6749                 goto panic;
6750         memmove(mp->bonds + n1 * 2, bonds_tmp, sizeof(Int) * 2 * n);
6751
6752         /*  Add angles, dihedrals, impropers  */
6753         {
6754                 Int nangles, ndihedrals, nimpropers;
6755                 Int *angles, *dihedrals, *impropers;
6756                 Int k, n3, n4;
6757                 Int *ip;
6758                 Int temp[4];
6759                 Atom *ap1, *ap2;
6760
6761                 angles = dihedrals = impropers = NULL;
6762                 nangles = ndihedrals = nimpropers = 0;
6763
6764                 for (i = 0; i < n; i++) {
6765                         n1 = bonds_tmp[i * 2];
6766                         n2 = bonds_tmp[i * 2 + 1];
6767                         ap1 = ATOM_AT_INDEX(mp->atoms, n1);
6768                         ap2 = ATOM_AT_INDEX(mp->atoms, n2);
6769                         /*  Angles X-n1-n2  */
6770                         for (j = 0; j < ap1->nconnects; j++) {
6771                                 n3 = ap1->connects[j];
6772                                 if (n3 == n2)
6773                                         continue;
6774                                 temp[0] = n3;
6775                                 temp[1] = n1;
6776                                 temp[2] = n2;
6777                                 for (k = 0; k < nangles; k++) {
6778                                         ip = angles + k * 3;
6779                                         if (ip[1] == n1 && ((ip[0] == n3 && ip[2] == n2) || (ip[0] == n2 && ip[2] == n3)))
6780                                                 break;
6781                                 }
6782                                 if (k == nangles) {
6783                                         if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
6784                                                 goto panic;
6785                                 }
6786                                 /*  Dihedrals X-n1-n2-X  */
6787                                 for (k = 0; k < ap2->nconnects; k++) {
6788                                         n4 = ap2->connects[k];
6789                                         if (n4 == n1 || n4 == n3)
6790                                                 continue;
6791                                         temp[3] = n4;
6792                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
6793                                                 goto panic;
6794                                 }
6795                                 /*  Impropers X-n2-n1-X  */
6796                         /*      temp[1] = n2;
6797                                 temp[2] = n1;
6798                                 for (k = 0; k < ap1->nconnects; k++) {
6799                                         n4 = ap1->connects[k];
6800                                         if (n4 == n2 || n4 <= n3)
6801                                                 continue;
6802                                         temp[3] = n4;
6803                                         if (AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, temp) == NULL)
6804                                                 goto panic;
6805                                 } */
6806                         }
6807                         /*  Angles X-n2-n1  */
6808                         for (j = 0; j < ap2->nconnects; j++) {
6809                                 n3 = ap2->connects[j];
6810                                 if (n3 == n1)
6811                                         continue;
6812                                 temp[0] = n1;
6813                                 temp[1] = n2;
6814                                 temp[2] = n3;
6815                                 for (k = 0; k < nangles; k++) {
6816                                         ip = angles + k * 3;
6817                                         if (ip[1] == n2 && ((ip[0] == n3 && ip[2] == n1) || (ip[0] == n1 && ip[2] == n3)))
6818                                                 break;
6819                                 }
6820                                 if (k == nangles) {
6821                                         if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
6822                                                 goto panic;
6823                                 }
6824                         }
6825                 }
6826                 temp[0] = kInvalidIndex;
6827                 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
6828                         goto panic;
6829                 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
6830                         goto panic;
6831                 if (AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, temp) == NULL)
6832                         goto panic;
6833                 MoleculeAddAngles(mp, angles, NULL);
6834                 MoleculeAddDihedrals(mp, dihedrals, NULL);
6835                 MoleculeAddImpropers(mp, impropers, NULL);
6836                 if (angles != NULL)
6837                         free(angles);
6838                 if (dihedrals != NULL)
6839                         free(dihedrals);
6840                 if (impropers != NULL)
6841                         free(impropers);
6842         }
6843         
6844         MoleculeIncrementModifyCount(mp);
6845         mp->needsMDRebuild = 1;
6846         __MoleculeUnlock(mp);
6847
6848         free(bonds_tmp);
6849         return n;       
6850
6851   panic:
6852         __MoleculeUnlock(mp);
6853         Panic("Low memory while adding bonds");
6854         return -1;  /*  Not reached  */
6855 }
6856
6857 int
6858 MoleculeDeleteBonds(Molecule *mp, Int nbonds, const Int *bonds)
6859 {
6860         int i, j, n1, n2;
6861         Atom *ap;
6862
6863         if (mp == NULL || nbonds <= 0)
6864                 return 0;
6865         if (mp->noModifyTopology)
6866                 return -4;  /*  Prohibited operation  */
6867
6868         __MoleculeLock(mp);
6869
6870         /*  Update connects[]  */
6871         for (i = 0; i < nbonds; i++) {
6872                 n1 = bonds[i * 2];
6873                 n2 = bonds[i * 2 + 1];
6874                 ap = ATOM_AT_INDEX(mp->atoms, n1);
6875                 for (j = 0; j < ap->nconnects; j++) {
6876                         if (ap->connects[j] == n2) {
6877                                 memmove(&ap->connects[j], &ap->connects[j + 1], sizeof(Int) * (ap->nconnects - j - 1));
6878                                 ap->nconnects--;
6879                                 break;
6880                         }
6881                 }
6882                 ap = ATOM_AT_INDEX(mp->atoms, n2);
6883                 for (j = 0; j < ap->nconnects; j++) {
6884                         if (ap->connects[j] == n1) {
6885                                 memmove(&ap->connects[j], &ap->connects[j + 1], sizeof(Int) * (ap->nconnects - j - 1));
6886                                 ap->nconnects--;
6887                                 break;
6888                         }
6889                 }
6890         }
6891
6892         /*  Remove bonds, angles, dihedrals, impropers  */      
6893         {
6894                 IntGroup *bg, *ag, *dg, *ig;
6895                 Int *ip;
6896
6897                 bg = IntGroupNew();
6898                 ag = IntGroupNew();
6899                 dg = IntGroupNew();
6900                 ig = IntGroupNew();
6901                 if (bg == NULL || ag == NULL || dg == NULL || ig == NULL)
6902                         goto panic;
6903                 for (i = 0; i < nbonds; i++) {
6904                         n1 = bonds[i * 2];
6905                         n2 = bonds[i * 2 + 1];
6906                         for (j = 0; j < mp->nbonds; j++) {
6907                                 ip = mp->bonds + j * 2;
6908                                 if ((ip[0] == n1 && ip[1] == n2)
6909                                  || (ip[1] == n1 && ip[0] == n2)) {
6910                                         if (IntGroupAdd(bg, j, 1) != 0)
6911                                                 goto panic;
6912                                 }
6913                         }
6914                         for (j = 0; j < mp->nangles; j++) {
6915                                 ip = mp->angles + j * 3;
6916                                 if ((ip[0] == n1 && ip[1] == n2)
6917                                  || (ip[1] == n1 && ip[0] == n2)
6918                                  || (ip[1] == n1 && ip[2] == n2)
6919                                  || (ip[2] == n1 && ip[1] == n2)) {
6920                                         if (IntGroupAdd(ag, j, 1) != 0)
6921                                                 goto panic;
6922                                 }
6923                         }
6924                         for (j = 0; j < mp->ndihedrals; j++) {
6925                                 ip = mp->dihedrals + j * 4;
6926                                 if ((ip[1] == n1 && ip[2] == n2)
6927                                  || (ip[2] == n1 && ip[1] == n2)) {
6928                                         if (IntGroupAdd(dg, j, 1) != 0)
6929                                                 goto panic;
6930                                 }
6931                         }
6932                         for (j = 0; j < mp->nimpropers; j++) {
6933                                 ip = mp->impropers + j * 4;
6934                                 if ((ip[0] == n1 && ip[2] == n2)
6935                                  || (ip[1] == n1 && ip[2] == n2)
6936                                  || (ip[3] == n1 && ip[2] == n2)
6937                                  || (ip[0] == n2 && ip[2] == n1)
6938                                  || (ip[1] == n2 && ip[2] == n1)
6939                                  || (ip[3] == n2 && ip[2] == n1)) {
6940                                         if (IntGroupAdd(ig, j, 1) != 0)
6941                                                 goto panic;
6942                                 }
6943                         }
6944                 }
6945                 if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, NULL, sizeof(Int) * 2, bg) != 0)
6946                         goto panic;
6947                 mp->nbonds -= IntGroupGetCount(bg);
6948                 
6949                 if (IntGroupGetCount(ag) > 0)
6950                         MoleculeDeleteAngles(mp, NULL, ag);
6951                 if (IntGroupGetCount(dg) > 0)
6952                         MoleculeDeleteDihedrals(mp, NULL, dg);
6953                 if (IntGroupGetCount(ig) > 0)
6954                         MoleculeDeleteImpropers(mp, NULL, ig);
6955                 IntGroupRelease(bg);
6956                 IntGroupRelease(ag);
6957                 IntGroupRelease(dg);
6958                 IntGroupRelease(ig);
6959         }
6960
6961         MoleculeIncrementModifyCount(mp);
6962         mp->needsMDRebuild = 1;
6963         __MoleculeUnlock(mp);
6964
6965         return nbonds;
6966
6967   panic:
6968         __MoleculeUnlock(mp);
6969         Panic("Low memory while removing bonds");
6970         return -1;  /*  Not reached  */
6971 }
6972
6973 int
6974 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
6975 {
6976         int n1, nc;
6977         if (mp == NULL || angles == NULL)
6978                 return 0;
6979         if (mp->noModifyTopology)
6980                 return -4;  /*  Prohibited operation  */
6981
6982         __MoleculeLock(mp);
6983         if (where != NULL)
6984                 nc = IntGroupGetCount(where);
6985         else {
6986                 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
6987                         ;
6988                 nc = n1;
6989         }
6990         if (nc > 0) {
6991                 n1 = mp->nangles;
6992                 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
6993                         || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
6994                         __MoleculeUnlock(mp);
6995                         Panic("Low memory while adding angles");
6996                 }
6997         }
6998         mp->needsMDRebuild = 1;
6999         __MoleculeUnlock(mp);
7000         return nc;
7001 }
7002
7003 int
7004 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
7005 {
7006         int nc;
7007         if (mp == NULL || where == NULL)
7008                 return 0;
7009         if (mp->noModifyTopology)
7010                 return -4;  /*  Prohibited operation  */
7011         __MoleculeLock(mp);
7012         if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
7013                 __MoleculeUnlock(mp);
7014                 Panic("Low memory while adding angles");
7015         }
7016         mp->nangles -= (nc = IntGroupGetCount(where));
7017         mp->needsMDRebuild = 1;
7018         __MoleculeUnlock(mp);
7019         return nc;
7020 }
7021
7022 int
7023 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
7024 {
7025         int n1, nc;
7026         if (mp == NULL || dihedrals == NULL)
7027                 return 0;
7028         if (mp->noModifyTopology)
7029                 return -4;  /*  Prohibited operation  */
7030         if (where != NULL)
7031                 nc = IntGroupGetCount(where);
7032         else {
7033                 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
7034                         ;
7035                 nc = n1;
7036         }
7037         if (nc <= 0)
7038                 return 0;
7039         n1 = mp->ndihedrals;
7040         __MoleculeLock(mp);
7041         if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
7042         || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
7043                 __MoleculeUnlock(mp);
7044                 Panic("Low memory while adding dihedrals");
7045         }
7046         mp->needsMDRebuild = 1;
7047         __MoleculeUnlock(mp);
7048         return nc;
7049 }
7050
7051 int
7052 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
7053 {       
7054         int nc;
7055         if (mp == NULL || where == NULL)
7056                 return 0;
7057         if (mp->noModifyTopology)
7058                 return -4;  /*  Prohibited operation  */
7059         __MoleculeLock(mp);
7060         if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
7061                 __MoleculeUnlock(mp);
7062                 Panic("Low memory while adding dihedrals");
7063         }
7064         mp->ndihedrals -= (nc = IntGroupGetCount(where));
7065         mp->needsMDRebuild = 1;
7066         __MoleculeUnlock(mp);
7067         return nc;
7068 }
7069
7070 int
7071 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
7072 {
7073         int n1, nc;
7074         if (mp == NULL || impropers == NULL)
7075                 return 0;
7076         if (mp->noModifyTopology)
7077                 return -4;  /*  Prohibited operation  */
7078         if (where != NULL)
7079                 nc = IntGroupGetCount(where);
7080         else {
7081                 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
7082                         ;
7083                 nc = n1;
7084         }
7085         if (nc <= 0)
7086                 return 0;
7087         n1 = mp->nimpropers;
7088         __MoleculeLock(mp);
7089         if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
7090         || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
7091                 __MoleculeUnlock(mp);
7092                 Panic("Low memory while adding impropers");
7093         }
7094         mp->needsMDRebuild = 1;
7095         __MoleculeUnlock(mp);
7096         return nc;
7097 }
7098
7099 int
7100 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
7101 {
7102         int nc;
7103         if (mp == NULL || where == NULL)
7104                 return 0;
7105         if (mp->noModifyTopology)
7106                 return -4;  /*  Prohibited operation  */
7107         __MoleculeLock(mp);
7108         if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
7109                 __MoleculeUnlock(mp);
7110                 Panic("Low memory while adding impropers");
7111         }
7112         mp->nimpropers -= (nc = IntGroupGetCount(where));
7113         __MoleculeUnlock(mp);
7114         return nc;
7115 }
7116
7117 int
7118 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
7119 {
7120         Int i, *ip;
7121         if (mp == NULL || mp->bonds == NULL)
7122                 return -1;
7123         for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
7124                 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
7125                         return i;
7126         }
7127         return -1;
7128 }
7129
7130 int
7131 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
7132 {
7133         Int i, *ip;
7134         if (mp == NULL || mp->angles == NULL)
7135                 return -1;
7136         for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
7137                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
7138                         (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
7139                         return i;
7140         }
7141         return -1;
7142 }
7143
7144 int
7145 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
7146 {
7147         Int i, *ip;
7148         if (mp == NULL || mp->dihedrals == NULL)
7149                 return -1;
7150         for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
7151                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
7152                         (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
7153                         return i;
7154         }
7155         return -1;
7156 }
7157
7158 int
7159 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
7160 {
7161         Int i, *ip;
7162         if (mp == NULL || mp->impropers == NULL)
7163                 return -1;
7164         for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
7165                 if (n3 != ip[2])
7166                         continue;
7167                 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
7168                         (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
7169                         (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
7170                         return i;
7171         }
7172         return -1;
7173 }
7174
7175 /*  Remove the bond at bondIndex and create two dummy atoms instead.
7176     The dummy atoms are placed at the end of atoms[], and the residue
7177         numbers are the same as the root atoms (i.e. the atoms to which
7178         the dummy atoms are connected). The indices are returned in
7179         dummyIndices[0,1].  */
7180 int
7181 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
7182 {
7183         Int roots[3], newBonds[5];
7184         Vector dr;
7185         Atom *rootp[2];
7186         Atom na[2], *nap;
7187         int i, natoms;
7188         if (mp == NULL || mp->noModifyTopology)
7189                 return 0;
7190         if (bondIndex < 0 || bondIndex >= mp->nbonds)
7191                 return -1;
7192         roots[0] = mp->bonds[bondIndex * 2];
7193         roots[1] = mp->bonds[bondIndex * 2 + 1];
7194         roots[2] = kInvalidIndex;
7195         rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
7196         rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
7197         VecSub(dr, rootp[0]->r, rootp[1]->r);
7198         for (i = 0; i < 2; i++) {
7199                 float w;
7200                 nap = &na[i];
7201                 memmove(nap, rootp[i], sizeof(na));
7202                 nap->aname[0] = '*';
7203                 strcpy(nap->element, "Du");
7204                 nap->type = 0;
7205                 nap->charge = nap->weight = 0.0;
7206                 nap->atomicNumber = 0;
7207                 nap->nconnects = 0;
7208                 w = (i == 0 ? 0.4 : -0.4);
7209                 VecScaleInc(nap->r, dr, w);
7210                 VecZero(nap->v);
7211                 VecZero(nap->f);
7212                 nap->intCharge = 0;
7213                 nap->exflags = 0;
7214         }
7215
7216         /*  Expand atoms array and append the dummy atoms at the end  */
7217         __MoleculeLock(mp);
7218         natoms = mp->natoms;
7219         if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
7220                 goto panic;
7221         memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
7222         dummyIndices[0] = natoms;
7223         dummyIndices[1] = natoms + 1;
7224
7225         /*  Remove the old bond and create new bonds  */
7226 /*      ig = IntGroupNewWithPoints(bondIndex, 1, -1);
7227         if (ig == NULL)
7228                 goto panic;
7229         MoleculeDeleteBonds(mp, NULL, ig);
7230         IntGroupRelease(ig); */
7231         MoleculeDeleteBonds(mp, 1, roots);
7232         newBonds[0] = roots[0];
7233         newBonds[1] = dummyIndices[0];
7234         newBonds[2] = roots[1];
7235         newBonds[3] = dummyIndices[1];
7236         newBonds[4] = kInvalidIndex;
7237         
7238         i = (MoleculeAddBonds(mp, 2, newBonds) < 0 ? -1 : 0);
7239         mp->needsMDRebuild = 1;
7240         __MoleculeUnlock(mp);
7241         return i;
7242
7243 panic:
7244         __MoleculeUnlock(mp);
7245         Panic("Low memory during creating dummy atoms");
7246         return 1;
7247 }
7248
7249 /*  Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
7250     a bond between the two root atoms. The value bondIndex is used as a
7251         hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
7252         the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
7253         is ignored and the new bond is stored at the end of bonds[].  */
7254 int
7255 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
7256 {
7257         return 0;
7258 }
7259
7260 /*
7261 Int
7262 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
7263 {
7264         Int n1, *np1;
7265         if (mol == NULL || mol->noModifyTopology)
7266                 return -1;
7267         n1 = mol->nangles;
7268         np1 = mol->angles;
7269         mol->nangles = 0;
7270         mol->angles = NULL;
7271         if (nangles > 0) {
7272                 __MoleculeLock(mol);
7273                 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
7274                 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
7275                 mol->needsMDRebuild = 1;
7276                 __MoleculeUnlock(mol);
7277         }
7278         *outAngles = np1;
7279         return n1;
7280 }
7281                                                 
7282 Int
7283 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
7284 {
7285         Int n1, *np1;
7286         if (mol == NULL || mol->noModifyTopology)
7287                 return -1;
7288         n1 = mol->ndihedrals;
7289         np1 = mol->dihedrals;
7290         mol->ndihedrals = 0;
7291         mol->dihedrals = NULL;
7292         if (ndihedrals > 0) {
7293                 __MoleculeLock(mol);
7294                 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
7295                 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
7296                 mol->needsMDRebuild = 1;
7297                 __MoleculeUnlock(mol);
7298         }
7299         *outDihedrals = np1;
7300         return n1;
7301 }
7302
7303 Int
7304 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
7305 {
7306         Int n1, *np1;
7307         if (mol == NULL || mol->noModifyTopology)
7308                 return -1;
7309         n1 = mol->nimpropers;
7310         np1 = mol->impropers;
7311         mol->nimpropers = 0;
7312         mol->impropers = NULL;
7313         if (nimpropers > 0) {
7314                 __MoleculeLock(mol);
7315                 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
7316                 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
7317                 mol->needsMDRebuild = 1;
7318                 __MoleculeUnlock(mol);
7319         }
7320         *outImpropers = np1;
7321         return n1;
7322 }
7323 */
7324
7325 Int
7326 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
7327 {
7328         Int i, j, k, *ip;
7329         Atom *ap;
7330         Int nangles;
7331         Int *angles;
7332         
7333         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
7334                 return 0;  /*  molecule is empty  */
7335         if (mol->noModifyTopology)
7336                 return -1;
7337         nangles = 0;
7338         angles = NULL;
7339         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7340                 Int *cp = ap->connects;
7341                 for (j = 0; j < ap->nconnects; j++) {
7342                         Int j0 = cp[j];
7343                         for (k = j + 1; k < ap->nconnects; k++) {
7344                                 Int k0 = cp[k];
7345                                 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
7346                                         ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
7347                                         ip[0] = j0;
7348                                         ip[1] = i;
7349                                         ip[2] = k0;
7350                                 }
7351                         }
7352                 }
7353         }
7354         if (nangles > 0) {
7355                 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
7356                 ip[0] = -1;
7357                 nangles--;
7358         }
7359         if (outAngles != NULL)
7360                 *outAngles = angles;
7361         return nangles;
7362 }
7363
7364 Int
7365 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
7366 {
7367         Int n1, n2, n3, n4, *ip;
7368         Atom *ap2, *ap3;
7369         Int ndihedrals;
7370         Int *dihedrals;
7371         
7372         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
7373                 return 0;  /*  molecule is empty  */
7374         ndihedrals = 0;
7375         dihedrals = NULL;
7376         for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
7377                 Int i1, i3, i4, *ip;
7378                 for (i3 = 0; i3 < ap2->nconnects; i3++) {
7379                         n3 = ap2->connects[i3];
7380                         if (n2 > n3)
7381                                 continue;
7382                         ap3 = ATOM_AT_INDEX(mol->atoms, n3);
7383                         for (i1 = 0; i1 < ap2->nconnects; i1++) {
7384                                 n1 = ap2->connects[i1];
7385                                 if (n1 == n3)
7386                                         continue;
7387                                 for (i4 = 0; i4 < ap3->nconnects; i4++) {
7388                                         n4 = ap3->connects[i4];
7389                                         if (n2 == n4 || n1 == n4)
7390                                                 continue;
7391                                         if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
7392                                                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
7393                                                 ip[0] = n1;
7394                                                 ip[1] = n2;
7395                                                 ip[2] = n3;
7396                                                 ip[3] = n4;
7397                                         }
7398                                 }
7399                         }
7400                 }
7401         }
7402         if (ndihedrals > 0) {
7403                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
7404                 ip[0] = -1;
7405                 ndihedrals--;
7406         }
7407         if (outDihedrals != NULL)
7408                 *outDihedrals = dihedrals;
7409         return ndihedrals;
7410 }
7411
7412 Int
7413 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
7414 {
7415         Int n1, n2, n3, n4, t1, t2, t3, t4, *ip;
7416         Parameter *par = mol->par;
7417         Atom *ap, *ap3;
7418         Int nimpropers;
7419         Int *impropers;
7420         
7421         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
7422                 return 0;  /*  molecule is empty  */
7423         if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
7424                 return 0;  /*  No improper parameters are defined  */
7425         nimpropers = 0;
7426         impropers = NULL;
7427         ap = mol->atoms;
7428         for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
7429                 Int i1, i2, i4, found, *ip;
7430                 t3 = ap3->type;
7431                 for (i1 = 0; i1 < ap3->nconnects; i1++) {
7432                         n1 = ap3->connects[i1];
7433                         t1 = ATOM_AT_INDEX(ap, n1)->type;
7434                         for (i2 = i1 + 1; i2 < ap3->nconnects; i2++) {
7435                                 n2 = ap3->connects[i2];
7436                                 t2 = ATOM_AT_INDEX(ap, n2)->type;
7437                                 for (i4 = i2 + 1; i4 < ap3->nconnects; i4++) {
7438                                         n4 = ap3->connects[i4];
7439                                         t4 = ATOM_AT_INDEX(ap, n4)->type;
7440                                         found = 0;
7441                                         if (ParameterLookupImproperPar(par, n1, n2, n3, n4, 1) != NULL)
7442                                                 found = 1;
7443                                         else if (ParameterLookupImproperPar(par, t1, t2, t3, t4, 0) != NULL)
7444                                                 found = 1;
7445                                         else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, 0) != NULL)
7446                                                 found = 1;
7447                                         if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
7448                                                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
7449                                                 ip[0] = n1;
7450                                                 ip[1] = n2;
7451                                                 ip[2] = n3;
7452                                                 ip[3] = n4;
7453                                         }
7454                                 }
7455                         }
7456                 }
7457         }
7458         if (nimpropers > 0) {
7459                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
7460                 ip[0] = -1;
7461                 nimpropers--;
7462         }
7463         if (outImpropers != NULL)
7464                 *outImpropers = impropers;
7465         return nimpropers;
7466 }
7467
7468 #pragma mark ====== Residues ======
7469
7470 void
7471 MoleculeCleanUpResidueTable(Molecule *mp)
7472 {
7473         int i, maxres;
7474         Atom *ap;
7475         if (mp == NULL || mp->natoms == 0)
7476                 return;
7477         maxres = 0;
7478         __MoleculeLock(mp);
7479         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7480                 if (ap->resSeq >= maxres)
7481                         maxres = ap->resSeq + 1;
7482                 if (ap->resSeq < mp->nresidues) {
7483                         if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
7484                                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
7485                 } else {
7486                         AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
7487                 }
7488         }
7489         if (maxres < mp->nresidues)
7490                 mp->nresidues = maxres;
7491         __MoleculeUnlock(mp);
7492 }
7493
7494 /*  Change the number of residues. If nresidues is greater than the current value,
7495     then the array mp->residues is expanded with null names. If nresidues is smaller
7496         than the current value, mp->nresidues is set to the smallest possible value
7497         that is no smaller than nresidues and larger than any of the resSeq values.  */
7498 int
7499 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
7500 {
7501         int n;
7502         if (mp == NULL)
7503                 return 0;
7504         if (mp->nresidues == nresidues)
7505                 return nresidues;
7506         else if (mp->nresidues < nresidues) {
7507                 __MoleculeLock(mp);
7508                 n = mp->nresidues;
7509                 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
7510                 while (n < nresidues)
7511                         mp->residues[n++][0] = 0;
7512                 __MoleculeUnlock(mp);
7513                 return nresidues;
7514         } else {
7515                 int i;
7516                 Atom *ap;
7517                 n = nresidues;
7518                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7519                         if (ap->resSeq >= n)
7520                                 n = ap->resSeq + 1;
7521                 }
7522                 mp->nresidues = n;
7523                 return n;
7524         }
7525 }
7526
7527 int
7528 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
7529 {
7530         IntGroupIterator iter;
7531         int withArray, resSeq, maxSeq;
7532         int i, j;
7533         Atom *ap;
7534         
7535         /*  If LSB of resSeqs is 1, then a constant value is used for all specified atoms  */
7536         if (((int)resSeqs & 1) == 0) {
7537                 withArray = 1;
7538                 resSeq = 0;
7539         } else {
7540                 withArray = 0;
7541                 resSeq = ((int)resSeqs - 1) / 2;
7542         }
7543         
7544         IntGroupIteratorInit(group, &iter);
7545
7546         /*  Change resSeqs  */
7547         maxSeq = 0;
7548         j = 0;
7549         __MoleculeLock(mp);
7550         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7551                 ap = ATOM_AT_INDEX(mp->atoms, i);
7552                 if (withArray)
7553                         resSeq = resSeqs[j++];
7554                 if (resSeq > maxSeq)
7555                         maxSeq = resSeq;
7556                 ap->resSeq = resSeq;
7557         }
7558         __MoleculeUnlock(mp);
7559
7560         /*  Expand array if necessary  */
7561         if (maxSeq >= mp->nresidues)
7562                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
7563
7564         /*  Synchronize resName and residues[]  */
7565         __MoleculeLock(mp);
7566         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7567                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
7568                         continue;
7569                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
7570         }
7571         IntGroupIteratorRelease(&iter);
7572         __MoleculeUnlock(mp);
7573         
7574         MoleculeIncrementModifyCount(mp);
7575         
7576         return 0;
7577 }
7578
7579 int
7580 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
7581 {
7582         return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(resSeq * 2 + 1));
7583 }
7584
7585 /*  Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
7586     specifies the mp->nresidues after modifying the residue numbers.
7587         If all atoms are modified, then the table of residue names is also shifted. Otherwise,
7588         the table of residue names is not touched. */
7589 int
7590 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
7591 {
7592         int i, maxSeq, nmodatoms;
7593         Atom *ap;
7594         IntGroupIterator iter;
7595         IntGroupIteratorInit(group, &iter);
7596         maxSeq = 0;
7597         if (nresidues < 0)
7598                 nresidues = mp->nresidues;
7599         nmodatoms = 0;
7600         __MoleculeLock(mp);
7601         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7602                 ap = ATOM_AT_INDEX(mp->atoms, i);
7603                 ap->resSeq += offset;
7604                 if (ap->resSeq < 0) {
7605                         /*  Bad argument; undo change and returns this index + 1  */
7606                         int bad_index = i;
7607                         ap->resSeq -= offset;
7608                         while ((i = IntGroupIteratorLast(&iter)) >= 0) {
7609                                 ap = ATOM_AT_INDEX(mp->atoms, i);
7610                                 ap->resSeq -= offset;
7611                         }
7612                         IntGroupIteratorRelease(&iter);
7613                         return bad_index + 1;
7614                 }
7615                 if (ap->resSeq > maxSeq)
7616                         maxSeq = ap->resSeq;
7617                 nmodatoms++;
7618         }
7619         if (maxSeq >= nresidues)
7620                 nresidues = maxSeq + 1;
7621         if (offset < 0 && nmodatoms == mp->natoms) {
7622                 /*  Shift the residue names downward  */
7623                 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
7624         }
7625         __MoleculeUnlock(mp);
7626         MoleculeChangeNumberOfResidues(mp, nresidues);
7627         if (offset > 0 && nmodatoms == mp->natoms) {
7628                 /*  Shift the residue names upward  */
7629                 __MoleculeLock(mp);
7630                 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
7631                 __MoleculeUnlock(mp);
7632         }
7633         IntGroupIteratorRelease(&iter);
7634
7635         MoleculeIncrementModifyCount(mp);
7636         
7637         return 0;
7638 }
7639
7640 /*  Change residue names for the specified residue numbers. Names is an array of
7641     chars containing argc*4 characters, and every 4 characters represent a
7642         residue name; characters '\x01'-'\x1f' are converted to '\0', which allow 
7643         names to be handled as a C string.  */
7644 int
7645 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
7646 {
7647         int i, maxSeq;
7648         Atom *ap;
7649         maxSeq = 0;
7650         for (i = 0; i < argc; i++) {
7651                 if (maxSeq < resSeqs[i])
7652                         maxSeq = resSeqs[i];
7653         }
7654         if (maxSeq >= mp->nresidues)
7655                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
7656         __MoleculeLock(mp);
7657         for (i = 0; i < argc; i++) {
7658                 char *p = mp->residues[resSeqs[i]];
7659                 int j;
7660                 strncpy(p, names + i * 4, 4);
7661                 for (j = 0; j < 4; j++) {
7662                         if (p[j] >= 0 && p[j] < 0x20)
7663                                 p[j] = 0;
7664                 }
7665         }
7666         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7667                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
7668         }
7669         __MoleculeUnlock(mp);
7670
7671         MoleculeIncrementModifyCount(mp);
7672         
7673         return 0;
7674 }
7675
7676 /*  Returns the maximum residue number actually used  */
7677 int
7678 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
7679 {
7680         int i, maxSeq;
7681         Atom *ap;
7682         maxSeq = -1;
7683         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7684                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
7685                         continue;
7686                 if (ap->resSeq > maxSeq)
7687                         maxSeq = ap->resSeq;
7688         }
7689         return maxSeq;
7690 }
7691
7692 /*  Returns the minimum residue number actually used  */
7693 int
7694 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
7695 {
7696         int i, minSeq;
7697         Atom *ap;
7698         minSeq = ATOMS_MAX_NUMBER;
7699         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7700                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
7701                         continue;
7702                 if (ap->resSeq < minSeq)
7703                         minSeq = ap->resSeq;
7704         }
7705         return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
7706 }
7707
7708 #pragma mark ====== Sort by Residues ======
7709
7710 static int
7711 sAtomSortComparator(const void *a, const void *b)
7712 {
7713         const Atom *ap, *bp;
7714         ap = *((const Atom **)a);
7715         bp = *((const Atom **)b);
7716         if (ap->resSeq == bp->resSeq) {
7717                 /*  Retain the original order (i.e. atom with larger pointer address is larger)  */
7718                 if (ap < bp)
7719                         return -1;
7720                 else if (ap > bp)
7721                         return 1;
7722                 else return 0;
7723         } else {
7724                 /*  Compare the residue sequence. However, residue sequence 0 is always larger.  */
7725                 if (ap->resSeq == 0)
7726                         return 1;
7727                 else if (bp->resSeq == 0)
7728                         return -1;
7729                 else if (ap->resSeq < bp->resSeq)
7730                         return -1;
7731                 else if (ap->resSeq > bp->resSeq)
7732                         return 1;
7733                 else return 0;
7734         }
7735 }
7736
7737 static void
7738 sMoleculeReorder(Molecule *mp)
7739 {
7740         int i, res, prevRes;
7741         Atom **apArray;
7742         Int *old2new;
7743         Atom *newAtoms;
7744         if (mp == NULL || mp->natoms <= 1)
7745                 return;
7746
7747         /*  Sort the atoms, bonds, etc. */
7748         apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
7749         old2new = (Int *)calloc(sizeof(Int), mp->natoms);
7750         newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
7751         if (apArray == NULL || old2new == NULL || newAtoms == NULL)
7752                 Panic("Low memory during reordering atoms");
7753         for (i = 0; i < mp->natoms; i++)
7754                 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
7755
7756         /*  Sort the atoms. Note: apArray is an array of "Pointer to Atom"  */
7757         qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
7758         
7759         /*  Make a table of 'which atom becomes which'  */
7760         for (i = 0; i < mp->natoms; i++) {
7761                 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
7762                 old2new[j] = i;
7763         }
7764         
7765         /*  Renumber the bonds, etc.  */
7766         for (i = 0; i < mp->nbonds * 2; i++) {
7767                 mp->bonds[i] = old2new[mp->bonds[i]];
7768         }
7769         for (i = 0; i < mp->nangles * 3; i++) {
7770                 mp->angles[i] = old2new[mp->angles[i]];
7771         }
7772         for (i = 0; i < mp->ndihedrals * 4; i++) {
7773                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
7774         }
7775         for (i = 0; i < mp->nimpropers * 4; i++) {
7776                 mp->impropers[i] = old2new[mp->impropers[i]];
7777         }
7778         for (i = 0; i < mp->natoms; i++) {
7779                 Int *ip, j;
7780                 for (j = 0, ip = apArray[i]->connects; j < apArray[i]->nconnects; j++, ip++)
7781                         *ip = old2new[*ip];
7782         }
7783         
7784         /*  Renumber the residues so that the residue numbers are contiguous  */
7785         res = prevRes = 0;
7786         for (i = 0; i < mp->natoms; i++) {
7787                 if (apArray[i]->resSeq == 0)
7788                         break;
7789                 if (apArray[i]->resSeq != prevRes) {
7790                         res++;
7791                         prevRes = apArray[i]->resSeq;
7792                         if (prevRes != res) {
7793                                 strncpy(mp->residues[res], mp->residues[prevRes], 4);
7794                         }
7795                 }
7796                 apArray[i]->resSeq = res;
7797         }
7798         mp->nresidues = res + 1;
7799
7800         /*  Sort the atoms and copy back to atoms[] */
7801         for (i = 0; i < mp->natoms; i++) {
7802                 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
7803         }
7804         memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
7805         
7806         /*  Free the locally allocated storage  */
7807         free(newAtoms);
7808         free(old2new);
7809         free(apArray);
7810
7811 }
7812
7813 /*  Renumber atoms  */
7814 int
7815 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
7816 {
7817         Int *old2new, i, j, retval;
7818         Atom *saveAtoms;
7819         if (mp == NULL)
7820                 return 0;
7821         if (mp->noModifyTopology)
7822                 return -1;
7823         if (old2new_out != NULL)
7824                 old2new = old2new_out;
7825         else
7826                 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
7827         saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
7828         if (old2new == NULL || saveAtoms == NULL)
7829                 Panic("Low memory during reordering atoms");
7830         memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
7831         __MoleculeLock(mp);
7832         for (i = 0; i < mp->natoms; i++)
7833                 old2new[i] = -1;
7834         for (i = 0; i < isize && i < mp->natoms; i++) {
7835                 j = new2old[i];
7836                 if (j < 0 || j >= mp->natoms) {
7837                         retval = 1; /* Out of range */
7838                         goto end;
7839                 }
7840                 if (old2new[j] != -1) {
7841                         retval = 2;  /*  Duplicate entry  */
7842                         goto end;
7843                 }
7844                 old2new[j] = i;
7845         }
7846         if (i < mp->natoms) {
7847                 for (j = 0; j < mp->natoms; j++) {
7848                         if (old2new[j] != -1)
7849                                 continue;
7850                         old2new[j] = i++;
7851                 }
7852         }
7853         if (i != mp->natoms) {
7854                 retval = 3;  /*  Internal inconsistency  */
7855                 goto end;
7856         }
7857
7858         /*  Renumber the bonds, etc.  */
7859         for (i = 0; i < mp->nbonds * 2; i++) {
7860                 mp->bonds[i] = old2new[mp->bonds[i]];
7861         }
7862         for (i = 0; i < mp->nangles * 3; i++) {
7863                 mp->angles[i] = old2new[mp->angles[i]];
7864         }
7865         for (i = 0; i < mp->ndihedrals * 4; i++) {
7866                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
7867         }
7868         for (i = 0; i < mp->nimpropers * 4; i++) {
7869                 mp->impropers[i] = old2new[mp->impropers[i]];
7870         }
7871         for (i = 0; i < mp->natoms; i++) {
7872                 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
7873                 Int *ip;
7874                 for (j = 0, ip = ap->connects; j < ap->nconnects; j++, ip++)
7875                         *ip = old2new[*ip];
7876         }
7877         
7878         /*  Renumber the atoms  */
7879         for (i = 0; i < mp->natoms; i++)
7880                 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
7881         retval = 0;
7882
7883         MoleculeIncrementModifyCount(mp);
7884         mp->needsMDRebuild = 1;
7885
7886   end:
7887         __MoleculeUnlock(mp);
7888         free(saveAtoms);
7889         if (old2new_out == NULL)
7890                 free(old2new);
7891         return retval;
7892 }
7893
7894 #pragma mark ====== Coordinate Transform ======
7895
7896 void
7897 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
7898 {
7899         int i;
7900         Atom *ap;
7901         if (mp == NULL || tr == NULL)
7902                 return;
7903         __MoleculeLock(mp);
7904         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7905                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
7906                         continue;
7907                 TransformVec(&ap->r, tr, &ap->r);
7908         }
7909         mp->needsMDCopyCoordinates = 1;
7910         __MoleculeUnlock(mp);
7911         sMoleculeNotifyChangeAppearance(mp);
7912 }
7913
7914 void
7915 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
7916 {
7917         int i;
7918         Atom *ap;
7919         if (mp == NULL || tr == NULL)
7920                 return;
7921         __MoleculeLock(mp);
7922         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7923                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
7924                         continue;
7925                 TransformVec(&ap->r, tr, &ap->r);
7926         }
7927         mp->needsMDCopyCoordinates = 1;
7928         __MoleculeUnlock(mp);
7929         sMoleculeNotifyChangeAppearance(mp);
7930 }
7931
7932 void
7933 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
7934 {
7935         int i;
7936         Atom *ap;
7937         if (mp == NULL || vp == NULL)
7938                 return;
7939         __MoleculeLock(mp);
7940         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7941                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
7942                         continue;
7943                 VecInc(ap->r, *vp);
7944         }
7945         mp->needsMDCopyCoordinates = 1;
7946         __MoleculeUnlock(mp);
7947         sMoleculeNotifyChangeAppearance(mp);
7948 }
7949
7950 void
7951 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
7952 {
7953         int i;
7954         Double w;
7955         Transform tr;
7956         Vector cv;
7957         Atom *ap;
7958         if (mp == NULL || axis == NULL)
7959                 return;
7960         w = VecLength(*axis);
7961         if (w < 1e-7)
7962                 return;
7963         __MoleculeLock(mp);
7964         /*  Construct a rotation transform: p' = c + A * (p - c)  */
7965         if (center == NULL)
7966                 cv.x = cv.y = cv.z = 0.0;
7967         else
7968                 cv = *center;
7969         TransformForRotation(tr, axis, angle, &cv);
7970
7971         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7972                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
7973                         continue;
7974                 TransformVec(&ap->r, tr, &ap->r);
7975         }
7976         mp->needsMDCopyCoordinates = 1;
7977         __MoleculeUnlock(mp);
7978         sMoleculeNotifyChangeAppearance(mp);
7979 }
7980
7981 void
7982 MoleculeReaxis(Molecule *mp, const Vector *xaxis, const Vector *yaxis, const Vector *zaxis, IntGroup *group)
7983 {
7984         int i;
7985         Atom *ap;
7986         Vector v;
7987         if (mp == NULL || xaxis == NULL || yaxis == NULL || zaxis == NULL)
7988                 return;
7989         __MoleculeLock(mp);
7990         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7991                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
7992                         continue;
7993                 v.x = VecDot(ap->r, *xaxis);
7994                 v.y = VecDot(ap->r, *yaxis);
7995                 v.z = VecDot(ap->r, *zaxis);
7996                 ap->r = v;
7997         }
7998         mp->needsMDCopyCoordinates = 1;
7999         __MoleculeUnlock(mp);
8000         sMoleculeNotifyChangeAppearance(mp);
8001 }
8002
8003 int
8004 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
8005 {
8006         int i;
8007         Atom *ap;
8008         Double w;
8009         if (mp == NULL || center == NULL)
8010                 return 1;
8011         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
8012                 return 2;   /*  Empty molecule  */
8013         w = 0.0;
8014         center->x = center->y = center->z = 0.0;
8015         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8016                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8017                         continue;
8018                 VecScaleInc(*center, ap->r, ap->weight);
8019                 w += ap->weight;
8020         }
8021         if (w < 1e-7)
8022                 return 3;  /*  Atomic weights are not defined?  */
8023         w = 1.0 / w;
8024         VecScaleSelf(*center, w);
8025         return 0;
8026 }
8027
8028 int
8029 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
8030 {
8031         Vector vmin, vmax;
8032         int i;
8033         Atom *ap;
8034         if (mp == NULL)
8035                 return 1;
8036         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
8037                 return 2;   /*  Empty molecule  */
8038         vmin.x = vmin.y = vmin.z = 1e50;
8039         vmax.x = vmax.y = vmax.z = -1e50;
8040         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8041                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8042                         continue;
8043                 if (vmin.x > ap->r.x)
8044                         vmin.x = ap->r.x;
8045                 if (vmin.y > ap->r.y)
8046                         vmin.y = ap->r.y;
8047                 if (vmin.z > ap->r.z)
8048                         vmin.z = ap->r.z;
8049                 if (vmax.x < ap->r.x)
8050                         vmax.x = ap->r.x;
8051                 if (vmax.y < ap->r.y)
8052                         vmax.y = ap->r.y;
8053                 if (vmax.z < ap->r.z)
8054                         vmax.z = ap->r.z;
8055         }
8056         if (min != NULL)
8057                 *min = vmin;
8058         if (max != NULL)
8059                 *max = vmax;
8060         return 0;       
8061 }
8062
8063 #pragma mark ====== Measurements ======
8064
8065 Double
8066 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
8067 {
8068         Vector r1, r2;
8069 /*      if (mp->is_xtal_coord) {
8070                 TransformVec(&r1, mp->cell->tr, vp1);
8071                 TransformVec(&r2, mp->cell->tr, vp2);
8072         } else */ {
8073                 r1 = *vp1;
8074                 r2 = *vp2;
8075         }
8076         VecDec(r1, r2);
8077         return VecLength(r1);
8078 }
8079
8080 Double
8081 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
8082 {
8083         Vector r1, r2, r3;
8084         double w;
8085 /*      if (mp->is_xtal_coord) {
8086                 TransformVec(&r1, mp->cell->tr, vp1);
8087                 TransformVec(&r2, mp->cell->tr, vp2);
8088                 TransformVec(&r3, mp->cell->tr, vp3);
8089         } else */ {
8090                 r1 = *vp1;
8091                 r2 = *vp2;
8092                 r3 = *vp3;
8093         }
8094         VecDec(r1, r2);
8095         VecDec(r3, r2);
8096         w = VecLength(r1) * VecLength(r3);
8097         if (w < 1e-20)
8098                 return NAN;
8099         return acos(VecDot(r1, r3) / w) * kRad2Deg;
8100 }
8101
8102 Double
8103 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
8104 {
8105         Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
8106         double w1, w2, w3;
8107 /*      if (mp->is_xtal_coord) {
8108                 TransformVec(&r1, mp->cell->tr, vp1);
8109                 TransformVec(&r2, mp->cell->tr, vp2);
8110                 TransformVec(&r3, mp->cell->tr, vp3);
8111                 TransformVec(&r4, mp->cell->tr, vp4);
8112         } else */ {
8113                 r1 = *vp1;
8114                 r2 = *vp2;
8115                 r3 = *vp3;
8116                 r4 = *vp4;
8117         }
8118         VecSub(r21, r1, r2);
8119         VecSub(r32, r2, r3);
8120         VecSub(r43, r3, r4);
8121         VecCross(v1, r21, r32);
8122         VecCross(v2, r32, r43);
8123         VecCross(v3, r32, v1);
8124         w1 = VecLength(v1);
8125         w2 = VecLength(v2);
8126         w3 = VecLength(v3);
8127         if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
8128                 return NAN;
8129         } else {
8130                 w1 = 1.0 / w1;
8131                 w2 = 1.0 / w2;
8132                 w3 = 1.0 / w3;
8133                 VecScaleSelf(v1, w1);
8134                 VecScaleSelf(v2, w2);
8135                 VecScaleSelf(v3, w3);
8136                 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
8137         }
8138 }
8139
8140 #pragma mark ====== XtalCell Parameters ======
8141
8142 void
8143 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
8144 {
8145         if (mp->cell != NULL) {
8146                 TransformVec(dst, mp->cell->tr, src);
8147         } else *dst = *src;
8148 }
8149
8150 void
8151 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
8152 {
8153         if (mp->cell != NULL) {
8154                 TransformVec(dst, mp->cell->rtr, src);
8155         } else *dst = *src;
8156 }
8157
8158 int
8159 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
8160 {
8161         static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
8162         int n1, n2, n3;
8163         Vector *vp1, *vp2, *vp3;
8164         Vector v1, v2;
8165
8166         if (cp == NULL)
8167                 return 0;
8168         for (n1 = 0; n1 < 3; n1++) {
8169                 if (cp->flags[n1] != 0)
8170                         break;
8171         }
8172         if (n1 == 3) {
8173                 /*  All directions are non-periodic  */
8174                 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
8175                 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
8176         } else {
8177                 n2 = (n1 + 1) % 3;
8178                 n3 = (n1 + 2) % 3;
8179                 vp1 = &(cp->axes[n1]);
8180                 vp2 = &(cp->axes[n2]);
8181                 vp3 = &(cp->axes[n3]);
8182                 cp->tr[n1] = vp1->x;
8183                 cp->tr[n1 + 3] = vp1->y;
8184                 cp->tr[n1 + 6] = vp1->z;
8185                 cp->tr[9] = cp->origin.x;
8186                 cp->tr[10] = cp->origin.y;
8187                 cp->tr[11] = cp->origin.z;
8188                 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
8189                         /*  1-dimensional or 2-dimensional system  */
8190                         /*  Create "dummy" axes, so that transforms between internal and cartesian coordinates are
8191                          possible with a single matrix  */
8192                         if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
8193                                 /*  1-dimensional  */
8194                                 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
8195                                 VecCross(v1, *vp1, xvec);
8196                                 VecCross(v2, *vp1, yvec);
8197                                 if (VecLength2(v1) < VecLength2(v2))
8198                                         v1 = v2;
8199                                 VecCross(v2, *vp1, v1);
8200                                 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
8201                                         return -1;   /*  Non-regular transform  */
8202                         } else if (cp->flags[n2] == 0) {
8203                                 v2 = *vp3;
8204                                 VecCross(v1, v2, *vp1);
8205                                 if (NormalizeVec(&v1, &v1))
8206                                         return -1;  /*  Non-regular transform  */
8207                         } else {
8208                                 v1 = *vp2;
8209                                 VecCross(v2, *vp1, v1);
8210                                 if (NormalizeVec(&v2, &v2))
8211                                         return -1;  /*  Non-regular transform  */
8212                         }
8213                         cp->tr[n2] = v1.x;
8214                         cp->tr[n2 + 3] = v1.y;
8215                         cp->tr[n2 + 6] = v1.z;
8216                         cp->tr[n3] = v2.x;
8217                         cp->tr[n3 + 3] = v2.y;
8218                         cp->tr[n3 + 6] = v2.z;
8219                 } else {
8220                         VecCross(v1, *vp1, *vp2);
8221                         if (fabs(VecDot(v1, *vp3)) < 1e-7)
8222                                 return -1;  /*  Non-regular transform  */
8223                         cp->tr[n2] = vp2->x;
8224                         cp->tr[n2 + 3] = vp2->y;
8225                         cp->tr[n2 + 6] = vp2->z;
8226                         cp->tr[n3] = vp3->x;
8227                         cp->tr[n3 + 3] = vp3->y;
8228                         cp->tr[n3 + 6] = vp3->z;
8229                 }
8230         }
8231         if (TransformInvert(cp->rtr, cp->tr))
8232                 return -1;  /*  Non-regular transform  */
8233
8234         /*  Calculate the reciprocal cell parameters  */
8235         cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[3] * cp->rtr[3] + cp->rtr[6] * cp->rtr[6]);
8236         cp->rcell[1] = sqrt(cp->rtr[1] * cp->rtr[1] + cp->rtr[4] * cp->rtr[4] + cp->rtr[7] * cp->rtr[7]);
8237         cp->rcell[2] = sqrt(cp->rtr[2] * cp->rtr[2] + cp->rtr[5] * cp->rtr[5] + cp->rtr[8] * cp->rtr[8]);
8238         cp->rcell[3] = acos((cp->rtr[1] * cp->rtr[2] + cp->rtr[4] * cp->rtr[5] + cp->rtr[7] * cp->rtr[8]) / (cp->rcell[1] * cp->rcell[2])) * kRad2Deg;
8239         cp->rcell[4] = acos((cp->rtr[2] * cp->rtr[0] + cp->rtr[5] * cp->rtr[3] + cp->rtr[8] * cp->rtr[6]) / (cp->rcell[2] * cp->rcell[0])) * kRad2Deg;
8240         cp->rcell[5] = acos((cp->rtr[0] * cp->rtr[1] + cp->rtr[3] * cp->rtr[4] + cp->rtr[6] * cp->rtr[7]) / (cp->rcell[0] * cp->rcell[1])) * kRad2Deg;
8241         
8242         if (calc_abc) {
8243                 /*  Calculate a, b, c, alpha, beta, gamma  */
8244                 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[3] * cp->tr[3] + cp->tr[6] * cp->tr[6]);
8245                 cp->cell[1] = sqrt(cp->tr[1] * cp->tr[1] + cp->tr[4] * cp->tr[4] + cp->tr[7] * cp->tr[7]);
8246                 cp->cell[2] = sqrt(cp->tr[2] * cp->tr[2] + cp->tr[5] * cp->tr[5] + cp->tr[8] * cp->tr[8]);
8247                 cp->cell[3] = acos((cp->tr[1] * cp->tr[2] + cp->tr[4] * cp->tr[5] + cp->tr[7] * cp->tr[8]) / (cp->cell[1] * cp->cell[2])) * kRad2Deg;
8248                 cp->cell[4] = acos((cp->tr[2] * cp->tr[0] + cp->tr[5] * cp->tr[3] + cp->tr[8] * cp->tr[6]) / (cp->cell[2] * cp->cell[0])) * kRad2Deg;
8249                 cp->cell[5] = acos((cp->tr[0] * cp->tr[1] + cp->tr[3] * cp->tr[4] + cp->tr[6] * cp->tr[7]) / (cp->cell[0] * cp->cell[1])) * kRad2Deg;
8250         }
8251         
8252         return 0;
8253 }
8254
8255 void
8256 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
8257 {
8258         XtalCell *cp;
8259         int i;
8260         Atom *ap;
8261         Transform cmat;
8262         if (mp == NULL)
8263                 return;
8264         __MoleculeLock(mp);
8265         memset(&cmat, 0, sizeof(Transform));
8266         if (a == 0.0) {
8267                 if (mp->cell != NULL) {
8268                         memmove(&cmat, &(mp->cell->tr), sizeof(Transform));
8269                         free(mp->cell);
8270                 } else {
8271                         cmat[0] = cmat[4] = cmat[8] = 1.0;
8272                 }
8273                 mp->cell = NULL;
8274         /*      mp->is_xtal_coord = 0; */
8275         } else {
8276                 cp = mp->cell;
8277                 if (cp == NULL) {
8278                         cp = (XtalCell *)malloc(sizeof(XtalCell));
8279                         if (cp == NULL)
8280                                 Panic("Low memory during setting cell parameters");
8281                         mp->cell = cp;
8282                         cmat[0] = cmat[4] = cmat[8] = 1.0;
8283                 } else {
8284                 /*      if (mp->is_xtal_coord)
8285                                 memmove(&cmat, &(cp->tr), sizeof(Transform)); */
8286                 }
8287         /*      mp->is_xtal_coord = 1; */
8288                 /*  alpha, beta, gamma are in degree  */
8289                 cp->cell[0] = a;
8290                 cp->cell[1] = b;
8291                 cp->cell[2] = c;
8292                 cp->cell[3] = alpha;
8293                 cp->cell[4] = beta;
8294                 cp->cell[5] = gamma;
8295                 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
8296                         /*  c unique (hexagonal etc.)  */
8297                         Double cosa, cosb, sinb, cosg;
8298                         cosa = cos(alpha * kDeg2Rad);
8299                         cosb = cos(beta * kDeg2Rad);
8300                         sinb = sin(beta * kDeg2Rad);
8301                         cosg = cos(gamma * kDeg2Rad);
8302                         cp->axes[0].x = a * sinb;
8303                         cp->axes[0].y = 0;
8304                         cp->axes[0].z = a * cosb;
8305                         cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
8306                         cp->axes[1].z = b * cosa;
8307                         cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
8308                         cp->axes[2].x = 0;
8309                         cp->axes[2].y = 0;
8310                         cp->axes[2].z = c;
8311                 } else {
8312                         /*  b unique  */
8313                         Double cosg, sing, cosa, cosb;
8314                         cosa = cos(alpha * kDeg2Rad);
8315                         cosb = cos(beta * kDeg2Rad);
8316                         cosg = cos(gamma * kDeg2Rad);
8317                         sing = sin(gamma * kDeg2Rad);
8318                         cp->axes[0].x = a * sing;
8319                         cp->axes[0].y = a * cosg;
8320                         cp->axes[0].z = 0;
8321                         cp->axes[1].x = 0;
8322                         cp->axes[1].y = b;
8323                         cp->axes[1].z = 0;
8324                         cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
8325                         cp->axes[2].y = c * cosa;
8326                         cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
8327                 }
8328                 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
8329                 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
8330                 MoleculeCalculateCellFromAxes(cp, 0);
8331                 TransformMul(cmat, cp->rtr, cmat);
8332         }
8333         
8334         /*  Update the coordinates (if requested)  */
8335         if (convertCoordinates) {
8336                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8337                         TransformVec(&(ap->r), cmat, &(ap->r));
8338                 }
8339         }
8340         
8341         /*  Update the anisotropic parameters  */
8342         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8343                 Aniso *anp = ap->aniso;
8344                 if (anp != NULL) {
8345                         MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5]);
8346                 }
8347         }
8348         __MoleculeUnlock(mp);
8349         sMoleculeNotifyChangeAppearance(mp);
8350 }
8351
8352 void
8353 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x23, Double x31)
8354 {
8355         Double d, dx;
8356         int u = 0;
8357         const Double log2 = 0.693147180559945;
8358         const Double pi22 = 19.7392088021787;  /* 2*pi**2 */
8359         Transform m1, m2;
8360         Aniso *anp;
8361         XtalCell *cp;
8362         Vector axis[3];
8363         Double val[3];
8364         if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
8365                 return;
8366         anp = mp->atoms[n1].aniso;
8367         __MoleculeLock(mp);
8368         if (anp == NULL) {
8369                 anp = (Aniso *)malloc(sizeof(Aniso));
8370                 if (anp == NULL) {
8371                         __MoleculeUnlock(mp);
8372                         Panic("Low memory during setting anisotropic atom parameters");
8373                 }
8374                 mp->atoms[n1].aniso = anp;
8375         }
8376         switch (type) {
8377                 case 1: d = 1; dx = 0.5; break;
8378                 case 2: d = log2; dx = log2; break;
8379                 case 3: d = log2; dx = log2 * 0.5; break;
8380                 case 4: u = 1; d = 0.25; dx = 0.25; break;
8381                 case 5: u = 1; d = 0.25; dx = 0.125; break;
8382                 case 8: u = 1; d = pi22; dx = pi22; break;
8383                 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
8384                 case 10: d = pi22; dx = pi22; break;
8385                 default: d = dx = 1; break;
8386         }
8387         anp->bij[0] = x11 * d;
8388         anp->bij[1] = x22 * d;
8389         anp->bij[2] = x33 * d;
8390         anp->bij[3] = x12 * dx;
8391         anp->bij[4] = x23 * dx;
8392         anp->bij[5] = x31 * dx;
8393         cp = mp->cell;
8394         if (cp != NULL && u == 1) {
8395                 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
8396                 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
8397                 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
8398                 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
8399                 anp->bij[4] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[3] * kDeg2Rad); */
8400                 anp->bij[5] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[4] * kDeg2Rad); */
8401         }
8402         
8403         /*  Calculate the principal axes (in Cartesian coordinates)  */
8404         /*  The principal axes are the eigenvectors of matrix At(B^-1)A, where
8405                 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
8406                 in which x and z are the crystal-space and cartesian coordinates. */
8407         m1[0] = anp->bij[0] / pi22;
8408         m1[4] = anp->bij[1] / pi22;
8409         m1[8] = anp->bij[2] / pi22;
8410         m1[1] = m1[3] = anp->bij[3] / pi22;
8411         m1[5] = m1[7] = anp->bij[4] / pi22;
8412         m1[2] = m1[6] = anp->bij[5] / pi22;
8413         MatrixInvert(m1, m1);
8414         if (cp != NULL) {
8415                 memmove(m2, cp->rtr, sizeof(Mat33));
8416                 MatrixMul(m1, m1, m2);
8417                 MatrixTranspose(m2, m2);
8418                 MatrixMul(m1, m2, m1);
8419         }
8420         MatrixSymDiagonalize(m1, val, axis);
8421         for (u = 0; u < 3; u++) {
8422                 if (val[u] < 0) {
8423                         fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
8424                         val[u] = 0.001;
8425                 } else {
8426                         val[u] = 1 / sqrt(val[u]);
8427                 }
8428                 anp->pmat[u] = axis[u].x * val[u];
8429                 anp->pmat[u+3] = axis[u].y * val[u];
8430                 anp->pmat[u+6] = axis[u].z * val[u];
8431         }
8432         __MoleculeUnlock(mp);
8433 }
8434
8435 int
8436 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic)
8437 {
8438         static Vector zeroVec = {0, 0, 0};
8439 /*      static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}; */
8440         XtalCell b;
8441         int n;
8442         if (mp == NULL)
8443                 return 0;
8444         if (ax == NULL) {
8445                 if (mp->cell != NULL)
8446                         free(mp->cell);
8447                 mp->cell = NULL;
8448         /*      mp->is_xtal_coord = 0; */
8449                 return 0;
8450         }
8451         b.axes[0] = (ax != NULL ? *ax : zeroVec);
8452         b.axes[1] = (ay != NULL ? *ay : zeroVec);
8453         b.axes[2] = (az != NULL ? *az : zeroVec);
8454         b.origin = *ao;
8455         memmove(b.flags, periodic, 3);
8456         if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
8457                 return -1;
8458         __MoleculeLock(mp);
8459         if (mp->cell != NULL)
8460                 free(mp->cell);
8461         mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
8462         if (mp->cell != NULL) {
8463                 memmove(mp->cell, &b, sizeof(XtalCell));
8464                 n = 0;
8465         } else n = -2;  /*  Out of memory  */
8466         __MoleculeUnlock(mp);
8467         return n;
8468 }
8469
8470 #pragma mark ====== Fragment manipulation ======
8471
8472 static void
8473 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
8474 {
8475         Atom *ap;
8476         int i;
8477         if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
8478                 return;
8479         IntGroupAdd(result, idx, 1);
8480         ap = ATOM_AT_INDEX(mp->atoms, idx);
8481         for (i = 0; i < ap->nconnects; i++) {
8482                 int idx2 = ap->connects[i];
8483                 if (IntGroupLookup(result, idx2, NULL))
8484                         continue;
8485                 sMoleculeFragmentSub(mp, idx2, result, exatoms);
8486         }
8487 }
8488
8489 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
8490     not containing the atoms in exatoms  */
8491 IntGroup *
8492 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
8493 {
8494         IntGroup *result;
8495         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
8496                 return NULL;
8497         result = IntGroupNew();
8498         sMoleculeFragmentSub(mp, n1, result, exatoms);
8499         return result;
8500 }
8501
8502 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
8503     not containing the atoms n2, n3, ... (terminated by -1)  */
8504 IntGroup *
8505 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
8506 {
8507         int i;
8508         IntGroup *exatoms, *result;
8509         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
8510                 return NULL;
8511         exatoms = IntGroupNew();
8512         for (i = 0; i < argc; i++)
8513                 IntGroupAdd(exatoms, argv[i], 1);
8514         result = IntGroupNew();
8515         sMoleculeFragmentSub(mp, n1, result, exatoms);
8516         IntGroupRelease(exatoms);
8517         return result;
8518 }
8519
8520 /*  The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
8521     not containing the atoms in exatoms  */
8522 IntGroup *
8523 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
8524 {
8525         IntGroupIterator iter;
8526         IntGroup *result;
8527         int i;
8528         if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
8529                 return NULL;
8530         IntGroupIteratorInit(inatoms, &iter);
8531         result = IntGroupNew();
8532         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8533                 sMoleculeFragmentSub(mp, i, result, exatoms);
8534         }
8535         IntGroupIteratorRelease(&iter);
8536         return result;
8537 }
8538
8539 /*  Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
8540     group is bound to the rest of the molecule via only one bond.
8541         If the result is true, then the atoms belonging to the (only) bond are returned
8542         in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
8543         and n2 can be NULL, if those informations are not needed.  */
8544 int
8545 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
8546 {
8547         int i, i1, i2, j, k, bond_count, nval1, nval2;
8548         Atom *ap;
8549         if (mp == NULL || mp->natoms == 0 || group == NULL)
8550                 return 0;  /*  Invalid arguments  */
8551         bond_count = 0;
8552         for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
8553                 i2 = IntGroupGetEndPoint(group, i);
8554                 for (j = i1; j < i2; j++) {
8555                         if (j < 0 || j >= mp->natoms)
8556                                 return 0;  /*  Invalid atom group  */
8557                         ap = ATOM_AT_INDEX(mp->atoms, j);
8558                         for (k = 0; k < ap->nconnects; k++) {
8559                                 if (IntGroupLookup(group, ap->connects[k], NULL) == 0) {
8560                                         bond_count++;
8561                                         nval1 = j;
8562                                         nval2 = ap->connects[k];
8563                                         if (bond_count > 1)
8564                                                 return 0;  /*  Too many bonds  */
8565                                 }
8566                         }
8567                 }
8568         }
8569         if (bond_count == 1) {
8570                 if (n1 != NULL)
8571                         *n1 = nval1;
8572                 if (n2 != NULL)
8573                         *n2 = nval2;
8574                 return 1;
8575         } else {
8576                 return 0;
8577         }       
8578 }
8579
8580 /*  Returns non-zero if the given group is 'rotatable' in the molecule. The group
8581     is said to be 'rotatable' when either of the following conditions are met; (1)
8582         the group is detachable, or (2) the group consists of two bonded atoms that define
8583         a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
8584         (either a new IntGroup or 'group' with incremented reference count; thus the caller
8585         is responsible for releasing the returned value).  */
8586 int
8587 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
8588 {
8589         int i1, i2;
8590         if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
8591                 if (rotGroup != NULL) {
8592                         IntGroupRetain(group);
8593                         *rotGroup = group;
8594                 }
8595                 return 1;
8596         }
8597         if (group != NULL && IntGroupGetCount(group) == 2) {
8598                 i1 = IntGroupGetNthPoint(group, 0);
8599                 i2 = IntGroupGetNthPoint(group, 1);
8600                 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
8601                         IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
8602                         if (frag == NULL)
8603                                 return 0;
8604                         i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
8605                         if (i1 == 0) {
8606                                 IntGroupRelease(frag);
8607                                 if (rotGroup != NULL)
8608                                         *rotGroup = NULL;
8609                                 return 0;
8610                         }
8611                         if (rotGroup != NULL)
8612                                 *rotGroup = frag;
8613                         else if (frag != NULL)
8614                                 IntGroupRelease(frag);
8615                         return i1;
8616                 }
8617         }
8618         return 0;
8619 }
8620
8621 #pragma mark ====== Multiple frame ======
8622
8623 int
8624 MoleculeGetNumberOfFrames(Molecule *mp)
8625 {
8626         if (mp == NULL)
8627                 return 0;
8628         if (mp->nframes <= 0) {
8629                 /*  Recalculate  */
8630                 int i, n;
8631                 Atom *ap;
8632                 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8633                         if (ap->nframes > n)
8634                                 n = ap->nframes;
8635                 }
8636                 if (n == 0)
8637                         n = 1;
8638                 mp->nframes = n;
8639         }
8640         return mp->nframes;
8641 }
8642
8643 int
8644 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
8645 {
8646         int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
8647         Vector *tempv, *vp;
8648         Atom *ap;
8649         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
8650                 return -1;
8651
8652         n_old = MoleculeGetNumberOfFrames(mp);
8653         n_new = n_old + count;
8654         last_inserted = IntGroupGetNthPoint(group, count - 1);
8655         if (n_new <= last_inserted) {
8656                 exframes = last_inserted - n_new + 1;  /*  number of extra frames that will be silently inserted  */
8657                 n_new += exframes;
8658         } else exframes = 0;
8659
8660         tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4);  /*  "*4" for handling cells  */
8661         if (tempv == NULL)
8662                 return -1;
8663
8664         __MoleculeLock(mp);
8665
8666         /*  Copy back the current coordinates  */
8667         /*  No change in the current coordinates, but the frame buffer is updated  */
8668         MoleculeSelectFrame(mp, mp->cframe, 1); 
8669         
8670         /*  Expand ap->frames for all atoms  */
8671         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8672                 if (ap->frames == NULL)
8673                         vp = (Vector *)calloc(sizeof(Vector), n_new);
8674                 else
8675                         vp = (Vector *)realloc(ap->frames, sizeof(Vector) * n_new);
8676                 if (vp == NULL) {
8677                         __MoleculeUnlock(mp);
8678                         return -1;
8679                 }
8680                 for (j = ap->nframes; j < n_new; j++)
8681                         vp[j] = ap->r;
8682                 ap->frames = vp;
8683         }
8684         if (mp->cell != NULL && (mp->frame_cells != NULL || inFrameCell != NULL)) {
8685                 if (mp->frame_cells == NULL) {
8686                         vp = (Vector *)calloc(sizeof(Vector), n_new * 4);
8687                         vp[0] = mp->cell->axes[0];
8688                         vp[1] = mp->cell->axes[1];
8689                         vp[2] = mp->cell->axes[2];
8690                         vp[3] = mp->cell->origin;
8691                 } else
8692                         vp = (Vector *)realloc(mp->frame_cells, sizeof(Vector) * 4 * n_new);
8693                 if (vp == NULL) {
8694                         __MoleculeUnlock(mp);
8695                         return -1;
8696                 }
8697                 mp->frame_cells = vp;
8698         }
8699         
8700         /*  group = [n0..n1-1, n2..n3-1, ...]  */
8701         /*  s = t = 0,  */
8702         /*  tempv[0..n0-1] <- ap[0..n0-1], s += n0,
8703             tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
8704                 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
8705                 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
8706                 ...
8707                 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
8708                 At last, s will become n_old and t will become count.  */
8709         for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8710                 int s, t, ns, ne, mult;
8711                 Vector cr;
8712                 ne = s = t = 0;
8713                 if (i == mp->natoms) {
8714                         if (mp->cell == NULL || mp->frame_cells == NULL)
8715                                 break;
8716                         vp = mp->frame_cells;
8717                         mult = 4;
8718                 } else {
8719                         cr = ap->r;
8720                         vp = ap->frames;
8721                         mult = 1;
8722                 }
8723                 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
8724                         if (ns > ne) {
8725                                 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
8726                                 s += ns - ne;
8727                         }
8728                         ne = IntGroupGetEndPoint(group, j);
8729                         while (ns < ne) {
8730                                 if (i == mp->natoms) {
8731                                         if (inFrameCell != NULL) {
8732                                                 tempv[ns * 4] = inFrameCell[t * 4];
8733                                                 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
8734                                                 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
8735                                                 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
8736                                         } else {
8737                                                 tempv[ns * 4] = mp->cell->axes[0];
8738                                                 tempv[ns * 4 + 1] = mp->cell->axes[1];
8739                                                 tempv[ns * 4 + 2] = mp->cell->axes[2];
8740                                                 tempv[ns * 4 + 3] = mp->cell->origin;
8741                                         }
8742                                 } else {
8743                                         if (inFrame != NULL)
8744                                                 tempv[ns] = inFrame[natoms * t + i];
8745                                         else
8746                                                 tempv[ns] = cr;
8747                                 }
8748                                 t++;
8749                                 ns++;
8750                         }
8751                 }
8752                 if (n_new > ne) {
8753                         memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
8754                         s += n_new - ne;
8755                 }
8756                 if (i < mp->natoms)
8757                         ap->nframes = n_new;
8758                 memmove(vp, tempv, sizeof(Vector) * mult * n_new);
8759         }
8760         free(tempv);
8761         mp->nframes = n_new;
8762         MoleculeSelectFrame(mp, last_inserted, 0);
8763         MoleculeIncrementModifyCount(mp);
8764         __MoleculeUnlock(mp);
8765         return count;
8766 }
8767
8768 int
8769 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
8770 {
8771         int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
8772         Vector *tempv, *vp;
8773         Atom *ap;
8774         IntGroup *group, *group2;
8775
8776         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
8777                 return -1;
8778
8779         /*  outFrame[] should have enough size for Vector * natoms * group.count  */
8780         memset(outFrame, 0, sizeof(Vector) * natoms * count);
8781         if (mp->cell != NULL && mp->frame_cells != NULL)
8782                 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
8783
8784         n_old = MoleculeGetNumberOfFrames(mp);
8785         if (n_old == 1)
8786                 return -2;  /*  Cannot delete last frame  */
8787
8788         group = IntGroupNew();
8789         group2 = IntGroupNewWithPoints(0, n_old, -1);
8790         IntGroupIntersect(inGroup, group2, group);
8791         IntGroupRelease(group2);
8792         count = IntGroupGetCount(group);
8793         n_new = n_old - count;
8794         if (n_new < 1) {
8795                 IntGroupRelease(group);
8796                 return -2;  /*  Trying to delete too many frames  */
8797         }
8798         tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4);  /*  "*4" for handling cells  */
8799         if (tempv == NULL) {
8800                 IntGroupRelease(group);
8801                 return -1;
8802         }
8803
8804         __MoleculeLock(mp);
8805
8806         /*  Copy back the current coordinates  */
8807         /*  No change in the current coordinates, but the frame buffer is updated  */
8808         MoleculeSelectFrame(mp, mp->cframe, 1); 
8809
8810         /*  Determine which frame should be selected after removal is completed  */
8811         {
8812                 int n1;
8813                 if (IntGroupLookup(group, mp->cframe, &i)) {
8814                         /*  cframe will be removed  */
8815                         n1 = IntGroupGetStartPoint(group, i) - 1;
8816                         if (n1 < 0)
8817                                 n1 = IntGroupGetEndPoint(group, i);
8818                 } else n1 = mp->cframe;
8819                 /*  Change to that frame  */
8820                 MoleculeSelectFrame(mp, n1, 0);
8821                 group2 = IntGroupNewFromIntGroup(group);
8822                 IntGroupReverse(group2, 0, n_old);
8823                 new_cframe = IntGroupLookupPoint(group2, n1);
8824                 if (new_cframe < 0)
8825                         return -3;  /*  This cannot happen  */
8826                 IntGroupRelease(group2);
8827         }
8828
8829         /*  group = [n0..n1-1, n2..n3-1, ...]  */
8830         /*  s = t = 0, */
8831         /*  tempv[0..n0-1] -> ap[0..n0-1], s += n0,
8832             tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
8833                 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
8834                 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
8835                 ...
8836                 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
8837                 At last, s will become n_new and t will become count.  */
8838         nframes = 0;
8839         for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8840                 int s, t, j, ns, ne;
8841                 int mult;
8842                 /*  if i == mp->natoms, mp->frame_cells is handled  */
8843                 if (i == mp->natoms) {
8844                         if (mp->cell == NULL || mp->frame_cells == NULL)
8845                                 break;
8846                         mult = 4;
8847                         vp = mp->frame_cells;
8848                         old_count = n_old;
8849                 } else {
8850                         mult = 1;
8851                         vp = ap->frames;
8852                         if (vp == NULL) {
8853                                 ap->frames = vp = (Vector *)calloc(sizeof(Vector), n_old);
8854                                 if (vp == NULL) {
8855                                         __MoleculeUnlock(mp);
8856                                         return -1;
8857                                 }
8858                         }
8859                         old_count = ap->nframes;
8860                 }
8861
8862                 /*  Copy vp to tempv  */
8863                 memset(tempv, 0, sizeof(Vector) * mult * n_old);
8864                 memmove(tempv, vp, sizeof(Vector) * mult * (old_count > n_old ? n_old : old_count));
8865                 ne = ns = s = t = 0;
8866                 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
8867                         if (ns > n_old)
8868                                 ns = n_old;
8869                         if (ns > ne) {
8870                                 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (ns - ne));
8871                                 s += ns - ne;
8872                         }
8873                         ne = IntGroupGetEndPoint(group, j);
8874                         if (ne > n_old)
8875                                 ne = n_old;
8876                         while (ns < ne) {
8877                                 if (i < mp->natoms)
8878                                         outFrame[natoms * t + i] = tempv[ns];
8879                                 else if (outFrameCell != NULL) {
8880                                         outFrameCell[i * 4] = tempv[ns * 4];
8881                                         outFrameCell[i * 4 + 1] = tempv[ns * 4 + 1];
8882                                         outFrameCell[i * 4 + 2] = tempv[ns * 4 + 2];
8883                                         outFrameCell[i * 4 + 3] = tempv[ns * 4 + 3];
8884                                 }
8885                                 t++;
8886                                 ns++;
8887                         }
8888                 }
8889                 if (n_old > ne) {
8890                         memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (n_old - ne));
8891                         s += n_old - ne;
8892                 }
8893                 if (i < mp->natoms)
8894                         ap->nframes = s;
8895                 if (nframes < s)
8896                         nframes = s;
8897                 if (s <= 1) {
8898                         if (i < mp->natoms) {
8899                                 free(ap->frames);
8900                                 ap->frames = NULL;
8901                                 ap->nframes = 0;
8902                         } else {
8903                                 free(mp->frame_cells);
8904                                 mp->frame_cells = NULL;
8905                         }
8906                 } else {
8907                         if (i < mp->natoms)
8908                                 ap->frames = (Vector *)realloc(ap->frames, sizeof(Vector) * s);
8909                         else
8910                                 mp->frame_cells = (Vector *)realloc(mp->frame_cells, sizeof(Vector) * 4 * s);
8911                 }
8912         }
8913         free(tempv);
8914         mp->nframes = nframes;
8915         
8916         /*  Select the "last" frame; do not "copy back" the coordinates to the frame table  */
8917 /*      i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
8918         MoleculeSelectFrame(mp, new_cframe, 0);
8919
8920         IntGroupRelease(group);
8921
8922         MoleculeIncrementModifyCount(mp);
8923         __MoleculeUnlock(mp);
8924         return count;
8925 }
8926
8927 int
8928 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
8929 {
8930         int i, cframe, ok;
8931         Atom *ap;
8932         if (mp == NULL || mp->natoms == 0)
8933                 return -1;
8934         cframe = mp->cframe;
8935         ok = 0;
8936         __MoleculeLock(mp);
8937         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8938                 if (copyback && cframe >= 0 && cframe < ap->nframes) {
8939                         /*  Write the current coordinate back to the frame array  */
8940                         ap->frames[cframe] = ap->r;
8941                 }
8942                 if (frame >= 0 && frame < ap->nframes) {
8943                         /*  Read the coordinate from the frame array  */
8944                         ap->r = ap->frames[frame];
8945                         ok = 1;
8946                 }
8947         }
8948
8949         if (mp->cell != NULL && mp->frame_cells != NULL) {
8950                 MoleculeSetPeriodicBox(mp, &mp->frame_cells[frame * 4], &mp->frame_cells[frame * 4 + 1], &mp->frame_cells[frame * 4 + 2], &mp->frame_cells[frame * 4 + 3], mp->cell->flags);
8951         }
8952         mp->needsMDCopyCoordinates = 1;
8953         __MoleculeUnlock(mp);
8954         if (ok) {
8955                 mp->cframe = frame;
8956                 sMoleculeNotifyChangeAppearance(mp);
8957                 return frame;
8958         } else return -1;
8959 }
8960
8961 #pragma mark ====== MO calculation ======
8962
8963 /*  Calculate an MO value for a single point.  */
8964 /*  Index is the MO number (1-based)  */
8965 /*  tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom.  */
8966 static Double
8967 sCalcMOPoint(const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
8968 {
8969         ShellInfo *sp;
8970         PrimInfo *pp;
8971         Double val, tval, *cnp, *tmpp, *mobasep, *mop;
8972         Int i, j;
8973         /*  Cache dr and |dr|^2  */
8974         for (i = 0; i < bset->natoms; i++) {
8975                 Vector r = bset->pos[i];
8976                 tmp[i * 4] = r.x = vp->x - r.x;
8977                 tmp[i * 4 + 1] = r.y = vp->y - r.y;
8978                 tmp[i * 4 + 2] = r.z = vp->z - r.z;
8979                 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
8980         }
8981         /*  Iterate over all shells  */
8982         val = 0.0;
8983         mobasep = bset->mo + (index - 1) * bset->ncomps;
8984         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
8985                 pp = bset->priminfos + sp->p_idx;
8986                 cnp = bset->cns + sp->cn_idx;
8987                 tmpp = tmp + sp->a_idx * 4;
8988                 mop = mobasep + sp->m_idx;
8989                 switch (sp->sym) {
8990                         case kGTOType_S: {
8991                                 tval = 0;
8992                                 for (j = 0; j < sp->nprim; j++) {
8993                                         tval += *cnp++ * exp(-pp->A * tmpp[3]);
8994                                         pp++;
8995                                 }
8996                                 val += mop[0] * tval;
8997                                 break;
8998                         }
8999                         case kGTOType_P: {
9000                                 Double x, y, z;
9001                                 x = y = z = 0;
9002                                 for (j = 0; j < sp->nprim; j++) {
9003                                         tval = exp(-pp->A * tmpp[3]);
9004                                         x += *cnp++ * tval;
9005                                         y += *cnp++ * tval;
9006                                         z += *cnp++ * tval;
9007                                         pp++;
9008                                 }
9009                                 x *= mop[0] * tmpp[0];
9010                                 y *= mop[1] * tmpp[1];
9011                                 z *= mop[2] * tmpp[2];
9012                                 val += x + y + z;
9013                                 break;
9014                         }
9015                         case kGTOType_SP: {
9016                                 Double t, x, y, z;
9017                                 t = x = y = z = 0;
9018                                 for (j = 0; j < sp->nprim; j++) {
9019                                         tval = exp(-pp->A * tmpp[3]);
9020                                         t += *cnp++ * tval;
9021                                         x += *cnp++ * tval;
9022                                         y += *cnp++ * tval;
9023                                         z += *cnp++ * tval;
9024                                         pp++;
9025                                 }
9026                                 t *= mop[0];
9027                                 x *= mop[1] * tmpp[0];
9028                                 y *= mop[2] * tmpp[1];
9029                                 z *= mop[3] * tmpp[2];
9030                                 val += t + x + y + z;
9031                                 break;
9032                         }
9033                         case kGTOType_D: {
9034                                 Double xx, yy, zz, xy, xz, yz;
9035                                 xx = yy = zz = xy = xz = yz = 0;
9036                                 for (j = 0; j < sp->nprim; j++) {
9037                                         tval = exp(-pp->A * tmpp[3]);
9038                                         xx += *cnp++ * tval;
9039                                         yy += *cnp++ * tval;
9040                                         zz += *cnp++ * tval;
9041                                         xy += *cnp++ * tval;
9042                                         xz += *cnp++ * tval;
9043                                         yz += *cnp++ * tval;
9044                                         pp++;
9045                                 }
9046                                 xx *= mop[0] * tmpp[0] * tmpp[0];
9047                                 yy *= mop[1] * tmpp[1] * tmpp[1];
9048                                 zz *= mop[2] * tmpp[2] * tmpp[2];
9049                                 xy *= mop[3] * tmpp[0] * tmpp[1];
9050                                 xz *= mop[4] * tmpp[0] * tmpp[2];
9051                                 yz *= mop[5] * tmpp[1] * tmpp[2];
9052                                 val += xx + yy + zz + xy + xz + yz;
9053                                 break;
9054                         }
9055                         case kGTOType_D5: {
9056                                 Double d0, d1p, d1n, d2p, d2n;
9057                                 d0 = d1p = d1n = d2p = d2n = 0;
9058                                 for (j = 0; j < sp->nprim; j++) {
9059                                         tval = exp(-pp->A * tmpp[3]);
9060                                         d0 += *cnp++ * tval;
9061                                         d1p += *cnp++ * tval;
9062                                         d1n += *cnp++ * tval;
9063                                         d2p += *cnp++ * tval;
9064                                         d2n += *cnp++ * tval;
9065                                         pp++;
9066                                 }
9067                                 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
9068                                 d1p *= mop[1] * tmpp[0] * tmpp[2];
9069                                 d1n *= mop[2] * tmpp[1] * tmpp[2];
9070                                 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
9071                                 d2n *= mop[4] * tmpp[0] * tmpp[1];
9072                                 val += d0 + d1p + d1n + d2p + d2n;
9073                                 break;
9074                         }
9075                 }
9076         }
9077         return val;
9078 }
9079
9080 /*  Calculate one MO. The input vectors should be in bohr unit (angstrom * 1.889725989 = kAngstrom2Bohr).  */
9081 /*  mono is the MO number (1-based)  */
9082 int
9083 MoleculeCalcMO(Molecule *mp, Int mono, const Vector *op, const Vector *dxp, const Vector *dyp, const Vector *dzp, Int nx, Int ny, Int nz, int (*callback)(double progress, void *ref), void *ref)
9084 {
9085         int ix, iy, iz, n, nn;
9086         Cube *cp;
9087         Double *tmp;
9088         if (mp == NULL || mp->bset == NULL)
9089                 return -1;
9090         if (mp->bset->cns == NULL) {
9091                 if (sSetupGaussianCoefficients(mp->bset) != 0)
9092                         return -1;
9093         }
9094         cp = (Cube *)calloc(sizeof(Cube), 1);
9095         if (cp == NULL) {
9096                 return -1;
9097         }
9098         cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
9099         if (cp->dp == NULL) {
9100                 free(cp);
9101                 return -1;
9102         }
9103         cp->idn = mono;
9104         cp->origin = *op;
9105         cp->dx = *dxp;
9106         cp->dy = *dyp;
9107         cp->dz = *dzp;
9108         cp->nx = nx;
9109         cp->ny = ny;
9110         cp->nz = nz;
9111         
9112         /*  TODO: use multithread  */
9113         tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms * 4);
9114         if (tmp == NULL) {
9115                 free(cp->dp);
9116                 free(cp);
9117                 return -1;
9118         }
9119         n = nn = 0;
9120         for (ix = 0; ix < nx; ix++) {
9121                 Vector p;
9122                 for (iy = 0; iy < ny; iy++) {
9123                         for (iz = 0; iz < nz; iz++) {
9124                                 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
9125                                 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
9126                                 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
9127                                 cp->dp[n++] = sCalcMOPoint(mp->bset, mono, &p, tmp);
9128                         }
9129                         if (callback != NULL && n - nn > 100) {
9130                                 nn = n;
9131                                 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
9132                                         free(cp->dp);
9133                                         free(cp);
9134                                         free(tmp);
9135                                         return -2;  /*  User interrupt  */
9136                                 }
9137                         }
9138                 }
9139         }
9140         free(tmp);
9141
9142         AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
9143         return mp->bset->ncubes - 1;
9144 }
9145
9146 int
9147 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
9148 {
9149         int i;
9150         Vector rmin, rmax, *vp;
9151         Double dr, dx, dy, dz;
9152         if (mp == NULL || mp->bset == NULL || mp->bset->natoms == 0)
9153                 return -1;
9154         if (npoints <= 0)
9155                 npoints = 1000000;
9156         rmin.x = rmin.y = rmin.z = 1e10;
9157         rmax.x = rmax.y = rmax.z = -1e10;
9158         for (i = 0, vp = mp->bset->pos; i < mp->bset->natoms; i++, vp++) {
9159                 dr = RadiusForAtomicNumber(ATOM_AT_INDEX(mp->atoms, i)->atomicNumber);
9160                 if (dr == 0.0)
9161                         dr = 1.0;
9162                 dr = dr * kAngstrom2Bohr * 3.0 + 2.0;
9163                 if (rmin.x > vp->x - dr)
9164                         rmin.x = vp->x - dr;
9165                 if (rmin.y > vp->y - dr)
9166                         rmin.y = vp->y - dr;
9167                 if (rmin.z > vp->z - dr)
9168                         rmin.z = vp->z - dr;
9169                 if (rmax.x < vp->x + dr)
9170                         rmax.x = vp->x + dr;
9171                 if (rmax.y < vp->y + dr)
9172                         rmax.y = vp->y + dr;
9173                 if (rmax.z < vp->z + dr)
9174                         rmax.z = vp->z + dr;
9175         }
9176         dx = rmax.x - rmin.x;
9177         dy = rmax.y - rmin.y;
9178         dz = rmax.z - rmin.z;
9179         dr = pow(dx * dy * dz / npoints, 1.0/3.0);
9180         *nx = floor(dx / dr + 0.5);
9181         *ny = floor(dy / dr + 0.5);
9182         *nz = floor(dz / dr + 0.5);
9183         if (*nx == 0)
9184                 *nx = 1;
9185         if (*ny == 0)
9186                 *ny = 1;
9187         if (*nz == 0)
9188                 *nz = 1;
9189         *op = rmin;
9190         xp->x = yp->y = zp->z = dr;
9191         xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
9192         return 0;
9193 }
9194
9195 const Cube *
9196 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
9197 {
9198         if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
9199                 return NULL;
9200         return mp->bset->cubes[index];
9201 }
9202
9203 int
9204 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
9205 {
9206         int i;
9207         if (mp == NULL || mp->bset == NULL)
9208                 return -1;
9209         for (i = 0; i < mp->bset->ncubes; i++) {
9210                 if (mp->bset->cubes[i]->idn == mono)
9211                         return i;
9212         }
9213         return -1;
9214 }
9215
9216 int
9217 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
9218 {
9219         int n;
9220         if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
9221                 return -1;
9222         CubeRelease(mp->bset->cubes[index]);
9223         if (index < n - 1)
9224                 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
9225         if (--(mp->bset->ncubes) == 0) {
9226                 free(mp->bset->cubes);
9227                 mp->bset->cubes = NULL;
9228         }
9229         return mp->bset->ncubes;
9230 }
9231
9232 int
9233 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
9234 {
9235         const Cube *cp;
9236         int i, j, k, n;
9237         FILE *fp;
9238         if (mp == NULL || mp->bset == NULL)
9239                 return -1;  /*  Molecule or the basis set information is empty  */
9240         cp = MoleculeGetCubeAtIndex(mp, index);
9241         if (cp == NULL)
9242                 return -2;  /*  MO not yet calculated  */
9243         fp = fopen(fname, "wb");
9244         if (fp == NULL)
9245                 return -3;  /*  Cannot create file  */
9246
9247         /*  Comment lines  */
9248         fprintf(fp, "%s MO=%d\n", comment, cp->idn);
9249         fprintf(fp, " MO coefficients\n");
9250         
9251         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms), cp->origin.x, cp->origin.y, cp->origin.z);
9252         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx, cp->dx.x, cp->dx.y, cp->dx.z);
9253         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny, cp->dy.x, cp->dy.y, cp->dy.z);
9254         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz, cp->dz.x, cp->dz.y, cp->dz.z);
9255         
9256         /*  Atomic information  */
9257         for (i = 0; i < mp->bset->natoms; i++) {
9258                 Vector *vp = mp->bset->pos + i;
9259                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
9260                 /*  The second number should actually be the effective charge  */
9261                 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber, vp->x, vp->y, vp->z);
9262         }
9263         fprintf(fp, "%5d%5d\n", 1, 1);
9264         
9265         /*  3D data  */
9266         for (i = n = 0; i < cp->nx; i++) {
9267                 for (j = 0; j < cp->ny; j++) {
9268                         for (k = 0; k < cp->nz; k++) {
9269                                 fprintf(fp, " %12.5e", cp->dp[n++]);
9270                                 if (k == cp->nz - 1 || k % 6 == 5)
9271                                         fprintf(fp, "\n");
9272                         }
9273                 }
9274         }
9275         fclose(fp);
9276         return 0;
9277 }