OSDN Git Service

Molecule#expand_by_symmetry now returns an array of atom indices instead of IntGroup.
[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 static Atom *
46 s_AtomDuplicate(Atom *dst, const Atom *src, Int copy_frame)
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 && copy_frame) {
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         if (src->nconnects > ATOM_CONNECTS_LIMIT) {
69                 dst->connects.ptr = NULL;
70                 dst->nconnects = 0;
71                 NewArray(&(dst->connects.ptr), &(dst->nconnects), sizeof(Int), src->nconnects);
72                 memmove(dst->connects.ptr, src->connects.ptr, sizeof(Int) * src->nconnects);
73         }
74         return dst;
75 }
76
77 Atom *
78 AtomDuplicate(Atom *dst, const Atom *src)
79 {
80         return s_AtomDuplicate(dst, src, 1);
81 }
82
83 Atom *
84 AtomDuplicateNoFrame(Atom *dst, const Atom *src)
85 {
86         return s_AtomDuplicate(dst, src, 0);
87 }
88
89 void
90 AtomClean(Atom *ap)
91 {
92         if (ap->aniso != NULL) {
93                 free(ap->aniso);
94                 ap->aniso = NULL;
95         }
96         if (ap->frames != NULL) {
97                 free(ap->frames);
98                 ap->frames = NULL;
99                 ap->nframes = 0;
100         }
101 }
102
103 void
104 CubeRelease(Cube *cp)
105 {
106         if (cp != NULL) {
107                 if (cp->dp != NULL)
108                         free(cp->dp);
109                 free(cp);
110         }
111 }
112
113 void
114 BasisSetRelease(BasisSet *bset)
115 {
116         int i;
117         if (bset == NULL)
118                 return;
119         if (bset->shells != NULL)
120                 free(bset->shells);
121         if (bset->priminfos != NULL)
122                 free(bset->priminfos);
123         if (bset->mo != NULL)
124                 free(bset->mo);
125         if (bset->cns != NULL)
126                 free(bset->cns);
127         if (bset->moenergies != NULL)
128                 free(bset->moenergies);
129         if (bset->scfdensities != NULL)
130                 free(bset->scfdensities);
131         if (bset->pos != NULL)
132                 free(bset->pos);
133         if (bset->nuccharges != NULL)
134                 free(bset->nuccharges);
135         if (bset->cubes != NULL) {
136                 for (i = 0; i < bset->ncubes; i++) {
137                         CubeRelease(bset->cubes[i]);
138                 }
139                 free(bset->cubes);
140         }
141         free(bset);
142 }
143
144 #define ATOM_CONNECTS_PTR(ap) ((ap)->nconnects > ATOM_CONNECTS_LIMIT ? (ap)->connects.ptr : (ap)->connects.data)
145
146 Int *
147 AtomConnects(Atom *ap)
148 {
149         if (ap == NULL)
150                 return NULL;
151         return ATOM_CONNECTS_PTR(ap);
152 }
153
154 Int
155 AtomConnectEntryAtIndex(Atom *ap, Int idx)
156 {
157         if (ap != NULL)
158                 return -1;
159         if (idx < 0 || idx >= ap->nconnects)
160                 return -1;
161         return ATOM_CONNECTS_PTR(ap)[idx];
162 }
163
164 void
165 AtomResizeConnects(Atom *ap, Int nconnects)
166 {
167         Int *p;
168         if (ap == NULL)
169                 return;
170         if (nconnects <= ATOM_CONNECTS_LIMIT) {
171                 if (ap->nconnects > ATOM_CONNECTS_LIMIT) {
172                         p = ap->connects.ptr;
173                         memmove(ap->connects.data, p, sizeof(Int) * nconnects);
174                         free(p);
175                 }
176         } else {
177                 if (ap->nconnects <= ATOM_CONNECTS_LIMIT) {
178                         p = NULL;
179                         ap->nconnects = 0;
180                         NewArray(&p, &(ap->nconnects), sizeof(Int), nconnects);
181                         memmove(p, ap->connects.data, sizeof(Int) * ap->nconnects);
182                         ap->connects.ptr = p;
183                 } else if (ap->nconnects < nconnects) {
184                         /*  Reallocate  */
185                         AssignArray(&(ap->connects.ptr), &(ap->nconnects), sizeof(Int), nconnects - 1, NULL);
186                 }
187         }
188         ap->nconnects = nconnects;
189 }
190
191 void
192 AtomSetConnectEntry(Atom *ap, Int idx, Int connect)
193 {
194         if (ap == NULL)
195                 return;
196         if (idx >= ap->nconnects) {
197                 /*  Insert a new entry at the last  */
198                 AtomResizeConnects(ap, idx + 1);
199         }
200         ATOM_CONNECTS_PTR(ap)[idx] = connect;
201 }
202
203 void
204 AtomInsertConnectEntry(Atom *ap, Int idx, Int connect)
205 {
206         Int n, *p;
207         if (ap == NULL)
208                 return;
209         if (idx < 0 || idx >= ap->nconnects)
210                 idx = ap->nconnects;
211         AtomResizeConnects(ap, ap->nconnects + 1);
212         n = ap->nconnects - idx - 1;  /*  Number of entries to be moved towards the bottom  */
213         p = ATOM_CONNECTS_PTR(ap);
214         if (n > 0) {
215                 memmove(p + idx + 1, p + idx, sizeof(Int) * n);
216         }
217         p[idx] = connect;
218 }
219
220 void
221 AtomDeleteConnectEntry(Atom *ap, Int idx)
222 {
223         Int n, *p;
224         if (ap == NULL)
225                 return;
226         if (idx < 0 || idx >= ap->nconnects)
227                 return;
228         n = ap->nconnects - idx - 1;  /*  Number of entries to be moved towards the top  */
229         p = ATOM_CONNECTS_PTR(ap);
230         if (n > 0) {
231                 memmove(p + idx, p + idx + 1, sizeof(Int) * n);
232         }
233         AtomResizeConnects(ap, ap->nconnects - 1);
234 }
235
236 #pragma mark ====== Accessor types ======
237
238 MolEnumerable *
239 MolEnumerableNew(Molecule *mol, int kind)
240 {
241         MolEnumerable *mseq = (MolEnumerable *)calloc(sizeof(MolEnumerable), 1);
242         if (mseq != NULL) {
243                 mseq->mol = MoleculeRetain(mol);
244                 mseq->kind = kind;
245         }
246         return mseq;
247 }
248
249 void
250 MolEnumerableRelease(MolEnumerable *mseq)
251 {
252         if (mseq != NULL) {
253                 MoleculeRelease(mseq->mol);
254                 free(mseq);
255         }
256 }
257
258 AtomRef *
259 AtomRefNew(Molecule *mol, int idx)
260 {
261         AtomRef *aref = (AtomRef *)calloc(sizeof(AtomRef), 1);
262         if (aref != NULL) {
263                 aref->mol = MoleculeRetain(mol);
264                 aref->idx = idx;
265         }
266         return aref;
267 }
268
269 void
270 AtomRefRelease(AtomRef *aref)
271 {
272         if (aref != NULL) {
273                 MoleculeRelease(aref->mol);
274                 free(aref);
275         }
276 }
277
278 #pragma mark ====== Creation of molecules ======
279
280 Molecule *
281 MoleculeNew(void)
282 {
283         char name[40];
284         Molecule *mp = (Molecule *)calloc(sizeof(Molecule), 1);
285         if (mp == NULL)
286                 Panic("Cannot allocate new molecule record");
287         snprintf(name, sizeof name, "Untitled %d", sMoleculeUntitledCount++);
288         ObjectInit((Object *)mp, (Object **)&sMoleculeRoot, name);
289         return mp;
290 }
291
292 Molecule *
293 MoleculeNewWithName(const char *name)
294 {
295         Molecule *mp = MoleculeNew();
296         MoleculeSetName(mp, name);
297         return mp;
298 }
299
300 Molecule *
301 MoleculeInitWithAtoms(Molecule *mp, const Atom *atoms, int natoms)
302 {
303         int i;
304         if (mp == NULL)
305                 mp = MoleculeNew();
306         if (natoms == 0)
307                 return mp;
308         if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
309                 Panic("Cannot allocate memory for atoms");
310         for (i = 0; i < natoms; i++)
311                 AtomDuplicate(mp->atoms + i, atoms + i);
312         mp->nframes = -1;  /*  Should be recalculated later  */
313         return mp;
314 }
315
316 Molecule *
317 MoleculeInitWithMolecule(Molecule *mp2, const Molecule *mp)
318 {
319         MoleculeInitWithAtoms(mp2, mp->atoms, mp->natoms);
320         if (mp->nbonds > 0) {
321                 if (NewArray(&mp2->bonds, &mp2->nbonds, sizeof(Int)*2, mp->nbonds) == NULL)
322                         goto error;
323                 memmove(mp2->bonds, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
324         }
325         if (mp->nangles > 0) {
326                 if (NewArray(&mp2->angles, &mp2->nangles, sizeof(Int)*3, mp->nangles) == NULL)
327                         goto error;
328                 memmove(mp2->angles, mp->angles, sizeof(Int) * 3 * mp->nangles);
329         }
330         if (mp->ndihedrals > 0) {
331                 if (NewArray(&mp2->dihedrals, &mp2->ndihedrals, sizeof(Int)*4, mp->ndihedrals) == NULL)
332                         goto error;
333                 memmove(mp2->dihedrals, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
334         }
335         if (mp->nimpropers > 0) {
336                 if (NewArray(&mp2->impropers, &mp2->nimpropers, sizeof(Int)*4, mp->nimpropers) == NULL)
337                         goto error;
338                 memmove(mp2->impropers, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
339         }
340         if (mp->nresidues > 0) {
341                 if (NewArray(&mp2->residues, &mp2->nresidues, sizeof(mp->residues[0]), mp->nresidues) == NULL)
342                         goto error;
343                 memmove(mp2->residues, mp->residues, sizeof(mp->residues[0]) * mp->nresidues);
344         }
345         if (mp->cell != NULL) {
346                 mp2->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
347                 memmove(mp2->cell, mp->cell, sizeof(XtalCell));
348         }
349         if (mp->nsyms > 0) {
350                 mp2->nsyms = mp->nsyms;
351                 mp2->syms = (Transform *)calloc(sizeof(Transform), mp2->nsyms);
352                 memmove(mp2->syms, mp->syms, sizeof(Transform) * mp2->nsyms);
353         }
354         if (mp->par != NULL)
355                 mp2->par = ParameterDuplicate(mp->par);
356         if (mp->arena != NULL) {
357                 md_arena_new(mp2);
358                 md_arena_init_from_arena(mp2->arena, mp->arena);
359         }
360         
361         return mp2;
362   error:
363         Panic("Cannot allocate memory for duplicate molecule");
364         return NULL;  /*  Not reached  */
365 }
366
367 /*  Assign a unique name to this parameter record  */
368 void
369 MoleculeSetName(Molecule *mp, const char *name)
370 {
371         ObjectSetName((Object *)mp, name, (Object *)sMoleculeRoot);
372 }
373
374 const char *
375 MoleculeGetName(Molecule *mp)
376 {
377         return ObjectGetName((Object *)mp);
378 }
379
380 Molecule *
381 MoleculeWithName(const char *name)
382 {
383         return (Molecule *)ObjectWithName(name, (Object *)sMoleculeRoot);
384 }
385
386 void
387 MoleculeSetPath(Molecule *mol, const char *fname)
388 {
389         char *buf, *cwd;
390         if (mol == NULL || fname == NULL)
391                 return;
392         if (fname[0] == '/' || (isalpha(fname[0]) && fname[1] == ':')) {
393                 /*  Full path  */
394                 buf = strdup(fname);
395         } else {
396                 cwd = getcwd(NULL, 0);
397                 asprintf(&buf, "%s/%s", cwd, fname);
398                 free(cwd);
399         }
400         if (mol->path != NULL) {
401                 if (strcmp(mol->path, buf) == 0) {
402                         /*  No change  */
403                         free(buf);
404                         return;
405                 }
406                 free((void *)(mol->path));
407         }
408         mol->path = buf;
409         if (mol->arena != NULL) {
410                 md_close_output_files(mol->arena);
411         }
412 }
413
414 const char *
415 MoleculeGetPath(Molecule *mol)
416 {
417         if (mol == NULL)
418                 return NULL;
419         return mol->path;
420 }
421
422 Molecule *
423 MoleculeRetain(Molecule *mp)
424 {
425         ObjectIncrRefCount((Object *)mp);
426         return mp;
427 }
428
429 void
430 MoleculeClear(Molecule *mp)
431 {
432         if (mp == NULL)
433                 return;
434         if (mp->arena != NULL) {
435                 md_arena_set_molecule(mp->arena, NULL);
436                 mp->arena = NULL;
437         }
438         if (mp->par != NULL) {
439                 ParameterRelease(mp->par);
440                 mp->par = NULL;
441         }
442         if (mp->bset != NULL) {
443                 BasisSetRelease(mp->bset);
444                 mp->bset = NULL;
445         }
446         if (mp->atoms != NULL) {
447                 int i;
448                 for (i = 0; i < mp->natoms; i++)
449                         AtomClean(mp->atoms + i);
450                 free(mp->atoms);
451                 mp->atoms = NULL;
452                 mp->natoms = 0;
453         }
454         if (mp->bonds != NULL) {
455                 free(mp->bonds);
456                 mp->bonds = NULL;
457                 mp->nbonds = 0;
458         }
459         if (mp->angles != NULL) {
460                 free(mp->angles);
461                 mp->angles = NULL;
462                 mp->nangles = 0;
463         }
464         if (mp->dihedrals != NULL) {
465                 free(mp->dihedrals);
466                 mp->dihedrals = NULL;
467                 mp->ndihedrals = 0;
468         }
469         if (mp->impropers != NULL) {
470                 free(mp->impropers);
471                 mp->impropers = NULL;
472                 mp->nimpropers = 0;
473         }
474         if (mp->residues != NULL) {
475                 free(mp->residues);
476                 mp->residues = NULL;
477                 mp->nresidues = 0;
478         }
479         if (mp->elpots != NULL) {
480                 free(mp->elpots);
481                 mp->elpots = NULL;
482                 mp->nelpots = 0;
483         }
484         if (mp->path != NULL) {
485                 free((void *)mp->path);
486                 mp->path = NULL;
487         }
488 }
489
490 void
491 MoleculeRelease(Molecule *mp)
492 {
493         if (mp == NULL)
494                 return;
495         if (mp->exmolobj != NULL)
496                 MoleculeReleaseExternalHook(mp);
497         if (ObjectDecrRefCount((Object *)mp) == 0) {
498                 MoleculeClear(mp);
499                 ObjectDealloc((Object *)mp, (Object **)&sMoleculeRoot);
500         }
501 }
502
503 void
504 MoleculeExchange(Molecule *mp1, Molecule *mp2)
505 {
506         Molecule mp_temp;
507         struct MainView *mview1, *mview2;
508         struct MDArena *arena1, *arena2;
509         /*  mview and arena must be kept as they are  */
510         mview1 = mp1->mview;
511         mview2 = mp2->mview;
512         arena1 = mp1->arena;
513         arena2 = mp2->arena;
514         /*  'natoms' is the first member to be copied  */
515         int ofs = offsetof(Molecule, natoms);
516         memmove((char *)(&mp_temp) + ofs, (char *)mp1 + ofs, sizeof(Molecule) - ofs);
517         memmove((char *)mp1 + ofs, (char *)mp2 + ofs, sizeof(Molecule) - ofs);
518         memmove((char *)mp2 + ofs, (char *)(&mp_temp) + ofs, sizeof(Molecule) - ofs);
519         mp1->arena = arena1;
520         mp2->arena = arena2;
521         mp1->mview = mview1;
522         mp2->mview = mview2;
523 /*      if (mp1->arena != NULL && mp1->arena->mol == mp2)
524                 mp1->arena->mol = mp1;
525         if (mp1->arena != NULL && mp1->arena->xmol == mp2)
526                 mp1->arena->xmol = mp1;
527         if (mp2->arena != NULL && mp2->arena->mol == mp1)
528                 mp2->arena->mol = mp2;
529         if (mp2->arena != NULL && mp2->arena->xmol == mp1)
530                 mp2->arena->xmol = mp2; */
531 }
532
533 #pragma mark ====== Mutex ======
534
535 void
536 MoleculeLock(Molecule *mol)
537 {
538         if (mol == NULL || mol->mutex == NULL)
539                 return;
540         MoleculeCallback_lockMutex(mol->mutex);
541 }
542
543 void
544 MoleculeUnlock(Molecule *mol)
545 {
546         if (mol == NULL || mol->mutex == NULL)
547                 return;
548         MoleculeCallback_unlockMutex(mol->mutex);
549 }
550
551 #pragma mark ====== Modify count ======
552
553 void
554 MoleculeIncrementModifyCount(Molecule *mp)
555 {
556         if (mp != NULL) {
557                 if (++(mp->modifyCount) == 1)
558                         MoleculeCallback_notifyModification(mp, 0);
559         /*      fprintf(stderr, "MoleculeIncrementModifyCount: %d\n", mp->modifyCount); */
560         }
561 }
562
563 void
564 MoleculeClearModifyCount(Molecule *mp)
565 {
566         if (mp != NULL) {
567                 mp->modifyCount = 0;
568         /*      fprintf(stderr, "MoleculeClearModifyCount: %d\n", mp->modifyCount); */
569         }
570 }
571
572 #pragma mark ====== File handling functions ======
573
574 static const char *
575 guessMoleculeType(const char *fname)
576 {
577         char buf[1024], *p;
578         FILE *fp;
579         const char *retval = NULL;
580         fp = fopen(fname, "rb");
581         if (fp != NULL) {
582                 memset(buf, 0, sizeof buf);
583                 if (fread(buf, 1, sizeof buf - 1, fp) > 0) {
584                         if (strncmp(buf, "PSF", 3) == 0)
585                                 retval = "psf";
586                         else if (((p = strstr(buf, "ATOM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r'))
587                         || ((p = strstr(buf, "HETATM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r')))
588                                 retval = "pdb";
589                         else
590                                 retval = "???";  /*  unknown  */
591                 }
592                 fclose(fp);
593         }
594         return retval;
595 }
596
597 static int
598 guessElement(Atom *ap)
599 {
600         int atomicNumber = -1;
601         if (ap->atomicNumber > 0)
602                 atomicNumber = ap->atomicNumber;
603         else {
604                 atomicNumber = GuessAtomicNumber(ap->element, ap->weight);
605                 if (atomicNumber <= 0 && ap->aname[0] != 0)
606                         atomicNumber = GuessAtomicNumber(ap->aname, ap->weight);
607         }
608         if (atomicNumber >= 0) {
609                 ap->atomicNumber = atomicNumber;
610                 if (ap->weight <= 0)
611                         ap->weight = WeightForAtomicNumber(atomicNumber);
612                 if (ap->element[0] == 0)
613                         ElementToString(atomicNumber, ap->element);
614         }
615         return atomicNumber;
616 }
617
618 static int
619 sReadLineWithInterrupt(char *buf, int size, FILE *stream, int *lineNumber)
620 {
621         static int lastLineNumber = 0;
622         if (lineNumber != NULL) {
623                 if (*lineNumber == 0)
624                         lastLineNumber = 0;
625                 else if (*lineNumber >= lastLineNumber + 1000) {
626                         if (MyAppCallback_checkInterrupt() != 0)
627                                 return -1;  /*  User interrupt  */
628                         lastLineNumber = *lineNumber;
629                 }
630         }
631         return ReadLine(buf, size, stream, lineNumber);
632 }
633
634 int
635 MoleculeLoadFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
636 {
637         int retval;
638         if (ftype == NULL || *ftype == 0) {
639                 const char *cp;
640                 cp = strrchr(fname, '.');
641                 if (cp != NULL)
642                         ftype = cp + 1;
643                 else {
644                         cp = guessMoleculeType(fname);
645                         if (strcmp(cp, "???") != 0)
646                                 ftype = cp;
647                 }
648         }
649         if (strcasecmp(ftype, "psf") == 0) {
650                 retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
651         } else if (strcasecmp(ftype, "pdb") == 0) {
652                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
653         } else if (strcasecmp(ftype, "tep") == 0) {
654                 retval = MoleculeLoadTepFile(mp, fname, errbuf, errbufsize);
655         } else if (strcasecmp(ftype, "res") == 0 || strcasecmp(ftype, "ins") == 0) {
656                 retval = MoleculeLoadShelxFile(mp, fname, errbuf, errbufsize);
657         } else if (strcasecmp(ftype, "fchk") == 0 || strcasecmp(ftype, "fch") == 0) {
658                 retval = MoleculeLoadGaussianFchkFile(mp, fname, errbuf, errbufsize);
659         } else {
660                 snprintf(errbuf, errbufsize, "Unknown format %s", ftype);
661                 return 1;
662         }
663 /*      if (retval != 0) {
664                 retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
665         } */
666         if (retval == 0)
667                 MoleculeSetPath(mp, fname);
668         return retval;
669 }
670
671 int
672 MoleculeLoadMbsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
673 {
674         FILE *fp;
675         char buf[1024];
676         int i, j, k, n, err, fn, nframes;
677         int lineNumber;
678         int ibuf[12];
679         Int iibuf[4];
680         double dbuf[12];
681         int mview_ibuf[16];
682         float mview_fbuf[8];
683         char cbuf[12][8];
684         const char **pp;
685         char *bufp, *valp, *comp;
686         Int *ip;
687         Double *dp;
688         Vector v;
689         Atom *ap;
690         const int kUndefined = -10000000;
691         err = 0;
692         if (errbuf == NULL) {
693                 errbuf = buf;
694                 errbufsize = 1024;
695         }
696         errbuf[0] = 0;
697         if (mp->natoms != 0 || mp->par != NULL || mp->arena != NULL) {
698                 snprintf(errbuf, errbufsize, "The molecule must be empty");
699                 return 1;
700         }
701         fp = fopen(fname, "rb");
702         if (fp == NULL) {
703                 snprintf(errbuf, errbufsize, "Cannot open file");
704                 return 1;
705         }
706         for (i = 0; i < 8; i++)
707                 mview_fbuf[i] = kUndefined;
708         for (i = 0; i < 16; i++)
709                 mview_ibuf[i] = kUndefined;
710         /*      flockfile(fp); */
711         lineNumber = 0;
712         fn = 0;
713         nframes = 0;
714         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
715                 if (strncmp(buf, "!:", 2) != 0)
716                         continue;   /*  Skip until section header is found  */
717                 bufp = buf;
718                 strsep(&bufp, " \t\n");
719                 if (strcmp(buf, "!:atoms") == 0) {
720                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
721                                 if (buf[0] == '!')
722                                         continue;
723                                 if (buf[0] == '\n')
724                                         break;
725                                 /* idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge */
726                                 if (sscanf(buf, "%d %6s %d %6s %6s %6s %lf %lf %6s %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) {
727                                         snprintf(errbuf, errbufsize, "line %d: coordinates cannot be read for atom %d", lineNumber, mp->natoms + 1);
728                                         goto exit;
729                                 }
730                                 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
731                                 strncpy(ap->segName, cbuf[0], 4);
732                                 ap->resSeq = ibuf[1];
733                                 strncpy(ap->resName, cbuf[1], 4);
734                                 strncpy(ap->aname, cbuf[2], 4);
735                                 ap->type = AtomTypeEncodeToUInt(cbuf[3]);
736                                 ap->charge = dbuf[0];
737                                 ap->weight = dbuf[1];
738                                 strncpy(ap->element, cbuf[4], 2);
739                                 ap->atomicNumber = ibuf[2];
740                                 ap->occupancy = dbuf[2];
741                                 ap->tempFactor = dbuf[3];
742                                 ap->intCharge = ibuf[3];
743                         }
744                         continue;
745                 } else if (strcmp(buf, "!:atoms_symop") == 0) {
746                         i = 0;
747                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
748                                 if (buf[0] == '!')
749                                         continue;
750                                 if (buf[0] == '\n')
751                                         break;
752                                 /* idx symop symbase */
753                                 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
754                                         snprintf(errbuf, errbufsize, "line %d: symmetry operations cannot be read for atom %d", lineNumber, i + 1);
755                                         goto exit;
756                                 }
757                                 if (i >= mp->natoms) {
758                                         snprintf(errbuf, errbufsize, "line %d: too many atomic symmetry info\n", lineNumber);
759                                         goto exit;
760                                 }
761                                 ap = ATOM_AT_INDEX(mp->atoms, i);
762                                 ap->symop.sym = ibuf[1] / 1000000;
763                                 ap->symop.dx = (ibuf[1] % 1000000) / 10000;
764                                 ap->symop.dy = (ibuf[1] % 10000) / 100;
765                                 ap->symop.dz = ibuf[1] % 100;
766                                 ap->symbase = ibuf[2];
767                                 i++;
768                         }
769                         continue;
770                 } else if (strcmp(buf, "!:atoms_fix") == 0) {
771                         i = 0;
772                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
773                                 if (buf[0] == '!')
774                                         continue;
775                                 if (buf[0] == '\n')
776                                         break;
777                                 /* idx fix_force fix_pos */
778                                 if (sscanf(buf, "%d %lf %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]) < 5) {
779                                         snprintf(errbuf, errbufsize, "line %d: fix atom info cannot be read for atom %d", lineNumber, i + 1);
780                                         goto exit;
781                                 }
782                                 if (i >= mp->natoms) {
783                                         snprintf(errbuf, errbufsize, "line %d: too many fix atom info\n", lineNumber);
784                                         goto exit;
785                                 }
786                                 ap = ATOM_AT_INDEX(mp->atoms, i);
787                                 ap->fix_force = dbuf[0];
788                                 ap->fix_pos.x = dbuf[1];
789                                 ap->fix_pos.y = dbuf[2];
790                                 ap->fix_pos.z = dbuf[3];
791                                 i++;
792                         }
793                         continue;
794                 } else if (strcmp(buf, "!:mm_exclude") == 0) {
795                         i = 0;
796                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
797                                 if (buf[0] == '!')
798                                         continue;
799                                 if (buf[0] == '\n')
800                                         break;
801                                 /* idx mm_exclude periodic_exclude */
802                                 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
803                                         snprintf(errbuf, errbufsize, "line %d: mm_exclude flags cannot be read for atom %d", lineNumber, i + 1);
804                                         goto exit;
805                                 }
806                                 if (i >= mp->natoms) {
807                                         snprintf(errbuf, errbufsize, "line %d: too many mm_exclude flags\n", lineNumber);
808                                         goto exit;
809                                 }
810                                 ap = ATOM_AT_INDEX(mp->atoms, i);
811                                 ap->mm_exclude = (ibuf[1] != 0);
812                                 ap->periodic_exclude = (ibuf[2] != 0);
813                                 i++;
814                         }
815                         continue;
816                 } else if (strcmp(buf, "!:positions") == 0) {
817                         i = 0;
818                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
819                                 if (buf[0] == '!')
820                                         continue;
821                                 if (buf[0] == '\n')
822                                         break;
823                                 /* idx x y z */
824                                 if ((j = sscanf(buf, "%d %lf %lf %lf %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5])) < 4) {
825                                         snprintf(errbuf, errbufsize, "line %d: atom position cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
826                                         goto exit;
827                                 }
828                                 if (j > 4 && nframes != 0) {
829                                         snprintf(errbuf, errbufsize, "line %d: atom position sigma can only be given for frame 0", lineNumber);
830                                         goto exit;
831                                 }
832                                 if (j > 4 && j != 7) {
833                                         snprintf(errbuf, errbufsize, "line %d: atom position sigma cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
834                                         goto exit;
835                                 }
836                                 if (i >= mp->natoms) {
837                                         snprintf(errbuf, errbufsize, "line %d: too many atom position records\n", lineNumber);
838                                         goto exit;
839                                 }
840                                 v.x = dbuf[0];
841                                 v.y = dbuf[1];
842                                 v.z = dbuf[2];
843                                 ap = ATOM_AT_INDEX(mp->atoms, i);
844                                 if (nframes > 0) {
845                                         AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes, &v);
846                                         if (nframes == 1)
847                                                 ap->frames[0] = ap->r;
848                                 }
849                                 ap->r = v;
850                                 if (j == 7) {
851                                         ap->sigma.x = dbuf[3];
852                                         ap->sigma.y = dbuf[4];
853                                         ap->sigma.z = dbuf[5];
854                                 }
855                                 i++;
856                         }
857                         nframes++;
858                         if (nframes >= 2) {
859                                 mp->nframes = nframes;
860                                 mp->cframe = nframes - 1;
861                         } else {
862                                 mp->nframes = mp->cframe = 0;
863                         }
864                         continue;
865                 } else if (strcmp(buf, "!:bonds") == 0) {
866                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
867                                 if (buf[0] == '!')
868                                         continue;
869                                 if (buf[0] == '\n')
870                                         break;
871                                 /* from1 to1 from2 to2 from3 to3 from4 to4 */ 
872                                 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]);
873                                 if (i < 2 || i % 2 != 0) {
874                                         snprintf(errbuf, errbufsize, "line %d: bad bond format", lineNumber);
875                                         goto exit;
876                                 }
877                                 for (j = 0; j < i; j += 2) {
878                                         iibuf[0] = ibuf[j];
879                                         iibuf[1] = ibuf[j + 1];
880                                         if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[0] == iibuf[1]) {
881                                                 snprintf(errbuf, errbufsize, "line %d: bad bond format", lineNumber);
882                                                 goto exit;
883                                         }
884                                         AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, iibuf);
885                                         for (k = 0; k < 2; k++) {
886                                                 const Int *cp;
887                                                 ap = ATOM_AT_INDEX(mp->atoms, iibuf[k]);
888                                                 cp = AtomConnects(ap);
889                                                 for (n = 0; n < ap->nconnects; n++, cp++) {
890                                                         if (*cp == iibuf[1 - k])
891                                                                 break;
892                                                 }
893                                                 if (n >= ap->nconnects) {
894                                                 /*      if (ap->nconnects >= ATOMS_MAX_CONNECTS - 1) {
895                                                                 snprintf(errbuf, errbufsize, "line %d: too many bonds on atom %d", lineNumber, iibuf[k]);
896                                                                 goto exit;
897                                                         } */
898                                                         AtomInsertConnectEntry(ap, ap->nconnects, iibuf[1 - k]);
899                                                 }
900                                         }
901                                 }
902                         }
903                         continue;
904                 } else if (strcmp(buf, "!:angles") == 0) {
905                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
906                                 if (buf[0] == '!')
907                                         continue;
908                                 if (buf[0] == '\n')
909                                         break;
910                                 /* a1 b1 c1 a2 b2 c2 a3 b3 c3 */ 
911                                 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]);
912                                 if (i == 0 || i % 3 != 0) {
913                                         snprintf(errbuf, errbufsize, "line %d: bad angle format", lineNumber);
914                                         goto exit;
915                                 }
916                                 for (j = 0; j < i; j += 3) {
917                                         iibuf[0] = ibuf[j];
918                                         iibuf[1] = ibuf[j + 1];
919                                         iibuf[2] = ibuf[j + 2];
920                                         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]) {
921                                                 snprintf(errbuf, errbufsize, "line %d: bad angle format", lineNumber);
922                                                 goto exit;
923                                         }
924                                         AssignArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, mp->nangles, iibuf);
925                                 }
926                         }
927                         continue;
928                 } else if (strcmp(buf, "!:dihedrals") == 0) {
929                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
930                                 if (buf[0] == '!')
931                                         continue;
932                                 if (buf[0] == '\n')
933                                         break;
934                                 /* a1 b1 c1 d1 a2 b2 c2 d2 */ 
935                                 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]);
936                                 if (i == 0 || i % 4 != 0) {
937                                         snprintf(errbuf, errbufsize, "line %d: bad dihedral format", lineNumber);
938                                         goto exit;
939                                 }
940                                 for (j = 0; j < i; j += 4) {
941                                         iibuf[0] = ibuf[j];
942                                         iibuf[1] = ibuf[j + 1];
943                                         iibuf[2] = ibuf[j + 2];
944                                         iibuf[3] = ibuf[j + 3];
945                                         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]) {
946                                                 snprintf(errbuf, errbufsize, "line %d: bad dihedral format", lineNumber);
947                                                 goto exit;
948                                         }
949                                         AssignArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, mp->ndihedrals, iibuf);
950                                 }
951                         }
952                         continue;
953                 } else if (strcmp(buf, "!:impropers") == 0) {
954                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
955                                 if (buf[0] == '!')
956                                         continue;
957                                 if (buf[0] == '\n')
958                                         break;
959                                 /* a1 b1 c1 d1 a2 b2 c2 d2 */ 
960                                 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]);
961                                 if (i == 0 || i % 4 != 0) {
962                                         snprintf(errbuf, errbufsize, "line %d: bad improper format", lineNumber);
963                                         goto exit;
964                                 }
965                                 for (j = 0; j < i; j += 4) {
966                                         iibuf[0] = ibuf[j];
967                                         iibuf[1] = ibuf[j + 1];
968                                         iibuf[2] = ibuf[j + 2];
969                                         iibuf[3] = ibuf[j + 3];
970                                         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]) {
971                                                 snprintf(errbuf, errbufsize, "line %d: bad improper format", lineNumber);
972                                                 goto exit;
973                                         }
974                                         AssignArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, mp->nimpropers, iibuf);
975                                 }
976                         }
977                         continue;
978                 } else if (strcmp(buf, "!:xtalcell") == 0 && mp->cell == NULL) {
979                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
980                                 if (buf[0] == '!')
981                                         continue;
982                                 if (buf[0] == '\n')
983                                         break;
984                                 /* a b c alpha beta gamma [sigmaflag] */ 
985                                 if ((j = sscanf(buf, "%lf %lf %lf %lf %lf %lf %d", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5], &ibuf[0])) < 6) {
986                                         snprintf(errbuf, errbufsize, "line %d: bad xtalcell format", lineNumber);
987                                         goto exit;
988                                 }
989                                 MoleculeSetCell(mp, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], 0);
990                                 if (j == 7 && ibuf[0] != 0) {
991                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
992                                                 snprintf(errbuf, errbufsize, "line %d: sigma for xtalcell are missing", lineNumber);
993                                                 goto exit;
994                                         }
995                                         if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
996                                                 snprintf(errbuf, errbufsize, "line %d: bad xtalcell sigma format", lineNumber);
997                                                 goto exit;
998                                         }
999                                         if (mp->cell != NULL) {
1000                                                 mp->cell->has_sigma = 1;
1001                                                 for (i = 0; i < 6; i++) {
1002                                                         mp->cell->cellsigma[i] = dbuf[i];
1003                                                 }
1004                                         } else {
1005                                                 snprintf(errbuf, errbufsize, "line %d: cell sigma are given while cell is not given", lineNumber);
1006                                         }
1007                                 }
1008                         }
1009                         continue;
1010                 } else if (strcmp(buf, "!:symmetry_operations") == 0) {
1011                         i = 0;
1012                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1013                                 Transform tr;
1014                                 if (buf[0] == '!')
1015                                         continue;
1016                                 if (buf[0] == '\n')
1017                                         break;
1018                                 /* a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3 */
1019                                 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1020                                         snprintf(errbuf, errbufsize, "line %d: bad symmetry_operation format", lineNumber);
1021                                         goto exit;
1022                                 }
1023                                 if (i < 3) {
1024                                         tr[i] = dbuf[0];
1025                                         tr[i + 3] = dbuf[1];
1026                                         tr[i + 6] = dbuf[2];
1027                                 } else {
1028                                         tr[9] = dbuf[0];
1029                                         tr[10] = dbuf[1];
1030                                         tr[11] = dbuf[2];
1031                                 }
1032                                 i++;
1033                                 if (i == 4) {
1034                                         AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1035                                         i = 0;
1036                                 }
1037                         }
1038                         continue;
1039                 } else if (strcmp(buf, "!:anisotropic_thermal_parameters") == 0) {
1040                         i = 0;
1041                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1042                                 if (buf[0] == '!')
1043                                         continue;
1044                                 if (buf[0] == '\n')
1045                                         break;
1046                                 /* b11 b22 b33 b12 b13 b23 [has_sigma] */
1047                                 if ((j = sscanf(buf, "%lf %lf %lf %lf %lf %lf %d", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5], &ibuf[0])) < 6) {
1048                                         snprintf(errbuf, errbufsize, "line %d: anisotropic thermal parameters cannot be read for atom %d", lineNumber, i + 1);
1049                                         goto exit;
1050                                 }
1051                                 if (i >= mp->natoms) {
1052                                         snprintf(errbuf, errbufsize, "line %d: too many anisotropic thermal parameters\n", lineNumber);
1053                                         goto exit;
1054                                 }
1055                                 if (dbuf[0] == 0.0 && dbuf[1] == 0.0 && dbuf[2] == 0.0 && dbuf[3] == 0.0 && dbuf[4] == 0.0 && dbuf[5] == 0.0) {
1056                                         /*  Skip it  */
1057                                 } else {
1058                                         MoleculeSetAniso(mp, i, 0, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], NULL);
1059                                 }
1060                                 if (j == 7 && ibuf[0] != 0) {
1061                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1062                                                 snprintf(errbuf, errbufsize, "line %d: anisotropic thermal parameters sigma missing", lineNumber);
1063                                                 goto exit;
1064                                         }
1065                                         if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1066                                                 snprintf(errbuf, errbufsize, "line %d: anisotropic thermal parameters sigma cannot be read for atom %d", lineNumber, i + 1);
1067                                                 goto exit;
1068                                         }
1069                                         ap = ATOM_AT_INDEX(mp->atoms, i);
1070                                         if (ap->aniso == NULL) {
1071                                                 snprintf(errbuf, errbufsize, "line %d: anisotropic thermal parameters sigma are given while the parameters are not given", lineNumber);
1072                                                 goto exit;
1073                                         }
1074                                         ap->aniso->has_bsig = 1;
1075                                         for (j = 0; j < 6; j++)
1076                                                 ap->aniso->bsig[j] = dbuf[j];
1077                                 }
1078                                 i++;
1079                         }
1080                         continue;
1081                 } else if (strcmp(buf, "!:periodic_box") == 0) {
1082                         Vector vs[5];
1083                         Byte has_sigma = 0;
1084                         i = 0;
1085                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1086                                 if (buf[0] == '!')
1087                                         continue;
1088                                 if (buf[0] == '\n')
1089                                         break;
1090                                 /* ax ay az; bx by bz; cx cy cz; ox oy oz; fx fy fz [sigma; sa sb sc s_alpha s_beta s_gamma] */
1091                                 if (i < 4) {
1092                                         if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1093                                                 snprintf(errbuf, errbufsize, "line %d: bad periodic_box format", lineNumber);
1094                                                 goto exit;
1095                                         }
1096                                         vs[i].x = dbuf[0];
1097                                         vs[i].y = dbuf[1];
1098                                         vs[i].z = dbuf[2];
1099                                         i++;
1100                                         continue;
1101                                 }
1102                                 if ((j = sscanf(buf, "%d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3])) < 3) {
1103                                         snprintf(errbuf, errbufsize, "line %d: bad periodic_box format", lineNumber);
1104                                         goto exit;
1105                                 }
1106                                 if (j == 4 && ibuf[3] != 0)
1107                                         has_sigma = 1;
1108                                 cbuf[0][0] = ibuf[0];
1109                                 cbuf[0][1] = ibuf[1];
1110                                 cbuf[0][2] = ibuf[2];
1111                                 MoleculeSetPeriodicBox(mp, vs, vs + 1, vs + 2, vs + 3, cbuf[0]);
1112                                 if (has_sigma) {
1113                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1114                                                 snprintf(errbuf, errbufsize, "line %d: sigma for cell parameters are missing", lineNumber);
1115                                                 goto exit;
1116                                         }
1117                                         if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1118                                                 snprintf(errbuf, errbufsize, "line %d: bad periodic_box sigma format", lineNumber);
1119                                                 goto exit;
1120                                         }
1121                                         if (mp->cell != NULL) {
1122                                                 mp->cell->has_sigma = 1;
1123                                                 for (i = 0; i < 6; i++) {
1124                                                         mp->cell->cellsigma[i] = dbuf[i];
1125                                                 }
1126                                         } else {
1127                                                 snprintf(errbuf, errbufsize, "line %d: cell sigma are given while cell is not given", lineNumber);
1128                                         }
1129                                 }
1130                                 break;
1131                         }
1132                         continue;
1133                 } else if (strcmp(buf, "!:frame_periodic_boxes") == 0) {
1134                         Vector vs[5];
1135                         i = 0;
1136                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1137                                 if (buf[0] == '!')
1138                                         continue;
1139                                 if (buf[0] == '\n')
1140                                         break;
1141                                 if (sscanf(buf, "%lf %lf %f", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1142                                         snprintf(errbuf, errbufsize, "line %d: bad frame_periodic_box format", lineNumber);
1143                                         goto exit;
1144                                 }
1145                                 vs[i].x = dbuf[0];
1146                                 vs[i].y = dbuf[1];
1147                                 vs[i].z = dbuf[2];
1148                                 i++;
1149                                 if (i == 4) {
1150                                         AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells, vs);
1151                                         i = 0;
1152                                 }
1153                         }
1154                         continue;
1155                 } else if (strcmp(buf, "!:md_parameters") == 0) {
1156                         MDArena *arena;
1157                         if (mp->arena == NULL)
1158                                 mp->arena = md_arena_new(NULL);
1159                         arena = mp->arena;
1160                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1161                                 if (buf[0] == '!')
1162                                         continue;
1163                                 if (buf[0] == '\n')
1164                                         break;
1165                                 bufp = buf;
1166                                 comp = strsep(&bufp, " \t");
1167                                 if (bufp != NULL) {
1168                                         while (*bufp == ' ' || *bufp == '\t')
1169                                                 bufp++;
1170                                         valp = strsep(&bufp, "\n");
1171                                 } else valp = NULL;
1172                                 if (strcmp(comp, "alchem_flags") == 0) {
1173                                         j = (valp == NULL ? 0 : atoi(valp));
1174                                         if (j > 0) {
1175                                                 valp = (char *)malloc(j);
1176                                                 i = 0;
1177                                                 while ((k = fgetc(fp)) >= 0) {
1178                                                         ungetc(k, fp);
1179                                                         if (k < '0' || k > '9') {
1180                                                                 snprintf(errbuf, errbufsize, "line %d: too few flags in alchem_flags block", lineNumber + 1);
1181                                                                 free(valp);
1182                                                                 goto exit;
1183                                                         }
1184                                                         ReadLine(buf, sizeof buf, fp, &lineNumber);
1185                                                         bufp = buf;
1186                                                         while (*bufp != 0) {
1187                                                                 if (*bufp >= '0' && *bufp <= '2') {
1188                                                                         if (i >= j) {
1189                                                                                 snprintf(errbuf, errbufsize, "line %d: too many flags in alchem_flags block", lineNumber);
1190                                                                                 free(valp);
1191                                                                                 goto exit;
1192                                                                         }
1193                                                                         valp[i++] = *bufp - '0';
1194                                                                 } else if (*bufp != ' ' && *bufp != '\t' && *bufp != '\n') {
1195                                                                         snprintf(errbuf, errbufsize, "line %d: strange character (0x%02x) in alchem_flags block", lineNumber, (int)*bufp);
1196                                                                         free(valp);
1197                                                                         goto exit;
1198                                                                 }
1199                                                                 bufp++;
1200                                                         }
1201                                                         if (i == j)
1202                                                                 break;
1203                                                 }
1204                                                 md_set_alchemical_flags(arena, j, valp);
1205                                                 free(valp);
1206                                         }
1207                                         continue;
1208                                 }
1209                                 /*  In the following, the redundant "!= NULL" is to suppress suprious warning  */
1210                                 if ((strcmp(comp, "log_file") == 0 && (pp = &arena->log_result_name) != NULL)
1211                                         || (strcmp(comp, "coord_file") == 0 && (pp = &arena->coord_result_name) != NULL)
1212                                         || (strcmp(comp, "vel_file") == 0 && (pp = &arena->vel_result_name) != NULL)
1213                                         || (strcmp(comp, "force_file") == 0 && (pp = &arena->force_result_name) != NULL)
1214                                         || (strcmp(comp, "debug_file") == 0 && (pp = &arena->debug_result_name) != NULL)) {
1215                                         if (*valp == 0 || strstr(valp, "(null)") == valp)
1216                                                 *pp = NULL;
1217                                         else {
1218                                                 valp = strdup(valp);
1219                                                 if (valp != NULL) {
1220                                                         char *valp1 = strchr(valp, '\n');
1221                                                         if (valp1 != NULL)
1222                                                                 *valp1 = 0;
1223                                                 }
1224                                                 *pp = valp;
1225                                         }
1226                                 } else if ((strcmp(comp, "debug_output_level") == 0 && (ip = &arena->debug_output_level) != NULL)
1227                                                    || (strcmp(comp, "coord_output_freq") == 0 && (ip = &arena->coord_output_freq) != NULL)
1228                                                    || (strcmp(comp, "energy_output_freq") == 0 && (ip = &arena->energy_output_freq) != NULL)
1229                                                    || (strcmp(comp, "coord_frame") == 0 && (ip = &arena->coord_result_frame) != NULL)
1230                                                    || (strcmp(comp, "andersen_freq") == 0 && (ip = &arena->andersen_thermo_freq) != NULL)
1231                                                    || (strcmp(comp, "random_seed") == 0 && (ip = &arena->random_seed) != NULL)
1232                                                    || (strcmp(comp, "use_xplor_shift") == 0 && (ip = &arena->use_xplor_shift) != NULL)
1233                                                    || (strcmp(comp, "relocate_center") == 0 && (ip = &arena->relocate_center) != NULL)
1234                                                    || (strcmp(comp, "surface_potential_freq") == 0 && (ip = &arena->surface_potential_freq) != NULL)
1235                                                    || (strcmp(comp, "use_graphite") == 0 && (ip = &arena->use_graphite) != NULL)) {
1236                                         *ip = (valp == NULL ? 0 : atoi(valp));
1237                                 } else if ((strcmp(comp, "timestep") == 0 && (dp = &arena->timestep) != NULL)
1238                                                    || (strcmp(comp, "cutoff") == 0 && (dp = &arena->cutoff) != NULL)
1239                                                    || (strcmp(comp, "electro_cutoff") == 0 && (dp = &arena->electro_cutoff) != NULL)
1240                                                    || (strcmp(comp, "pairlist_distance") == 0 && (dp = &arena->pairlist_distance) != NULL)
1241                                                    || (strcmp(comp, "temperature") == 0 && (dp = &arena->temperature) != NULL)
1242                                                    || (strcmp(comp, "andersen_coupling") == 0 && (dp = &arena->andersen_thermo_coupling) != NULL)
1243                                                    || (strcmp(comp, "dielectric") == 0 && (dp = &arena->dielectric) != NULL)
1244                                                    || (strcmp(comp, "gradient_convergence") == 0 && (dp = &arena->gradient_convergence) != NULL)
1245                                                    || (strcmp(comp, "coordinate_convergence") == 0 && (dp = &arena->coordinate_convergence) != NULL)
1246                                                    || (strcmp(comp, "scale14_vdw") == 0 && (dp = &arena->scale14_vdw) != NULL)
1247                                                    || (strcmp(comp, "scale14_elect") == 0 && (dp = &arena->scale14_elect) != NULL)
1248                                                    || (strcmp(comp, "surface_probe_radius") == 0 && (dp = &arena->probe_radius) != NULL)
1249                                                    || (strcmp(comp, "surface_tension") == 0 && (dp = &arena->surface_tension) != NULL)
1250                                                    || (strcmp(comp, "alchemical_lambda") == 0 && (dp = &arena->alchem_lambda) != NULL)
1251                                                    || (strcmp(comp, "alchemical_delta_lambda") == 0 && (dp = &arena->alchem_dlambda) != NULL)) {
1252                                         *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1253                                 }
1254                         }
1255                         continue;
1256                 } else if (strcmp(buf, "!:pressure_control_parameters") == 0) {
1257                         MDPressureArena *pressure;
1258                         if (mp->arena == NULL)
1259                                 mp->arena = md_arena_new(mp);
1260                         if (mp->arena->pressure == NULL)
1261                                 mp->arena->pressure = pressure_new();
1262                         pressure = mp->arena->pressure;
1263                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1264                                 if (buf[0] == '!')
1265                                         continue;
1266                                 if (buf[0] == '\n')
1267                                         break;
1268                                 bufp = buf;
1269                                 comp = strsep(&bufp, " \t");
1270                                 if (bufp != NULL) {
1271                                         while (*bufp == ' ' || *bufp == '\t')
1272                                                 bufp++;
1273                                         valp = strsep(&bufp, "\n");
1274                                 } else valp = NULL;
1275                                 if (strcmp(comp, "pressure") == 0) {
1276                                         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) {
1277                                                 snprintf(errbuf, errbufsize, "line %d: bad format", lineNumber);
1278                                                 goto exit;
1279                                         }
1280                                         for (i = 0; i < 9; i++)
1281                                                 pressure->apply[i] = dbuf[i];
1282                                 } else if (strcmp(comp, "pressure_cell_flexibility") == 0) {
1283                                         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) {
1284                                                 snprintf(errbuf, errbufsize, "line %d: bad format", lineNumber);
1285                                                 goto exit;
1286                                         }
1287                                         for (i = 0; i < 8; i++)
1288                                                 pressure->cell_flexibility[i] = dbuf[i];
1289                                 } else if ((strcmp(comp, "pressure_freq") == 0 && (ip = &pressure->freq) != NULL)) {
1290                                         *ip = (valp == NULL ? 0 : atoi(valp));
1291                                 } else if ((strcmp(comp, "pressure_coupling") == 0 && (dp = &pressure->coupling) != NULL)
1292                                                    || (strcmp(comp, "pressure_fluctuate_cell_origin") == 0 && (dp = &pressure->fluctuate_cell_origin) != NULL)
1293                                                    || (strcmp(comp, "pressure_fluctuate_cell_orientation") == 0 && (dp = &pressure->fluctuate_cell_orientation) != NULL)) {
1294                                         *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1295                                 }
1296                         }
1297                         continue;
1298                 } else if (strcmp(buf, "!:velocity") == 0) {
1299                         i = 0;
1300                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1301                                 if (buf[0] == '!')
1302                                         continue;
1303                                 if (buf[0] == '\n')
1304                                         break;
1305                                 /* idx vx vy vz */
1306                                 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1307                                         snprintf(errbuf, errbufsize, "line %d: atom velocity cannot be read for atom %d", lineNumber, i + 1);
1308                                         goto exit;
1309                                 }
1310                                 if (i >= mp->natoms) {
1311                                         snprintf(errbuf, errbufsize, "line %d: too many atom velocity records\n", lineNumber);
1312                                         goto exit;
1313                                 }
1314                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1315                                 ap->v.x = dbuf[0];
1316                                 ap->v.y = dbuf[1];
1317                                 ap->v.z = dbuf[2];
1318                                 i++;
1319                         }
1320                         continue;
1321                 } else if (strcmp(buf, "!:force") == 0) {
1322                         i = 0;
1323                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1324                                 if (buf[0] == '!')
1325                                         continue;
1326                                 if (buf[0] == '\n')
1327                                         break;
1328                                 /* idx fx fy fz */
1329                                 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1330                                         snprintf(errbuf, errbufsize, "line %d: atom force cannot be read for atom %d", lineNumber, i + 1);
1331                                         goto exit;
1332                                 }
1333                                 if (i >= mp->natoms) {
1334                                         snprintf(errbuf, errbufsize, "line %d: too many atom force records\n", lineNumber);
1335                                         goto exit;
1336                                 }
1337                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1338                                 ap->f.x = dbuf[0];
1339                                 ap->f.y = dbuf[1];
1340                                 ap->f.z = dbuf[2];
1341                                 i++;
1342                         }
1343                         continue;
1344                 } else if (strcmp(buf, "!:parameter") == 0 || strcmp(buf, "!:parameters") == 0) {
1345                         Parameter *par = mp->par;
1346                         if (par == NULL) {
1347                                 mp->par = ParameterNew();
1348                                 par = mp->par;
1349                         }
1350                         bufp = NULL;
1351                         i = 0;
1352                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1353                                 if (buf[0] == '!')
1354                                         continue;
1355                                 if (buf[0] == '\n')
1356                                         break;
1357                                 j = ParameterReadFromString(par, buf, &bufp, fname, lineNumber, 0);
1358                                 if (j < 0) {
1359                                         snprintf(errbuf, errbufsize, "%s", bufp);
1360                                         goto exit;
1361                                 }
1362                                 i += j;
1363                         }
1364                         if (bufp != NULL) {
1365                                 MyAppCallback_setConsoleColor(1);
1366                                 MyAppCallback_showScriptMessage("%s", bufp);
1367                                 MyAppCallback_setConsoleColor(0);
1368                                 free(bufp);
1369                         }
1370                         continue;
1371                 } else if (strcmp(buf, "!:trackball") == 0) {
1372                         i = 0;
1373                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1374                                 if (buf[0] == '!')
1375                                         continue;
1376                                 if (buf[0] == '\n')
1377                                         break;
1378                                 /* scale; trx try trz; theta_deg x y z */
1379                                 if ((i == 0 && sscanf(buf, "%f", &mview_fbuf[0]) < 1)
1380                                         || (i == 1 && sscanf(buf, "%f %f %f",
1381                                                                                  &mview_fbuf[1], &mview_fbuf[2], &mview_fbuf[3]) < 3)
1382                                         || (i == 2 && sscanf(buf, "%f %f %f %f",
1383                                                                                  &mview_fbuf[4], &mview_fbuf[5], &mview_fbuf[6], &mview_fbuf[7]) < 4)) {
1384                                         snprintf(errbuf, errbufsize, "line %d: bad trackball format", lineNumber);
1385                                         goto exit;
1386                                 }
1387                                 i++;
1388                         }
1389                         continue;
1390                 } else if (strcmp(buf, "!:view") == 0) {
1391                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1392                                 if (buf[0] == '!')
1393                                         continue;
1394                                 if (buf[0] == '\n')
1395                                         break;
1396                                 bufp = buf;
1397                                 comp = strsep(&bufp, " \t");
1398                                 if (bufp != NULL) {
1399                                         while (*bufp == ' ' || *bufp == '\t')
1400                                                 bufp++;
1401                                         valp = strsep(&bufp, "\n");
1402                                 } else valp = NULL;
1403                                 /*  In the following, the redundant "!= NULL" is to suppress suprious warning  */
1404                                 if ((strcmp(comp, "show_unit_cell") == 0 && (i = 1))
1405                                         || (strcmp(comp, "show_periodic_box") == 0 && (i = 2))
1406                                         || (strcmp(comp, "show_expanded_atoms") == 0 && (i = 3))
1407                                         || (strcmp(comp, "show_ellipsoids") == 0 && (i = 4))
1408                                         || (strcmp(comp, "show_hydrogens") == 0 && (i = 5))
1409                                         || (strcmp(comp, "show_dummy_atoms") == 0 && (i = 6))
1410                                         || (strcmp(comp, "show_rotation_center") == 0 && (i = 7))
1411                                         || (strcmp(comp, "show_graphite_flag") == 0 && (i = 8))
1412                                         || (strcmp(comp, "show_periodic_image_flag") == 0 && (i = 9))
1413                                         || (strcmp(comp, "show_graphite") == 0 && (i = 10))) {
1414                                         mview_ibuf[i - 1] = atoi(valp);
1415                                 } else if (strcmp(comp, "show_periodic_image") == 0) {
1416                                         sscanf(valp, "%d %d %d %d %d %d",
1417                                                    &mview_ibuf[10], &mview_ibuf[11], &mview_ibuf[12],
1418                                                    &mview_ibuf[13], &mview_ibuf[14], &mview_ibuf[15]);
1419                                 }
1420                         }
1421                         continue;
1422                 }
1423                 /*  Unknown sections are silently ignored  */
1424         }
1425
1426         MoleculeCleanUpResidueTable(mp);
1427         if (mp->arena != NULL)
1428                 md_arena_set_molecule(mp->arena, mp);
1429
1430 exit:
1431         fclose(fp);
1432         if (errbuf[0] != 0) {
1433                 /*  The content of mp may be broken, so make it empty  */
1434                 MoleculeClear(mp);
1435                 return -1;
1436         } else {
1437                 MainView *mview = mp->mview;
1438                 if (mview != NULL) {
1439                         if (mview_ibuf[0] != kUndefined)
1440                                 mview->showUnitCell = mview_ibuf[0];
1441                         if (mview_ibuf[1] != kUndefined)
1442                                 mview->showPeriodicBox = mview_ibuf[1];
1443                         if (mview_ibuf[2] != kUndefined)
1444                                 mview->showExpandedAtoms = mview_ibuf[2];
1445                         if (mview_ibuf[3] != kUndefined)
1446                                 mview->showEllipsoids = mview_ibuf[3];
1447                         if (mview_ibuf[4] != kUndefined)
1448                                 mview->showHydrogens = mview_ibuf[4];
1449                         if (mview_ibuf[5] != kUndefined)
1450                                 mview->showDummyAtoms = mview_ibuf[5];
1451                         if (mview_ibuf[6] != kUndefined)
1452                                 mview->showRotationCenter = mview_ibuf[6];
1453                         if (mview_ibuf[7] != kUndefined)
1454                                 mview->showGraphiteFlag = mview_ibuf[7];
1455                         if (mview_ibuf[8] != kUndefined)
1456                                 mview->showPeriodicImageFlag = mview_ibuf[8];
1457                         if (mview_ibuf[9] != kUndefined)
1458                                 mview->showGraphite = mview_ibuf[9];
1459                         for (i = 0; i < 6; i++) {
1460                                 if (mview_ibuf[10 + i] != kUndefined)
1461                                         mview->showPeriodicImage[i] = mview_ibuf[10 + i];
1462                         }
1463                         if (mview->track != NULL) {
1464                                 if (mview_fbuf[0] != kUndefined)
1465                                         TrackballSetScale(mview->track, mview_fbuf[0]);
1466                                 if (mview_fbuf[1] != kUndefined)
1467                                         TrackballSetTranslate(mview->track, mview_fbuf + 1);
1468                                 if (mview_fbuf[4] != kUndefined)
1469                                         TrackballSetRotate(mview->track, mview_fbuf + 4);
1470                         }
1471                 }
1472         }
1473         return 0;
1474 }
1475
1476 int
1477 MoleculeLoadPsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
1478 {
1479         FILE *fp;
1480         char buf[1024];
1481         char *p;
1482         int section = -1;
1483         int i, j, err, fn;
1484         int lineNumber;
1485         Int ibuf[12];
1486         Vector *frames = NULL;
1487         Atom *ap;
1488         err = 0;
1489         if (errbuf == NULL) {
1490                 errbuf = buf;
1491                 errbufsize = 1024;
1492         }
1493         errbuf[0] = 0;
1494         if (mp == NULL)
1495                 mp = MoleculeNew();
1496         fp = fopen(fname, "rb");
1497         if (fp == NULL) {
1498                 snprintf(errbuf, errbufsize, "Cannot open file");
1499                 return 1;
1500         }
1501 /*      flockfile(fp); */
1502         lineNumber = 0;
1503         fn = 0;
1504         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1505                 if (strncmp(buf, "PSF", 3) == 0) {
1506                         section = 0;
1507                         continue;
1508                 } else {
1509                         for (p = buf; *p != 0 && isspace(*p); p++) {}
1510                         if (*p == 0) {
1511                                 section++;
1512                                 continue;
1513                         }
1514                 }
1515                 if (strstr(buf, "!COORD") != NULL) {
1516                         /*  Extended psf file with coordinates  */
1517                         if (fn > 0) {
1518                                 /*  Allocate a temporary storage for frames  */
1519                                 size_t size = sizeof(Vector) * mp->natoms * fn;
1520                                 if (frames == NULL)
1521                                         frames = (Vector *)malloc(size);
1522                                 else
1523                                         frames = (Vector *)realloc(frames, size);
1524                                 if (frames == NULL)
1525                                         goto panic;
1526                         #if 0
1527                                 if (fn == 1) {
1528                                         /*  Copy the coordinates of the first frame  */
1529                                         for (i = 0; i < mp->natoms; i++) {
1530                                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1531                                                 frames[i] = ap->r;
1532                                         }
1533                                 }
1534                                 /*  Copy the coordinates of the last frame to the newly created frame  */
1535                                 memmove(frames + sizeof(Vector) * mp->natoms * fn, frames + sizeof(Vector) * mp->natoms * (fn - 1), sizeof(Vector) * mp->natoms);
1536                         #endif
1537                         }
1538                         /*  Read coordinates  */
1539                         for (i = 0; i < mp->natoms; i++) {
1540                                 double dval[3];
1541                                 Vector r;
1542                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1543                                         err = 1;
1544                                         snprintf(errbuf, errbufsize, "line %d: premature end of file while reading coordinates (frame %d)", lineNumber, fn);
1545                                         goto exit;
1546                                 }
1547                                 if (sscanf(buf, "%lg %lg %lg", dval, dval + 1, dval + 2) != 3) {
1548                                         err = 1;
1549                                         snprintf(errbuf, errbufsize, "line %d: coordinates cannot be read for atom %d", lineNumber, i + 1);
1550                                         goto exit;
1551                                 }
1552                                 r.x = dval[0];
1553                                 r.y = dval[1];
1554                                 r.z = dval[2];
1555                                 if (fn == 0)
1556                                         ATOM_AT_INDEX(mp->atoms, i)->r = r;
1557                                 else
1558                                         frames[mp->natoms * (fn - 1) + i] = r;
1559                         }
1560                         fn++;
1561                         continue;
1562                 }
1563                 
1564                 if (section == 2) {
1565                         /*  Atoms  */
1566                         Int natoms;
1567                         ReadFormat(buf, "I8", &natoms);
1568                         if (mp->atoms != NULL)
1569                                 free(mp->atoms);
1570                         if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
1571                                 goto panic;
1572                         mp->nresidues = 0;
1573                         for (i = 0; i < natoms; i++) {
1574                                 struct {
1575                                         char segName[5], resName[4], atomName[5], atomType[3], element[3];
1576                                         Int serial;
1577                                 } w;
1578                                 memset(&w, 0, sizeof(w));
1579                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1580                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1581                                         err = 1;
1582                                         snprintf(errbuf, errbufsize, "line %d: premature end of file while reading atoms", lineNumber);
1583                                         goto exit;
1584                                 }
1585                                 ReadFormat(buf, "I8 x1 S4 I5 x1 S3 x2 S4 x1 S4 F16 F10",
1586                                         &w.serial, w.segName, &ap->resSeq, w.resName, w.atomName, 
1587                                         w.atomType, &ap->charge, &ap->weight);
1588                                 strncpy(ap->segName, w.segName, 4);
1589                                 strncpy(ap->resName, w.resName, 3);
1590                                 strncpy(ap->aname, w.atomName, 4);
1591                                 ap->type = AtomTypeEncodeToUInt(w.atomType);
1592                                 /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
1593                                 ap->atomicNumber = GuessAtomicNumber(w.atomName, ap->weight);
1594                                 ElementToString(ap->atomicNumber, w.element);
1595                                 strncpy(ap->element, w.element, 2);
1596                         /*      w.element[0] = 0;
1597                                 for (p = w.atomName; *p != 0; p++) {
1598                                         if (isalpha(*p) && *p != '_') {
1599                                                 w.element[0] = toupper(*p);
1600                                                 if (isalpha(p[1]) && p[1] != '_') {
1601                                                         w.element[1] = toupper(p[1]);
1602                                                         w.element[2] = 0;
1603                                                 } else {
1604                                                         w.element[1] = 0;
1605                                                 }
1606                                                 break;
1607                                         }
1608                                 }
1609                                 strncpy(ap->element, w.element, 2);
1610                                 ap->atomicNumber = ElementToInt(w.element); */
1611                                 if (w.resName[0] == 0)
1612                                         strncpy(ap->resName, "XXX", 3);
1613                                 if (ap->resSeq > mp->nresidues)
1614                                         mp->nresidues = ap->resSeq;
1615                         }
1616                         if (mp->residues != NULL)
1617                                 free(mp->residues);
1618                         if (NewArray(&mp->residues, &mp->nresidues, sizeof(char (*)[4]), mp->nresidues + 1) == 0)
1619                                 goto panic;
1620                         for (i = 0; i < mp->natoms; i++) {
1621                                 j = mp->atoms[i].resSeq;
1622                                 if (mp->residues[j][0] == 0)
1623                                         strncpy(mp->residues[j], mp->atoms[i].resName, 4);
1624                         }
1625                         continue;
1626                 } else if (section == 3) {
1627                         /*  Bonds  */
1628                         Int nbonds;
1629                         Int *bp;
1630                         ReadFormat(buf, "I8", &nbonds);
1631                         if (mp->bonds != NULL)
1632                                 free(mp->bonds);
1633                         if (NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, nbonds) == NULL)
1634                                 goto panic;
1635                         bp = mp->bonds;
1636                         for (i = 0; i < nbonds; i += 4) {
1637                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1638                                         snprintf(errbuf, errbufsize, "line %d: premature end of file while reading bonds", lineNumber);
1639                                         err = 1;
1640                                         goto exit;
1641                                 }
1642                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1643                                         ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1644                                 for (j = 0; j < 4 && i + j < nbonds; j++) {
1645                                         Int b1, b2;
1646                                         Atom *ap;
1647                                         b1 = ibuf[j * 2] - 1;    /* Internal atom number is 0-based */
1648                                         b2 = ibuf[j * 2 + 1] - 1;
1649                                         if (b1 < 0 || b1 >= mp->natoms || b2 < 0 || b2 >= mp->natoms) {
1650                                                 snprintf(errbuf, errbufsize, "line %d: The bond %d-%d includes non-existent atom", lineNumber, b1+1, b2+1);
1651                                                 err = 1;
1652                                                 goto exit;
1653                                         }
1654                                         *bp++ = b1;
1655                                         *bp++ = b2;
1656                                         ap = ATOM_AT_INDEX(mp->atoms, b1);
1657                                         AtomInsertConnectEntry(ap, ap->nconnects, b2);
1658                                 /*      if (ap->nconnects < ATOMS_MAX_CONNECTS)
1659                                                 ap->connects[ap->nconnects++] = b2;
1660                                         else {
1661                                                 snprintf(errbuf, errbufsize, "line %d: The atom %d has more than %d bonds", lineNumber, b1+1, ATOMS_MAX_CONNECTS);
1662                                                 err = 1;
1663                                                 goto exit;
1664                                         } */
1665                                         ap = ATOM_AT_INDEX(mp->atoms, b2);
1666                                         AtomInsertConnectEntry(ap, ap->nconnects, b1);
1667                                 /*      if (ap->nconnects < ATOMS_MAX_CONNECTS)
1668                                                 ap->connects[ap->nconnects++] = b1;
1669                                         else {
1670                                                 snprintf(errbuf, errbufsize, "line %d: The atom %d has more than %d bonds", lineNumber, b2+1, ATOMS_MAX_CONNECTS);
1671                                                 err = 1;
1672                                                 goto exit;
1673                                         } */
1674                                 }
1675                         }
1676                         continue;
1677                 } else if (section == 4) {
1678                         /*  Angles  */
1679                         Int nangles;
1680                         Int *gp;
1681                         ReadFormat(buf, "I8", &nangles);
1682                         if (mp->angles != NULL)
1683                                 free(mp->angles);
1684                         if (NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, nangles) == NULL)
1685                                 goto panic;
1686                         gp = mp->angles;
1687                         for (i = 0; i < nangles; i += 3) {
1688                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1689                                         snprintf(errbuf, errbufsize, "line %d: premature end of file while reading angles", lineNumber);
1690                                         err = 1;
1691                                         goto exit;
1692                                 }
1693                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1694                                         ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7, ibuf + 8);
1695                                 for (j = 0; j < 3 && i + j < nangles; j++) {
1696                                         Int a1, a2, a3;
1697                                         a1 = ibuf[j * 3] - 1;   /* Internal atom number is 0-based */
1698                                         a2 = ibuf[j * 3 + 1] - 1;
1699                                         a3 = ibuf[j * 3 + 2] - 1;
1700                                         if (a1 < 0 || a1 >= mp->natoms || a2 < 0 || a2 >= mp->natoms || a3 < 0 || a3 >= mp->natoms) {
1701                                                 snprintf(errbuf, errbufsize, "line %d: The angle %d-%d-%d includes non-existent atom", lineNumber, a1+1, a2+1, a3+1);
1702                                                 err = 1;
1703                                                 goto exit;
1704                                         }
1705                                         *gp++ = a1;
1706                                         *gp++ = a2;
1707                                         *gp++ = a3;
1708                                 }
1709                         }
1710                         continue;
1711                 } else if (section == 5 || section == 6) {
1712                         /*  Dihedrals and Impropers  */
1713                         Int ndihedrals;
1714                         Int *dp;
1715                         ReadFormat(buf, "I8", &ndihedrals);
1716                         if (section == 5) {
1717                                 if (NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, ndihedrals) == NULL)
1718                                         goto panic;
1719                                 dp = mp->dihedrals;
1720                         } else {
1721                                 if (NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, ndihedrals) == NULL)
1722                                         goto panic;
1723                                 dp = mp->impropers;
1724                         }
1725                         for (i = 0; i < ndihedrals; i += 2) {
1726                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1727                                         fclose(fp);
1728                                         snprintf(errbuf, errbufsize, "line %d: premature end of file while reading %s", lineNumber, (section == 5 ? "dihedral" : "improper"));
1729                                         err = 1;
1730                                         goto exit;
1731                                 }
1732                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3, ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1733                                 for (j = 0; j < 2 && i + j < ndihedrals; j++) {
1734                                         Int d1, d2, d3, d4;
1735                                         d1 = ibuf[j * 4] - 1;   /*  Internal atom number is 0-based  */
1736                                         d2 = ibuf[j * 4 + 1] - 1;
1737                                         d3 = ibuf[j * 4 + 2] - 1;
1738                                         d4 = ibuf[j * 4 + 3] - 1;
1739                                         if (d1 < 0 || d1 >= mp->natoms || d2 < 0 || d2 >= mp->natoms || d3 < 0 || d3 >= mp->natoms || d4 < 0 || d4 >= mp->natoms) {
1740                                                 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);
1741                                                 err = 1;
1742                                                 goto exit;
1743                                         }
1744                                         *dp++ = d1;
1745                                         *dp++ = d2;
1746                                         *dp++ = d3;
1747                                         *dp++ = d4;
1748                                 }
1749                         }
1750                         continue;
1751                 }
1752         }
1753         
1754         /*  Create frames for each atom if necessary  */
1755         if (fn > 1) {
1756                 for (i = 0; i < mp->natoms; i++) {
1757                         ap = ATOM_AT_INDEX(mp->atoms, i);
1758                         ap->frames = (Vector *)malloc(sizeof(Vector) * fn);
1759                         if (ap->frames == NULL)
1760                                 goto panic;
1761                         ap->nframes = fn;
1762                         for (j = 0; j < fn; j++)
1763                                 ap->frames[j] = frames[mp->natoms * j + i];
1764                 }
1765                 free(frames);
1766                 frames = NULL;
1767         }
1768
1769   exit:
1770 /*      funlockfile(fp); */
1771         fclose(fp);
1772         mp->nframes = -1;  /*  Should be recalculated later  */
1773         if (err)
1774                 return 1;
1775         else if (section == -1)
1776                 return -1;
1777         return 0;
1778   panic:
1779         Panic("low memory while reading structure file %s", fname);
1780         return 1; /* not reached */
1781 }
1782
1783 /* ("-x", "y", "-z+0.5") -> (-1,0,0,0,0,1,0,0,0,0,-1,0.5)  */
1784 static int
1785 sMoleculeSymopStringsToTransform(char **symops, Transform tr)
1786 {
1787         int i;
1788         char *symop;
1789         memset(tr, 0, sizeof(Transform));
1790         for (i = 0; i < 3; i++) {
1791                 symop = symops[i];
1792                 if (symop == NULL)
1793                         return 1;
1794                 while (*symop != 0) {
1795                         int sn = 1;
1796                         while (isspace(*symop))
1797                                 symop++;
1798                         if (*symop == 0 || *symop == '\r' || *symop == 'n')
1799                                 break;
1800                         if (*symop == '-') {
1801                                 sn = -1;
1802                                 symop++;
1803                         } else if (*symop == '+') {
1804                                 sn = 1;
1805                                 symop++;
1806                         }
1807                         while (isspace(*symop))
1808                                 symop++;
1809                         if (*symop == '.' || isdigit(*symop)) {
1810                                 /*  Numerical offset  */
1811                                 double d = strtod(symop, &symop);
1812                                 if (*symop == '/') {
1813                                         double dd = strtod(symop + 1, &symop);
1814                                         if (dd > 0)
1815                                                 d /= dd;
1816                                         else
1817                                                 return 1;  /*  Bad format  */
1818                                 }
1819                                 tr[9 + i] = d * sn;
1820                         } else if (*symop == 'x' || *symop == 'X') {
1821                                 tr[i] = sn;
1822                                 symop++;
1823                         } else if (*symop == 'y' || *symop == 'Y') {
1824                                 tr[i + 3] = sn;
1825                                 symop++;
1826                         } else if (*symop == 'z' || *symop == 'Z') {
1827                                 tr[i + 6] = sn;
1828                                 symop++;
1829                         } else return 1;  /*  Bad format  */
1830                 } /* end while (*symop != 0) */
1831         }
1832         return 0;
1833 }
1834
1835 static void
1836 sMoleculeGenerateSymopWithTransform(Molecule *mp, Transform gtr, int num)
1837 {
1838         int i, j;
1839         Transform tr;
1840         if (num <= 0)
1841                 num = mp->nsyms;
1842         for (i = 0; i < num; i++) {
1843                 memmove(tr, mp->syms[i], sizeof(Transform));
1844                 TransformMul(tr, gtr, tr);
1845                 for (j = 9; j < 12; j++) {
1846                         if (tr[j] >= 1.0)
1847                                 tr[j] -= 1.0;
1848                         else if (tr[j] <= 0.0)
1849                                 tr[j] += 1.0;
1850                 }
1851                 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1852         }
1853 }
1854
1855 static char *
1856 sChomp(char *buf)
1857 {
1858         char *p = buf + strlen(buf) - 1;
1859         if (p >= buf && (*p == '\n' || *p == '\r')) {
1860                 *p = 0;
1861                 if (--p >= buf && (*p == '\n' || *p == '\r')) {
1862                         *p = 0;
1863                 }
1864         }
1865         return buf;
1866 }
1867
1868 int
1869 MoleculeLoadTepFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
1870 {
1871         FILE *fp;
1872         char buf[1024];
1873         int section = -1;
1874         int lineNumber;
1875         int cellType;
1876         Int ibuf[12];
1877         Double fbuf[12];
1878         Int *bonds, nbonds;
1879         if (errbuf == NULL) {
1880                 errbuf = buf;
1881                 errbufsize = 1024;
1882         }
1883         errbuf[0] = 0;
1884         if (mp == NULL)
1885                 mp = MoleculeNew();
1886         fp = fopen(fname, "rb");
1887         if (fp == NULL) {
1888                 snprintf(errbuf, errbufsize, "Cannot open file");
1889                 return 1;
1890         }
1891         lineNumber = 0;
1892         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1893                 if (section == -1) {
1894                         /*  Title  */
1895                         section = 0;
1896                         continue;
1897                 }
1898                 if (section == 0) {
1899                         /*  XtalCell  */
1900                         ReadFormat(buf, "I1F8F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5);
1901                         cellType = ibuf[0];
1902                         MoleculeSetCell(mp, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], 0);
1903                         section = 1;
1904                         continue;
1905                 }
1906                 if (section == 1) {
1907                         /*  Symmetry  */
1908                         Transform tr;
1909                         if (cellType == 0) {
1910                                 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);
1911                                 tr[0] = fbuf[1];
1912                                 tr[3] = fbuf[2];
1913                                 tr[6] = fbuf[3];
1914                                 tr[1] = fbuf[5];
1915                                 tr[4] = fbuf[6];
1916                                 tr[7] = fbuf[7];
1917                                 tr[2] = fbuf[9];
1918                                 tr[5] = fbuf[10];
1919                                 tr[8] = fbuf[11];
1920                                 tr[9] = fbuf[0];
1921                                 tr[10] = fbuf[4];
1922                                 tr[11] = fbuf[8];
1923                         } else {
1924                                 char *symops[3], *brks;
1925                                 sChomp(buf);
1926                                 memset(tr, 0, sizeof(Transform));
1927                                 ReadFormat(buf, "I1", ibuf);
1928                                 symops[0] = strtok_r(buf + 1, ", ", &brks);
1929                                 symops[1] = strtok_r(NULL, ", ", &brks);
1930                                 symops[2] = strtok_r(NULL, ", ", &brks);
1931                                 if (sMoleculeSymopStringsToTransform(symops, tr)) {
1932                                         snprintf(errbuf, errbufsize, "line %d: bad symmetry specification", lineNumber);
1933                                         return 1;
1934                                 }
1935                         }
1936                         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
1937                                 goto panic;
1938                         if (ibuf[0] != 0)
1939                                 section = 2;
1940                         continue;
1941                 }
1942                 if (section == 2) {      /*  Atoms  */
1943                         char name[8];
1944                         Atom *ap;
1945                         int atomType;
1946                         int atomIndex = mp->natoms;
1947                         ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
1948                         memset(ap, 0, gSizeOfAtomRecord);
1949                         ReadFormat(buf, "S6x3x9x9F9F9F9F9", name, fbuf, fbuf+1, fbuf+2, fbuf+3);
1950                         strncpy(ap->aname, name, 4);
1951                         ap->r.x = fbuf[0];
1952                         ap->r.y = fbuf[1];
1953                         ap->r.z = fbuf[2];
1954                         MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
1955                 /*      ap->atomicNumber = AtomNameToElement(ap->name);
1956                         ElementToString(ap->atomicNumber, ap->element); */
1957                 /*      sAtomSetElement(ap, -1, ap->name); */
1958                         guessElement(ap);
1959                         atomType = fbuf[3];
1960                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1961                                 snprintf(errbuf, errbufsize, "unexpected end of file");
1962                                 return 1;
1963                         }
1964                         ReadFormat(buf, "I1F8F9F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
1965                         atomType = fbuf[6];
1966                         if ((atomType >= 0 && atomType <= 5) || (atomType >= 8 && atomType <= 10)) { 
1967                                 /*  Anisotropic thermal parameters  */
1968                                 MoleculeSetAniso(mp, atomIndex, atomType, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[5], fbuf[4], NULL);
1969                         }
1970                         if (ibuf[0] != 0)
1971                                 section = 3;
1972                         continue;
1973                 }
1974         }
1975         fclose(fp);
1976         MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
1977         if (nbonds > 0) {
1978                 MoleculeAddBonds(mp, nbonds, bonds);
1979                 free(bonds);
1980         }
1981         mp->nframes = -1;  /*  Should be recalculated later  */
1982         return 0;
1983   panic:
1984         Panic("low memory while reading structure file %s", fname);
1985         return -1; /* not reached */
1986 }
1987
1988 int
1989 MoleculeLoadShelxFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
1990 {
1991         FILE *fp;
1992         char buf[1024];
1993         char *p1, *p2;
1994         int n;
1995         int lineNumber;
1996         int latticeType;
1997         int currentResSeq = 0;
1998         char currentResName[6];
1999         Transform tr;
2000         int ibuf[12];
2001         float fbuf[12];
2002         Double dbuf[12];
2003         Int nsfacs = 0;
2004         Int nbonds, *bonds;
2005         char (*sfacs)[4] = NULL;
2006
2007         if (errbuf == NULL) {
2008                 errbuf = buf;
2009                 errbufsize = 1024;
2010         }
2011         errbuf[0] = 0;
2012         if (mp == NULL)
2013                 mp = MoleculeNew();
2014         currentResName[0] = 0;
2015         fp = fopen(fname, "rb");
2016         if (fp == NULL) {
2017                 snprintf(errbuf, errbufsize, "Cannot open file");
2018                 return 1;
2019         }
2020         lineNumber = 0;
2021         tr[0] = tr[4] = tr[8] = 1;
2022         tr[1] = tr[2] = tr[3] = tr[5] = tr[6] = tr[7] = tr[9] = tr[10] = tr[11] = 0;
2023         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), 0, tr) == 0)
2024                 goto panic;
2025         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2026                 if (strncmp(buf, "CELL", 4) == 0) {
2027                         /*  XtalCell  */
2028                         sscanf(buf + 4, " %f %f %f %f %f %f %f", fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2029                         MoleculeSetCell(mp, fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], fbuf[6], 0);
2030                         continue;
2031                 } else if (strncmp(buf, "SFAC", 4) == 0) {
2032                         sChomp(buf);
2033                         for (p1 = strtok_r(buf + 4, " ", &p2); p1 != NULL; p1 = strtok_r(NULL, " ", &p2)) {
2034                                 char *pp = (char *)AssignArray(&sfacs, &nsfacs, 4, nsfacs, NULL);
2035                                 if (pp == NULL)
2036                                         goto panic;
2037                                 strncpy(pp, p1, 3);
2038                                 pp[3] = 0;
2039                         }
2040                         continue;
2041                 } else if (strncmp(buf, "LATT", 4) == 0) {
2042                         sscanf(buf + 4, " %d", &latticeType);
2043                         continue;
2044                 } else if (strncmp(buf, "SYMM", 4) == 0) {
2045                         char *symops[3], *brks;
2046                         memset(tr, 0, sizeof(Transform));
2047                 //      ReadFormat(buf + 4, "I1", ibuf);
2048                         sChomp(buf);
2049                         symops[0] = strtok_r(buf + 4, ",", &brks);
2050                         symops[1] = strtok_r(NULL, ",", &brks);
2051                         symops[2] = strtok_r(NULL, ",", &brks);
2052                         if (sMoleculeSymopStringsToTransform(symops, tr)) {
2053                                 snprintf(errbuf, errbufsize, "line %d: bad symmetry specification", lineNumber);
2054                                 return 1;
2055                         }
2056                         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2057                                 goto panic;
2058                         continue;
2059                 } else if (strncmp(buf, "RESI", 4) == 0) {
2060                         for (p1 = buf + 4; isspace(*p1); p1++);
2061                         if (isalpha(*p1)) {
2062                                 for (p2 = p1 + 1; isalnum(*p2); p2++);
2063                                 *p2 = 0;
2064                                 strncpy(currentResName, p1, 4);
2065                                 currentResName[4] = 0;
2066                                 p1 = p2 + 1;
2067                         } else currentResName[0] = 0;
2068                         sscanf(buf + 4, " %d", &currentResSeq);
2069                         continue;
2070                 } else {
2071                         /* Atom name: [A-Za-z]{1,2}[0-9]*  */
2072                         for (p1 = buf; p1 < buf + 2 && (isalpha(*p1) && *p1 != '_'); p1++);
2073                         if (p1 > buf) {
2074                                 while (isdigit(*p1))
2075                                         p1++;
2076                         }
2077                         if (p1 > buf && p1 <= buf + 4 && isspace(*p1)) {
2078                                 /*  Atom  */
2079                                 Atom *ap;
2080                                 char cont[4];
2081                                 int atomIndex = mp->natoms;
2082                                 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2083                                 memset(ap, 0, gSizeOfAtomRecord);
2084                                 strncpy(ap->aname, buf, 4);
2085                                 n = sscanf(p1, " %d %f %f %f %f %f %f %2s", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, cont);
2086                                 if (n == 8 && strcmp(cont, "=") == 0) {
2087                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2088                                                 snprintf(errbuf, errbufsize, "line %d: unexpected end of file within the atom cards", lineNumber);
2089                                                 return 1;
2090                                         }
2091                                         sscanf(buf, " %f %f %f %f", fbuf+6, fbuf+7, fbuf+8, fbuf+9);
2092                                         n = 10;   /*  Aniso  */
2093                                 } else n = 5; /*  Iso  */
2094                                 ap->r.x = fbuf[0];
2095                                 ap->r.y = fbuf[1];
2096                                 ap->r.z = fbuf[2];
2097                                 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2098                                 ap->occupancy = fbuf[3];
2099                                 if (ap->aname[0] != 'Q' && ibuf[0] >= 1 && ibuf[0] <= nsfacs) {
2100                                         strncpy(ap->element, sfacs[ibuf[0] - 1], 2);
2101                                         ap->element[2] = 0;
2102                                 /*      sAtomSetElement(ap, -1, sfacs[ibuf[0] - 1]); */
2103                                 /*      strncpy(ap->element, sfacs[ibuf[0] - 1], 4);
2104                                         ap->atomicNumber = ElementToInt(ap->element); */
2105                         /*      } else {
2106                                         sAtomSetElement(ap, -1, ap->name); */
2107                                 /*      ap->atomicNumber = AtomNameToElement(ap->name);
2108                                         ElementToString(ap->atomicNumber, ap->element); */
2109                                 }
2110                                 guessElement(ap);
2111                                 if (n == 10 || fbuf[4] >= 0.0) {
2112                                         int i, c, j;
2113                                         /*  Read in the standard deviations  */
2114                                         ReadLine(buf, sizeof buf, fp, &lineNumber);
2115                                         for (i = 0; i < 9; i++) {
2116                                                 j = 3 + i * 8;
2117                                                 c = buf[j + 8];
2118                                                 buf[j + 8] = 0;
2119                                                 dbuf[i] = strtod(buf + j, NULL);
2120                                                 buf[j + 8] = c;
2121                                         }
2122                                         ap->sigma.x = dbuf[0];
2123                                         ap->sigma.y = dbuf[1];
2124                                         ap->sigma.z = dbuf[2];
2125                                 }
2126                                 if (n == 5)
2127                                         ap->tempFactor = fbuf[4] * 78.9568352087147; /* 8*pi*pi */
2128                                 else
2129                                         MoleculeSetAniso(mp, atomIndex, 8, fbuf[4], fbuf[5], fbuf[6], fbuf[9], fbuf[7], fbuf[8], dbuf);
2130                                 ap->resSeq = currentResSeq;
2131                                 strncpy(ap->resName, currentResName, 4);
2132                         }
2133                         continue;
2134                 }
2135         }
2136         fclose(fp);
2137
2138         /*  Add symmetry operations according to the lattice type  */
2139         switch (latticeType < 0 ? -latticeType : latticeType) {
2140                 static Transform tr_i = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0.5};
2141                 static Transform tr_c = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0};
2142                 static Transform tr_a = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.5};
2143                 static Transform tr_b = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5};
2144                 static Transform tr_r1 = {0, 1, 0, -1, -1, 0, 0, 0, 1, 0, 0, 0};
2145                 static Transform tr_r2 = {-1, -1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0};
2146                 case 1:  /* P */
2147                         break;
2148                 case 2:  /* I */
2149                         sMoleculeGenerateSymopWithTransform(mp, tr_i, 0);
2150                         break;
2151                 case 3:  /* Rhombohedral obverse on hexagonal axes  */
2152                         n = mp->nsyms;
2153                         sMoleculeGenerateSymopWithTransform(mp, tr_r1, n);
2154                         sMoleculeGenerateSymopWithTransform(mp, tr_r2, n);
2155                         break;
2156                 case 4:  /* F */
2157                         n = mp->nsyms;
2158                         sMoleculeGenerateSymopWithTransform(mp, tr_a, n);
2159                         sMoleculeGenerateSymopWithTransform(mp, tr_b, n);
2160                         sMoleculeGenerateSymopWithTransform(mp, tr_c, n);
2161                         break;
2162                 case 5:  /* A */
2163                         sMoleculeGenerateSymopWithTransform(mp, tr_a, 0);
2164                         break;
2165                 case 6:  /* B */
2166                         sMoleculeGenerateSymopWithTransform(mp, tr_b, 0);
2167                         break;
2168                 case 7:  /* C */
2169                         sMoleculeGenerateSymopWithTransform(mp, tr_c, 0);
2170                         break;
2171         }
2172                 
2173         if (latticeType > 0) {
2174                 static Transform tr_inv = {-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0};
2175                 sMoleculeGenerateSymopWithTransform(mp, tr_inv, 0);
2176         }
2177         
2178         MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
2179         if (nbonds > 0) {
2180                 MoleculeAddBonds(mp, nbonds, bonds);
2181                 free(bonds);
2182         }
2183         mp->nframes = -1;  /*  Should be recalculated later  */
2184         return 0;
2185   panic:
2186         Panic("low memory while reading structure file %s", fname);
2187         return -1; /* not reached */
2188 }
2189
2190 /*  Add one gaussian orbital shell information (not undoable)  */
2191 int
2192 MoleculeAddGaussianOrbitalShell(Molecule *mol, Int sym, Int nprims, Int a_idx)
2193 {
2194         BasisSet *bset;
2195         ShellInfo *shellp;
2196         if (mol == NULL)
2197                 return -1;  /*  Molecule is empty  */
2198         bset = mol->bset;
2199         if (bset == NULL) {
2200                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2201                 if (bset == NULL)
2202                         return -2;  /*  Low memory  */
2203         }
2204         shellp = AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), bset->nshells, NULL);
2205         if (shellp == NULL)
2206                 return -2;  /*  Low memory  */
2207         switch (sym) {
2208                 case 0:  shellp->sym = kGTOType_S;  shellp->ncomp = 1; break;
2209                 case 1:  shellp->sym = kGTOType_P;  shellp->ncomp = 3; break;
2210                 case -1: shellp->sym = kGTOType_SP; shellp->ncomp = 4; break;
2211                 case 2:  shellp->sym = kGTOType_D;  shellp->ncomp = 6; break;
2212                 case -2: shellp->sym = kGTOType_D5; shellp->ncomp = 5; break;
2213                         /*  TODO: Support F/F7 type orbitals  */
2214                         /*      case 3: sp->sym = kGTOtype_F;  sp->ncomp = 10; break;
2215                          case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break; */
2216                 default:
2217                         return -3;  /* Unsupported shell type  */
2218         }
2219         shellp->nprim = nprims;
2220         shellp->a_idx = a_idx;
2221         if (bset->shells < shellp) {
2222                 shellp->m_idx = shellp[-1].m_idx + shellp[-1].ncomp;
2223                 shellp->p_idx = shellp[-1].p_idx + shellp[-1].nprim;
2224         } else {
2225                 shellp->m_idx = 0;
2226                 shellp->p_idx = 0;
2227         }
2228         return 0;
2229 }
2230
2231 /*  Add a set of gaussian primitive coefficients (not undoable)  */
2232 int
2233 MoleculeAddGaussianPrimitiveCoefficients(Molecule *mol, Double exponent, Double contraction, Double contraction_sp)
2234 {
2235         BasisSet *bset;
2236         PrimInfo *primp;
2237         if (mol == NULL)
2238                 return -1;  /*  Molecule is empty  */
2239         bset = mol->bset;
2240         if (bset == NULL) {
2241                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2242                 if (bset == NULL)
2243                         return -2;  /*  Low memory  */
2244         }
2245         primp = AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), bset->npriminfos, NULL);
2246         if (primp == NULL)
2247                 return -2;  /*  Low memory  */
2248         primp->A = exponent;
2249         primp->C = contraction;
2250         primp->Csp = contraction_sp;
2251         return 0;
2252 }
2253
2254 /*  Set MO coefficients for idx-th MO  */
2255 int
2256 MoleculeSetMOCoefficients(Molecule *mol, Int idx, Double energy, Int ncomps, Double *coeffs)
2257 {
2258         BasisSet *bset;
2259         int i, n;
2260         if (mol == NULL)
2261                 return -1;  /*  Molecule is empty  */
2262         bset = mol->bset;
2263         if (bset == NULL) {
2264                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2265                 if (bset == NULL)
2266                         return -2;  /*  Low memory  */
2267         }
2268         if (bset->nmos == 0) {
2269                 if (bset->nshells > 0) {
2270                         /*  Shell info is already set: calculate the number of MOs from there  */
2271                         for (i = n = 0; i < bset->nshells; i++)
2272                                 n += bset->shells[i].ncomp;
2273                         bset->ncomps = n;
2274                 } else if (ncomps > 0) {
2275                         bset->ncomps = ncomps;
2276                 }
2277                 if (bset->rflag == 0)
2278                         bset->nmos = bset->ncomps * 2;
2279                 else
2280                         bset->nmos = bset->ncomps;
2281                 if (bset->nmos <= 0)
2282                         return -3;  /*  Bad or inconsistent number of MOs  */
2283                 bset->mo = (Double *)calloc(sizeof(Double), bset->nmos * bset->ncomps);
2284                 bset->moenergies = (Double *)calloc(sizeof(Double), bset->nmos);
2285                 if (bset->mo == NULL || bset->moenergies == NULL) {
2286                         if (bset->mo != NULL)
2287                                 free(bset->mo);
2288                         if (bset->moenergies != NULL)
2289                                 free(bset->moenergies);
2290                         bset->mo = NULL;
2291                         bset->moenergies = NULL;
2292                         bset->nmos = 0;
2293                         return -2;  /*  Low memory  */
2294                 }
2295         }
2296         if (idx < 0 || idx >= bset->nmos)
2297                 return -4;  /*  Bad MO index  */
2298         if (energy != -1000000)
2299                 bset->moenergies[idx] = energy;
2300         if (ncomps < bset->ncomps)
2301                 return -5;  /*  Insufficient number of data provided  */
2302         memmove(bset->mo + (idx * bset->ncomps), coeffs, sizeof(Double) * bset->ncomps);
2303         if (bset->cns != NULL) {
2304                 /*  Clear the cached values  */
2305                 free(bset->cns);
2306                 bset->cns = NULL;
2307                 bset->ncns = 0;
2308         }
2309         return 0;
2310 }
2311
2312 /*  Allocate BasisSet record. rflag: UHF, 0; RHF, 1; ROHF, 2
2313     ne_alpha: number of alpha electrons, ne_beta: number of beta electrons
2314     The natoms and pos are copied from mol.  */
2315 int
2316 MoleculeAllocateBasisSetRecord(Molecule *mol, Int rflag, Int ne_alpha, Int ne_beta)
2317 {
2318         BasisSet *bset;
2319         int i;
2320         Atom *ap;
2321         if (mol == NULL || mol->natoms == 0)
2322                 return -1;  /*  Molecule is empty  */
2323         bset = mol->bset;
2324         if (bset == NULL) {
2325                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2326                 if (bset == NULL)
2327                         return -2;  /*  Low memory  */
2328         }
2329         if (bset->pos != NULL) {
2330                 free(bset->pos);
2331                 bset->pos = NULL;
2332         }
2333         bset->natoms = mol->natoms;
2334         bset->pos = (Vector *)calloc(sizeof(Vector), bset->natoms);
2335         if (bset->pos == NULL)
2336                 return -2;  /*  Low memory  */
2337         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
2338                 bset->pos[i].x = ap->r.x * kAngstrom2Bohr;
2339                 bset->pos[i].y = ap->r.y * kAngstrom2Bohr;
2340                 bset->pos[i].z = ap->r.z * kAngstrom2Bohr;
2341         }
2342         bset->ne_alpha = ne_alpha;
2343         bset->ne_beta = ne_beta;
2344         bset->rflag = rflag;
2345         return 0;
2346 }
2347
2348 static void
2349 sSeparateTokens(char *inString, char **outPtr, int size)
2350 {
2351         char *p;
2352         int i;
2353         for (i = 0; i < size; i++) {
2354                 p = strtok((i == 0 ? inString : NULL), " \r\n");
2355                 if (p == NULL)
2356                         break;
2357                 outPtr[i] = p;
2358         }
2359         while (i < size) {
2360                 outPtr[i++] = NULL;
2361         }
2362 }
2363
2364 static int
2365 sReadNumberArray(void *basep, Int *countp, Int size, Int num, FILE *fp, int *lnp)
2366 {
2367         char buf[256];
2368         Int i, n;
2369         *((void **)basep) = NULL;
2370         *countp = 0;
2371         if (AssignArray(basep, countp, size, num - 1, NULL) == NULL)
2372                 return 4;  /*  Out of memory  */
2373         n = 0;
2374         while (ReadLine(buf, sizeof buf, fp, lnp) > 0) {
2375                 char *tokens[16], *p;
2376                 sSeparateTokens(buf, tokens, 16);
2377                 for (i = 0; i < 16; i++) {
2378                         if (tokens[i] == NULL)
2379                                 break;
2380                         if (size == sizeof(Int)) {
2381                                 (*((Int **)basep))[n] = strtol(tokens[i], &p, 0);
2382                         } else if (size == sizeof(Double)) {
2383                                 (*((Double **)basep))[n] = strtod(tokens[i], &p);
2384                         } else return -1;  /*  Internal error  */
2385                         if (tokens[i] == p || *p != 0)
2386                                 return 1;  /*  Non-digit character  */
2387                         if (++n == num) {
2388                                 if (i < 15 && tokens[i + 1] != NULL)
2389                                         return 2;  /*  Too many data  */
2390                                 return 0;  /*  All data are successfully read  */
2391                         }
2392                 }
2393         }
2394         return 3;  /*  Unexpected EOF  */                       
2395 }
2396
2397 static int
2398 sSetupGaussianCoefficients(BasisSet *bset)
2399 {
2400         ShellInfo *sp;
2401         PrimInfo *pp;
2402         int i, j, k;
2403         Double *dp, d;
2404         
2405         /*  Cache the contraction coefficients for efficient calculation  */
2406         /*  Sum up the number of components for all primitives  */
2407         for (i = k = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2408                 sp->cn_idx = k;
2409                 k += sp->nprim * sp->ncomp;
2410         }
2411         /*  Allocate memory for the cached values  */
2412         if (AssignArray(&bset->cns, &bset->ncns, sizeof(Double), k - 1, NULL) == NULL)
2413                 return 1;
2414         /*  Iterate over all primitives  */
2415         dp = bset->cns;
2416         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2417                 for (j = 0, pp = bset->priminfos + sp->p_idx; j < sp->nprim; j++, pp++) {
2418                         switch (sp->sym) {
2419                                 case kGTOType_S:
2420                                         // (8 alpha^3/pi^3)^0.25 exp(-alpha r^2)
2421                                         *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2422                                         break;
2423                                 case kGTOType_P:
2424                                         // (128 alpha^5/pi^3)^0.25 [x|y|z]exp(-alpha r^2)
2425                                         d = pp->C * pow(pp->A, 1.25) * 1.425410941;
2426                                         *dp++ = d;
2427                                         *dp++ = d;
2428                                         *dp++ = d;
2429                                         break;
2430                                 case kGTOType_SP:
2431                                         *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2432                                         d = pp->Csp * pow(pp->A, 1.25) * 1.425410941;
2433                                         *dp++ = d;
2434                                         *dp++ = d;
2435                                         *dp++ = d;
2436                                         break;
2437                                 case kGTOType_D:
2438                                         //  xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2)
2439                                         //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2440                                         d = pp->C * pow(pp->A, 1.75);
2441                                         dp[0] = dp[1] = dp[2] = d * 1.645922781;
2442                                         dp[3] = dp[4] = dp[5] = d * 2.850821881;
2443                                         dp += 6;
2444                                         break;
2445                                 case kGTOType_D5:
2446                                         //  3zz-rr:   (128 alpha^7/9pi^3)^0.25 (3zz-rr)exp(-alpha r^2)
2447                                         //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2448                                         //  xx-yy:    (128 alpha^7/pi^3)^0.25 (xx-yy)exp(-alpha r^2)
2449                                         d = pp->C * pow(pp->A, 1.75);
2450                                         dp[0] = d * 0.822961390;
2451                                         dp[1] = dp[2] = dp[4] = d * 2.850821881;
2452                                         dp[3] = d * 1.425410941;
2453                                         dp += 5;
2454                                         break;
2455                         }
2456                 }
2457         }
2458         return 0;
2459 }
2460
2461 int
2462 MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
2463 {
2464         FILE *fp;
2465         char buf[1024];
2466         int lineNumber;
2467         int natoms, nbasis, i, j, k, n, mxbond, retval, ncomps, nprims, nelec;
2468         BasisSet *bset;
2469         ShellInfo *sp;
2470         PrimInfo *pp;
2471         Int nary;
2472         Int *iary;
2473         Double *dary;
2474         Atom *ap;
2475         Vector *vp;
2476         Double w;
2477
2478         if (errbuf == NULL) {
2479                 errbuf = buf;
2480                 errbufsize = 1024;
2481         }
2482         errbuf[0] = 0;
2483         if (mp == NULL)
2484                 mp = MoleculeNew();
2485         bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2486         if (bset == NULL)
2487                 goto panic;
2488         mp->bset = bset;
2489         fp = fopen(fname, "rb");
2490         if (fp == NULL) {
2491                 snprintf(errbuf, errbufsize, "Cannot open file");
2492                 return 1;
2493         }
2494         lineNumber = 0;
2495         natoms = nbasis = -1;
2496         mxbond = 0;
2497         ncomps = 0;
2498         nelec = 0;
2499         nprims = 0;
2500         nary = 0;
2501         iary = NULL;
2502         dary = NULL;
2503         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2504                 char *tokens[16];
2505                 char *p = buf + 41;
2506                 if (lineNumber == 2) {
2507                         /*  job info line  */
2508                         if (buf[10] == 'U')
2509                                 bset->rflag = 0;  /*  UHF  */
2510                         else if (buf[11] == 'O')
2511                                 bset->rflag = 2;  /*  ROHF  */
2512                         else bset->rflag = 1; /*  RHF  */
2513                         continue;
2514                 }
2515                 while (p > buf && *p == ' ')
2516                         p--;
2517                 p[1] = 0;
2518                 sSeparateTokens(buf + 42, tokens, 16);
2519                 if (strcmp(buf, "Number of atoms") == 0) {
2520                         if (tokens[1] == NULL || (natoms = atoi(tokens[1])) <= 0) {
2521                                 snprintf(errbuf, errbufsize, "Line %d: strange number of atoms: %s", lineNumber, tokens[1]);
2522                                 retval = 2;
2523                                 goto cleanup;
2524                         }
2525                         /*  Allocate atom records (all are empty for now)  */
2526                         AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms - 1, NULL);
2527                         /*  Also allocate atom position array for MO calculations  */
2528                         AssignArray(&bset->pos, &bset->natoms, sizeof(Vector), natoms - 1, NULL);
2529                         /*  Also allocate nuclear charge array  */
2530                         bset->nuccharges = (Double *)calloc(sizeof(Double), natoms);
2531                 } else if (strcmp(buf, "Number of electrons") == 0) {
2532                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2533                                 snprintf(errbuf, errbufsize, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
2534                                 retval = 2;
2535                                 goto cleanup;
2536                         }
2537                         nelec = i;
2538                 } else if (strcmp(buf, "Number of alpha electrons") == 0) {
2539                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2540                                 snprintf(errbuf, errbufsize, "Line %d: strange number of alpha electrons: %s", lineNumber, tokens[1]);
2541                                 retval = 2;
2542                                 goto cleanup;
2543                         }
2544                         bset->ne_alpha = i;
2545                 } else if (strcmp(buf, "Number of beta electrons") == 0) {
2546                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2547                                 snprintf(errbuf, errbufsize, "Line %d: strange number of beta electrons: %s", lineNumber, tokens[1]);
2548                                 retval = 2;
2549                                 goto cleanup;
2550                         }
2551                         bset->ne_beta = i;
2552                         if (bset->ne_alpha + bset->ne_beta != nelec) {
2553                                 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);
2554                                 retval = 2;
2555                                 goto cleanup;
2556                         }
2557                 } else if (strcmp(buf, "Number of basis functions") == 0) {
2558                         if (tokens[1] == NULL || (nbasis = atoi(tokens[1])) <= 0) {
2559                                 snprintf(errbuf, errbufsize, "Line %d: strange number of basis functions: %s", lineNumber, tokens[1]);
2560                                 retval = 2;
2561                                 goto cleanup;
2562                         }
2563                 } else if (strcmp(buf, "Atomic numbers") == 0) {
2564                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2565                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2566                                 retval = 2;
2567                                 goto cleanup;
2568                         }
2569                         if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms, fp, &lineNumber) != 0) {
2570                                 snprintf(errbuf, errbufsize, "Line %d: cannot read atomic numbers", lineNumber);
2571                                 retval = 2;
2572                                 goto cleanup;
2573                         }
2574                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2575                                 ap->atomicNumber = iary[i];
2576                                 bset->nuccharges[i] = iary[i];
2577                                 ElementToString(ap->atomicNumber, ap->element);
2578                                 memmove(ap->aname, ap->element, 4);
2579                                 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
2580                                         ap->weight = w;
2581                         }
2582                         free(iary);
2583                         iary = NULL;
2584                 } else if (strcmp(buf, "Nuclear charges") == 0) {
2585                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2586                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2587                                 retval = 2;
2588                                 goto cleanup;
2589                         }
2590                         if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms, fp, &lineNumber) != 0) {
2591                                 snprintf(errbuf, errbufsize, "Line %d: cannot read nuclear charges", lineNumber);
2592                                 retval = 2;
2593                                 goto cleanup;
2594                         }
2595                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2596                                 bset->nuccharges[i] = dary[i];
2597                         }
2598                         free(iary);
2599                         iary = NULL;
2600                 } else if (strcmp(buf, "Current cartesian coordinates") == 0) {
2601                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * 3) {
2602                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of cartesian coordinates: %s", lineNumber, tokens[2]);
2603                                 retval = 2;
2604                                 goto cleanup;
2605                         }
2606                         if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms * 3, fp, &lineNumber) != 0) {
2607                                 snprintf(errbuf, errbufsize, "Line %d: cannot read cartesian coordinates", lineNumber);
2608                                 retval = 2;
2609                                 goto cleanup;
2610                         }
2611                         for (i = 0, ap = mp->atoms, vp = bset->pos; i < natoms; i++, ap = ATOM_NEXT(ap), vp++) {
2612                                 vp->x = dary[i * 3];
2613                                 vp->y = dary[i * 3 + 1];
2614                                 vp->z = dary[i * 3 + 2];
2615                                 ap->r.x = vp->x * kBohr2Angstrom;
2616                                 ap->r.y = vp->y * kBohr2Angstrom;
2617                                 ap->r.z = vp->z * kBohr2Angstrom;
2618                         }
2619                         free(dary);
2620                         dary = NULL;
2621                 } else if (strcmp(buf, "MxBond") == 0) {
2622                         if (tokens[1] == NULL || (mxbond = atoi(tokens[1])) <= 0) {
2623                                 snprintf(errbuf, errbufsize, "Line %d: strange number of bonds per atom: %s", lineNumber, tokens[1]);
2624                                 retval = 2;
2625                                 goto cleanup;
2626                         }
2627                 } else if (strcmp(buf, "IBond") == 0) {
2628                         Int *bonds;
2629                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * mxbond) {
2630                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of bonds: %s", lineNumber, tokens[2]);
2631                                 retval = 2;
2632                                 goto cleanup;
2633                         }
2634                         if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms * mxbond, fp, &lineNumber) != 0) {
2635                                 snprintf(errbuf, errbufsize, "Line %d: cannot read bond information", lineNumber);
2636                                 retval = 2;
2637                                 goto cleanup;
2638                         }
2639                         bonds = (Int *)malloc(sizeof(Int) * (mxbond * 2 + 1));
2640                         for (i = 0; i < natoms; i++) {
2641                                 for (j = k = 0; j < mxbond; j++) {
2642                                         n = iary[i * mxbond + j] - 1;
2643                                         if (n > i) {
2644                                                 /*  Connect atom i and atom n  */
2645                                                 bonds[k++] = i;
2646                                                 bonds[k++] = n;
2647                                         }
2648                                 }
2649                                 if (k > 0) {
2650                                         bonds[k] = kInvalidIndex;
2651                                         MoleculeAddBonds(mp, k / 2, bonds);
2652                                 }
2653                         }
2654                         free(iary);
2655                         free(bonds);
2656                         iary = NULL;
2657                 } else if (strcmp(buf, "Shell types") == 0) {
2658                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0) {
2659                                 snprintf(errbuf, errbufsize, "Line %d: wrong number of shell types: %s", lineNumber, tokens[2]);
2660                                 retval = 2;
2661                                 goto cleanup;
2662                         }
2663                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2664                                 snprintf(errbuf, errbufsize, "Line %d: cannot read shell types", lineNumber);
2665                                 retval = 2;
2666                                 goto cleanup;
2667                         }
2668                         /*  Allocate ShellInfo table and store shell type information  */
2669                         AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), nary - 1, NULL);
2670                         for (i = n = 0, sp = bset->shells; i < nary; i++, sp++) {
2671                                 switch (iary[i]) {
2672                                         case 0:  sp->sym = kGTOType_S;  sp->ncomp = 1; break;
2673                                         case 1:  sp->sym = kGTOType_P;  sp->ncomp = 3; break;
2674                                         case -1: sp->sym = kGTOType_SP; sp->ncomp = 4; break;
2675                                         case 2:  sp->sym = kGTOType_D;  sp->ncomp = 6; break;
2676                                         case -2: sp->sym = kGTOType_D5; sp->ncomp = 5; break;
2677                                                 /*  TODO: Support F/F7 type orbitals  */
2678                                                 /*      case 3: sp->sym = kGTOtype_F;  sp->ncomp = 10; break;
2679                                                  case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break; */
2680                                         default:
2681                                                 snprintf(errbuf, errbufsize, "Line %d: unsupported shell type %d", lineNumber, iary[i]);
2682                                                 retval = 2;
2683                                                 goto cleanup;
2684                                 }
2685                                 sp->m_idx = n;
2686                                 n += sp->ncomp;
2687                         }
2688                         bset->ncomps = ncomps = n;
2689                         free(iary);
2690                         iary = NULL;
2691                 } else if (strcmp(buf, "Number of primitives per shell") == 0) {
2692                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2693                                 snprintf(errbuf, errbufsize, "Line %d: wrong size of the primitive table: %s", lineNumber, tokens[2]);
2694                                 retval = 2;
2695                                 goto cleanup;
2696                         }
2697                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2698                                 snprintf(errbuf, errbufsize, "Line %d: cannot read primitive table", lineNumber);
2699                                 retval = 2;
2700                                 goto cleanup;
2701                         }
2702                         for (i = n = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2703                                 sp->nprim = iary[i];
2704                                 sp->p_idx = n;
2705                                 n += sp->nprim;
2706                         }
2707                         nprims = n;
2708                         free(iary);
2709                         iary = NULL;
2710                 } else if (strcmp(buf, "Shell to atom map") == 0) {
2711                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2712                                 snprintf(errbuf, errbufsize, "Line %d: wrong size of the shell-to-atom map: %s", lineNumber, tokens[2]);
2713                                 retval = 2;
2714                                 goto cleanup;
2715                         }
2716                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2717                                 snprintf(errbuf, errbufsize, "Line %d: cannot read shell-to-atom table", lineNumber);
2718                                 retval = 2;
2719                                 goto cleanup;
2720                         }
2721                         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2722                                 sp->a_idx = iary[i] - 1;
2723                         }
2724                         free(iary);
2725                         iary = NULL;
2726                 } else if (strcmp(buf, "Primitive exponents") == 0) {
2727                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nprims) {
2728                                 snprintf(errbuf, errbufsize, "Line %d: wrong number of primitive exponents: %s", lineNumber, tokens[2]);
2729                                 retval = 2;
2730                                 goto cleanup;
2731                         }
2732                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2733                                 snprintf(errbuf, errbufsize, "Line %d: cannot read primitive exponents", lineNumber);
2734                                 retval = 2;
2735                                 goto cleanup;
2736                         }
2737                         /*  Allocate PrimInfo table  */
2738                         AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), nprims - 1, NULL);
2739                         for (i = 0, pp = bset->priminfos; i < nprims; i++, pp++) {
2740                                 pp->A = dary[i];
2741                         }
2742                         free(dary);
2743                         dary = NULL;
2744                 } else if (strcmp(buf, "Contraction coefficients") == 0) {
2745                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2746                                 snprintf(errbuf, errbufsize, "Line %d: wrong number of contraction coefficients: %s", lineNumber, tokens[2]);
2747                                 retval = 2;
2748                                 goto cleanup;
2749                         }
2750                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2751                                 snprintf(errbuf, errbufsize, "Line %d: cannot read contraction coefficients", lineNumber);
2752                                 retval = 2;
2753                                 goto cleanup;
2754                         }
2755                         for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2756                                 pp->C = dary[i];
2757                         }
2758                         free(dary);
2759                         dary = NULL;
2760                 } else if (strcmp(buf, "P(S=P) Contraction coefficients") == 0) {
2761                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2762                                 snprintf(errbuf, errbufsize, "Line %d: wrong number of P(S=P) contraction coefficients: %s", lineNumber, tokens[2]);
2763                                 retval = 2;
2764                                 goto cleanup;
2765                         }
2766                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2767                                 snprintf(errbuf, errbufsize, "Line %d: cannot read P(S=P) contraction coefficients", lineNumber);
2768                                 retval = 2;
2769                                 goto cleanup;
2770                         }
2771                         for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2772                                 pp->Csp = dary[i];
2773                         }
2774                         free(dary);
2775                         dary = NULL;
2776                 } else if (strcmp(buf, "Alpha Orbital Energies") == 0) {
2777                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2778                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of alpha orbitals: %s", lineNumber, tokens[2]);
2779                                 retval = 2;
2780                                 goto cleanup;
2781                         }
2782                         if (sReadNumberArray(&bset->moenergies, &bset->nmos, sizeof(Double), i, fp, &lineNumber) != 0) {
2783                                 snprintf(errbuf, errbufsize, "Line %d: cannot read alpha orbital energies", lineNumber);
2784                                 retval = 2;
2785                                 goto cleanup;
2786                         }
2787                 } else if (strcmp(buf, "Alpha MO coefficients") == 0) {
2788                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2789                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of alpha MO coefficients: %s", lineNumber, tokens[2]);
2790                                 retval = 2;
2791                                 goto cleanup;
2792                         }
2793                         if (sReadNumberArray(&bset->mo, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2794                                 snprintf(errbuf, errbufsize, "Line %d: cannot read MO coefficients", lineNumber);
2795                                 retval = 2;
2796                                 goto cleanup;
2797                         }
2798                 } else if (strcmp(buf, "Beta Orbital Energies") == 0) {
2799                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2800                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of beta orbitals: %s", lineNumber, tokens[2]);
2801                                 retval = 2;
2802                                 goto cleanup;
2803                         }
2804                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2805                                 snprintf(errbuf, errbufsize, "Line %d: cannot read beta orbital energies", lineNumber);
2806                                 retval = 2;
2807                                 goto cleanup;
2808                         }
2809                         bset->moenergies = (Double *)realloc(bset->moenergies, sizeof(Double) * 2 * ncomps);
2810                         bset->nmos = ncomps * 2;
2811                         bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);
2812                         memmove(bset->moenergies + ncomps, dary, sizeof(Double) * ncomps);
2813                         memset(bset->mo + ncomps * ncomps, 0, sizeof(Double) * ncomps * ncomps);
2814                         free(dary);
2815                         dary = NULL;
2816                 } else if (strcmp(buf, "Beta MO coefficients") == 0) {
2817                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2818                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of beta MO coefficients: %s", lineNumber, tokens[2]);
2819                                 retval = 2;
2820                                 goto cleanup;
2821                         }
2822                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2823                                 snprintf(errbuf, errbufsize, "Line %d: cannot read alpha MO coefficients", lineNumber);
2824                                 retval = 2;
2825                                 goto cleanup;
2826                         }
2827                         bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);  /*  Should be unnecessary, just in case  */
2828                         memmove(bset->mo + ncomps * ncomps, dary, sizeof(Double) * ncomps * ncomps);
2829                         free(dary);
2830                         dary = NULL;
2831                 } else if (strcmp(buf, "Total SCF Density") == 0) {
2832                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * (ncomps + 1) / 2) {
2833                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of SCF densities: %s", lineNumber, tokens[2]);
2834                                 retval = 2;
2835                                 goto cleanup;
2836                         }
2837                         if (sReadNumberArray(&bset->scfdensities, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2838                                 snprintf(errbuf, errbufsize, "Line %d: cannot read SCF densities", lineNumber);
2839                                 retval = 2;
2840                                 goto cleanup;
2841                         }
2842                 }
2843         }
2844         if (mp->natoms == 0) {
2845                 snprintf(errbuf, errbufsize, "Atom information is missing");
2846                 retval = 2;
2847                 goto cleanup;
2848         }
2849         if (bset->shells == NULL || bset->priminfos == NULL) {
2850                 snprintf(errbuf, errbufsize, "Gaussian primitive information is missing");
2851                 retval = 2;
2852                 goto cleanup;
2853         }
2854         if (bset->mo == NULL) {
2855                 snprintf(errbuf, errbufsize, "MO coefficients were not found");
2856                 retval = 2;
2857                 goto cleanup;
2858         }
2859         if (sSetupGaussianCoefficients(bset) != 0) {
2860                 snprintf(errbuf, errbufsize, "Internal error during setup MO calculation");
2861                 retval = 2;
2862                 goto cleanup;
2863         }
2864         mp->nframes = -1;
2865         retval = 0;
2866 cleanup:
2867         fclose(fp);
2868         if (iary != NULL)
2869                 free(iary);
2870         if (dary != NULL)
2871                 free(dary);
2872         if (retval != 0) {
2873                 if (mp->bset != NULL) {
2874                         BasisSetRelease(mp->bset);
2875                         mp->bset = NULL;
2876                 }
2877         }
2878         return retval;
2879 panic:
2880         Panic("low memory while reading fchk file %s", fname);
2881         return -1; /* not reached */    
2882 }
2883
2884 int
2885 MoleculeLoadGamessDatFile(Molecule *mol, const char *fname, char *errbuf, int errbufsize)
2886 {
2887         FILE *fp;
2888         int newmol = 0;
2889         char buf[1024];
2890         int lineNumber, i, j, k, len, natoms = 0;
2891         int nframes = 0;
2892         int n1;
2893         int ival[8];
2894         double dval[8];
2895         char sval[16];
2896         Vector *vbuf = NULL;
2897         IntGroup *ig;
2898         int optimizing = 0, status = 0;
2899
2900         if (errbuf == NULL) {
2901                 errbuf = buf;
2902                 errbufsize = 1024;
2903         }
2904         errbuf[0] = 0;
2905         if (mol == NULL) {
2906                 mol = MoleculeNew();
2907         }
2908         if (mol->natoms == 0)
2909                 newmol = 1;
2910
2911         fp = fopen(fname, "rb");
2912         if (fp == NULL) {
2913                 snprintf(errbuf, errbufsize, "Cannot open file");
2914                 return 1;
2915         }
2916         
2917         /*  ESP is cleared (not undoable!)  */
2918         if (mol->elpots != NULL) {
2919                 free(mol->elpots);
2920                 mol->elpots = NULL;
2921                 mol->nelpots = 0;
2922         }
2923         
2924         lineNumber = 0;
2925         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2926         redo:
2927                 n1 = 0;
2928                 if (strncmp(buf, " $DATA", 6) == 0) {
2929                         /*  Initial geometry  */
2930                         if (!newmol) {
2931                                 vbuf = (Vector *)calloc(sizeof(Vector), mol->natoms);
2932                         }
2933                         i = 0;
2934                         ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Title  */
2935                         ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Symmetry  */
2936                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2937                                 if (strncmp(buf, " $END", 5) == 0)
2938                                         break;
2939                                 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
2940                                         snprintf(errbuf, errbufsize, "Line %d: bad format in $DATA section", lineNumber);
2941                                         return 2;
2942                                 }
2943                                 if (newmol) {
2944                                         Atom a;
2945                                         memset(&a, 0, sizeof(a));
2946                                         strncpy(a.aname, sval, 4);
2947                                         a.r.x = dval[1];
2948                                         a.r.y = dval[2];
2949                                         a.r.z = dval[3];
2950                                         a.atomicNumber = (Int)dval[0];
2951                                         strncpy(a.element, ElementToString(a.atomicNumber, sval), 3);
2952                                         a.type = AtomTypeEncodeToUInt(a.element);
2953                                         a.weight = WeightForAtomicNumber(a.atomicNumber);
2954                                         MoleculeCreateAnAtom(mol, &a, mol->natoms);
2955                                 } else {
2956                                         Atom *ap;
2957                                         if (i >= mol->natoms) {
2958                                                 snprintf(errbuf, errbufsize, "Line %d: too many atoms", lineNumber);
2959                                                 return 3;
2960                                         }
2961                                         if ((ap = ATOM_AT_INDEX(mol->atoms, i))->atomicNumber != dval[0]) {
2962                                                 snprintf(errbuf, errbufsize, "Line %d: atomic number does not match", lineNumber);
2963                                                 return 4;
2964                                         }
2965                                         vbuf[i].x = dval[1];
2966                                         vbuf[i].y = dval[2];
2967                                         vbuf[i].z = dval[3];
2968                                 }
2969                                 /*  Skip until a blank line is found  */
2970                                 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2971                                         for (j = 0; buf[j] == ' '; j++);
2972                                         if (buf[j] == '\n')
2973                                                 break;
2974                                 }
2975                                 i++;
2976                         }
2977                         natoms = i;
2978                         if (!newmol) {
2979                                 /*  Set atom positions  */
2980                                 IntGroup *ig;
2981                                 if (natoms < mol->natoms) {
2982                                         snprintf(errbuf, errbufsize, "Line %d: too few atoms", lineNumber);
2983                                         return 5;
2984                                 }
2985                                 ig = IntGroupNewWithPoints(0, natoms, -1);
2986                                 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, natoms, vbuf);
2987                                 IntGroupRelease(ig);
2988                         }
2989                         if (vbuf == NULL)
2990                                 vbuf = (Vector *)calloc(sizeof(Vector), natoms);
2991                         nframes = MoleculeGetNumberOfFrames(mol);
2992                         if (status < 0)
2993                                 break;
2994                         continue;
2995                 } else if (strstr(buf, "DATA FROM NSERCH") != NULL || (strstr(buf, "RESULTS FROM SUCCESSFUL") != NULL && (n1 = 1))) {
2996                         /*  Skip until the separator line is read (three or four lines)  */
2997                         i = 0;
2998                         do {
2999                                 if (i++ >= 4) {
3000                                         snprintf(errbuf, errbufsize, "Line %d: the separator line at the top of the coordinates is not found: bad format?", lineNumber);
3001                                         return 6;
3002                                 }
3003                                 ReadLine(buf, sizeof buf, fp, &lineNumber);
3004                         } while (strstr(buf, "----------------------------") == NULL);
3005                         for (i = 0; i < natoms; i++) {
3006                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3007                                         snprintf(errbuf, errbufsize, "Unexpected end of file in reading NSERCH data");
3008                                         return 6;
3009                                 }
3010                                 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3011                                         snprintf(errbuf, errbufsize, "Line %d: bad format in NSERCH coordinate data", lineNumber);
3012                                         return 7;
3013                                 }
3014                                 vbuf[i].x = dval[1];
3015                                 vbuf[i].y = dval[2];
3016                                 vbuf[i].z = dval[3];
3017                         }
3018                         ig = IntGroupNewWithPoints(nframes, 1, -1);
3019                         MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, natoms, vbuf, 0, NULL);
3020                         IntGroupRelease(ig);
3021                         nframes++;
3022                         if (n1 == 0)
3023                                 optimizing = 1;  /*  Flag to skip reading the VEC group  */
3024                         else
3025                                 optimizing = 0;
3026                         continue;
3027                 } else if (strstr(buf, "E(UHF)") != NULL || (strstr(buf, "E(RHF)") != NULL && (n1 = 1)) || (strstr(buf, "E(ROHF)") != NULL && (n1 = 2))) {
3028                         if (mol->bset == NULL) {
3029                                 i = MoleculeAllocateBasisSetRecord(mol, n1, 0, 0);
3030                                 if (i != 0) {
3031                                         snprintf(errbuf, errbufsize, "Line %d: cannot allocate basis set internal buffer", lineNumber);
3032                                         return 8;
3033                                 }
3034                         }
3035                 } else if (strncmp(buf, " $VEC", 5) == 0) {
3036                         Double *coeffs;
3037                         /*  Read the vec group  */
3038                         if (mol->bset == NULL || mol->bset->ncomps == 0)
3039                                 continue;  /*  Just ignore  */
3040                         if (optimizing)
3041                                 continue;  /*  Ignore VEC group during optimization  */
3042                         coeffs = (Double *)calloc(sizeof(Double), mol->bset->ncomps);
3043                         if (coeffs == NULL) {
3044                                 snprintf(errbuf, errbufsize, "Line %d: low memory during $VEC", lineNumber);
3045                                 return 9;
3046                         }
3047                         i = k = 0;
3048                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3049                                 len = strlen(buf);
3050                                 if (strncmp(buf, " $END", 5) == 0)
3051                                         break;
3052                                 while ((j = 5 + (k % 5) * 15) <= len && buf[j] != 0 && buf[j] != '\n') {
3053                                         strncpy(sval, buf + j, 15);
3054                                         sval[15] = 0;
3055                                         coeffs[k] = strtod(sval, NULL);
3056                                         k++;
3057                                         if ((k % 5) == 0)
3058                                                 break;
3059                                 }
3060                                 if (k < mol->bset->ncomps)
3061                                         continue;
3062                                 j = MoleculeSetMOCoefficients(mol, i, -1000000, k, coeffs);
3063                                 if (j != 0) {
3064                                         snprintf(errbuf, errbufsize, "Line %d: cannot set coefficients for MO %d", lineNumber, i + 1);
3065                                         free(coeffs);
3066                                         return 10;
3067                                 }
3068                                 i++;
3069                                 k = 0;
3070                         }
3071                         if (status < 0)
3072                                 break;
3073                         continue;
3074                 } else if ((strstr(buf, "ELECTRIC POTENTIAL") != NULL || strstr(buf, "ELECTROSTATIC POTENTIAL") != NULL) && strstr(buf, "ELPOTT") != NULL) {
3075                         i = 0;
3076                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3077                                 Elpot *ep;
3078                                 if (strstr(buf, "TOTAL NUMBER OF GRID POINTS") != NULL)
3079                                         continue;
3080                                 if (sscanf(buf, "%d %lf %lf %lf %lf", &ival[0], &dval[0], &dval[1], &dval[2], &dval[3]) < 5)
3081                                         break;
3082                                 ep = AssignArray(&mol->elpots, &mol->nelpots, sizeof(Elpot), i, NULL);
3083                                 ep->pos.x = dval[0];
3084                                 ep->pos.y = dval[1];
3085                                 ep->pos.z = dval[2];
3086                                 ep->esp = dval[3];
3087                                 i++;
3088                         }
3089                         if (status > 0)
3090                                 goto redo;  /*  This section has no end line, so the last line should be processed again  */
3091                         else break;    /*  End of file encountered or interrupted */
3092                 }  /*  TODO: read MOLPLT info if present  */
3093         }
3094         if (status < 0) {
3095                 snprintf(errbuf, errbufsize, "User interrupt at line %d", lineNumber);
3096                 return 11;
3097         }
3098         if (vbuf != NULL)
3099                 free(vbuf);
3100         if (newmol && mol->nbonds == 0) {
3101                 /*  Guess bonds  */
3102                 Int nbonds, *bonds;
3103                 MoleculeGuessBonds(mol, 1.2, &nbonds, &bonds);
3104                 if (nbonds > 0) {
3105                         MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds);
3106                         free(bonds);
3107                 }
3108         }
3109         return 0;
3110 }
3111
3112 int
3113 MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
3114 {
3115         int retval;
3116         if (ftype == NULL || *ftype == 0) {
3117                 const char *cp;
3118                 cp = strrchr(fname, '.');
3119                 if (cp != NULL)
3120                         ftype = cp + 1;
3121                 else {
3122                         cp = guessMoleculeType(fname);
3123                         if (strcmp(cp, "???") != 0)
3124                                 ftype = cp;
3125                 }
3126         }
3127         if (strcasecmp(ftype, "pdb") == 0) {
3128                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
3129         }
3130         if (retval != 0) {
3131                 /*  Try all formats once again  */
3132                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
3133         }
3134         return retval;
3135 }
3136
3137 int
3138 MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3139 {
3140         FILE *fp;
3141         char buf[1024];
3142         char *p;
3143         int lineNumber;
3144         int i, j, new_unit, retval;
3145         Atom *ap;
3146         IntGroup *ig;
3147         Vector *vp = NULL;
3148         Int ibuf[12];
3149         Int entries = 0;
3150         retval = 0;
3151         if (errbuf == NULL) {
3152                 errbuf = buf;
3153                 errbufsize = 1024;
3154         }
3155         errbuf[0] = 0;
3156         fp = fopen(fname, "rb");
3157         if (fp == NULL) {
3158                 snprintf(errbuf, errbufsize, "Cannot open file");
3159                 return -1;
3160         }
3161 /*      flockfile(fp); */
3162         if (mp->natoms == 0)
3163                 new_unit = 1;
3164         else {
3165                 /*  Allocate buffer for undo-capable modification  */
3166                 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
3167                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3168                         /*  Retain current position if the atom info is missing in the input file  */
3169                         vp[i] = ap->r;
3170                 }
3171                 new_unit = 0;
3172         }
3173         lineNumber = 0;
3174         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3175                 if (strncmp(buf, "END", 3) == 0)
3176                         break;
3177                 if (strncmp(buf, "HETATM", 6) == 0 || strncmp(buf, "ATOM", 4) == 0) {
3178                         struct {
3179                                 Int serial, intCharge, resSeq;
3180                                 Vector r;
3181                                 Double occ, temp;
3182                                 char segName[5], resName[4], atomName[5], resSeqStr[5], atomType[3], element[3], occStr[6];
3183                         } w;
3184                         memset(&w, 0, sizeof(w));
3185                         ReadFormat(buf, "x6 I5 x1 S4 x1 S3 x1 x1 S4 x1 x3 F8 F8 F8 S6 F6 x6 S4 S2 I2",
3186                                 &w.serial, w.atomName, w.resName, w.resSeqStr, &w.r.x, &w.r.y, &w.r.z,
3187                                 w.occStr, &w.temp, w.segName, w.element, &w.intCharge);
3188                         if (w.atomName[0] == 0) {
3189                                 continue;  /*  Atom name is empty  */
3190                         }
3191                         /*  A workaround for residue number >= 10000 (XPLOR style)  */
3192                         if (w.resSeqStr[0] >= 'A' && w.resSeqStr[0] <= 'Z') {
3193                                 w.resSeq = (w.resSeqStr[0] - 'A' + 10) * 1000 + atoi(w.resSeqStr + 1);
3194                         } else {
3195                                 w.resSeq = atoi(w.resSeqStr);
3196                         }
3197                         if (w.element[0] == 0) {
3198                                 /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
3199                                 for (p = w.atomName; *p != 0; p++) {
3200                                         if (isalpha(*p) && *p != '_') {
3201                                                 w.element[0] = toupper(*p);
3202                                                 if (isalpha(p[1]) && p[1] != '_') {
3203                                                         w.element[1] = toupper(p[1]);
3204                                                         w.element[2] = 0;
3205                                                 } else {
3206                                                         w.element[1] = 0;
3207                                                 }
3208                                                 break;
3209                                         }
3210                                 }
3211                         }
3212                         if (w.occStr[0] == 0)
3213                                 w.occ = 1.0;
3214                         else
3215                                 w.occ = atof(w.occStr);
3216                         if (w.serial <= 0) {
3217                                 snprintf(errbuf, errbufsize, "line %d: non-positive atom number %d", lineNumber, w.serial);
3218                                 retval = 1;
3219                                 goto abort;
3220                         }
3221                         w.serial--;  /*  The internal atom number is 0-based  */
3222                         if (w.serial >= mp->natoms) {
3223                                 if (new_unit) {
3224                                         /*  Create a new atom entry  */
3225                                         ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, w.serial, NULL);
3226                                 } else {
3227                                         snprintf(errbuf, errbufsize, "line %d: the atom number %d does not exist in the structure file", lineNumber, w.serial+1);
3228                                         retval = 1;
3229                                         goto abort;
3230                                 }
3231                         }
3232                         if (new_unit) {
3233                                 ap = ATOM_AT_INDEX(mp->atoms, w.serial);
3234                                 ap->r = w.r;
3235                                 ap->occupancy = w.occ;
3236                                 ap->tempFactor = w.temp;
3237                                 if (w.segName[0] == 0)
3238                                         strncpy(w.segName, "MAIN", 4);
3239                                 strncpy(ap->segName, w.segName, 4);
3240                                 ap->resSeq = w.resSeq;
3241                                 strncpy(ap->resName, w.resName, 4);
3242                                 strncpy(ap->aname, w.atomName, 4);
3243                                 strncpy(ap->element, w.element, 2);
3244                                 ap->intCharge = w.intCharge;
3245                                 if (ap->resSeq > 0) {
3246                                         if (ap->resSeq < mp->nresidues) {
3247                                                 /*  Update the resName according to residues[]  */
3248                                                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
3249                                         } else {
3250                                                 /*  Register the resName to residues[]  */
3251                                                 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, w.resName);
3252                                         }
3253                                 } else {
3254                                         ap->resSeq = 0;
3255                                         strcpy(ap->resName, "XXX");
3256                                         if (mp->nresidues == 0)
3257                                                 AssignArray(&mp->residues, &mp->nresidues, 4, 0, ap->resName);
3258                                 }
3259                                 i = ElementToInt(ap->element);
3260                                 if (i >= 0)
3261                                         ap->weight = gElementParameters[i].weight;
3262                         } else {
3263                                 /*  Not a new unit: only the atom position is updated  */
3264                                 vp[w.serial] = w.r;
3265                         }
3266                         entries++;
3267                 } else if (strncmp(buf, "CONECT", 6) == 0 && new_unit) {
3268                         i = ReadFormat(buf, "x6 I5I5I5I5I5I5I5I5I5I5I5I5",
3269                                 ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
3270                                 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7,
3271                                 ibuf + 8, ibuf + 9, ibuf + 10, ibuf + 11);
3272                         if (i >= 2) {
3273                                 Int bbuf[25];
3274                                 int bi;
3275                                 for (j = 0; j < i; j++) {
3276                                         if (ibuf[j] < 0 || ibuf[j] > mp->natoms) {
3277                                                 snprintf(errbuf, errbufsize, "line %d: The CONECT record contains non-existent atom %d", lineNumber, ibuf[j]);
3278                                                 retval = 1;
3279                                                 goto abort;
3280                                         } else if (ibuf[j] == 0)
3281                                                 break;
3282                                 }
3283                                 i = j;
3284                                 if (i < 2)
3285                                         continue;
3286                                 for (j = 1, bi = 0; j < i; j++) {
3287                                         if (ibuf[0] < ibuf[j]) {
3288                                                 bbuf[bi * 2] = ibuf[0] - 1;
3289                                                 bbuf[bi * 2 + 1] = ibuf[j] - 1;
3290                                                 bi++;
3291                                         }
3292                                 }
3293                                 if (bi == 0)
3294                                         continue;
3295                                 bbuf[bi * 2] = -1;
3296                                 retval = MoleculeAddBonds(mp, bi, bbuf);
3297                                 if (retval < 0) {
3298                                         snprintf(errbuf, errbufsize, "line %d: bad bond specification", lineNumber);
3299                                         retval = 1;
3300                                         goto abort;
3301                                 }
3302                         }
3303                 }
3304         }
3305 /*      funlockfile(fp); */
3306         fclose(fp);
3307         if (new_unit) {
3308                 /*  Renumber atoms if some atom number is unoccupied  */
3309                 int *old2new, oldidx, newidx;
3310                 old2new = (int *)calloc(sizeof(int), mp->natoms);
3311                 if (old2new == NULL) {
3312                         snprintf(errbuf, errbufsize, "Out of memory");
3313                         retval = 1;
3314                         goto abort;
3315                 }
3316                 for (oldidx = newidx = 0; oldidx < mp->natoms; oldidx++) {
3317                         ap = ATOM_AT_INDEX(mp->atoms, oldidx);
3318                         if (ap->aname[0] != 0) {
3319                                 old2new[oldidx] = newidx;
3320                                 if (oldidx > newidx)
3321                                         memmove(ATOM_AT_INDEX(mp->atoms, newidx), ap, gSizeOfAtomRecord);
3322                                 newidx++;
3323                         }
3324                 }
3325                 mp->natoms = newidx;
3326                 if (oldidx > newidx) {
3327                         /*  Renumber the connects and bonds  */
3328                         Int *cp;
3329                         for (i = 0; i < mp->natoms; i++) {
3330                                 ap = ATOM_AT_INDEX(mp->atoms, i);
3331                                 cp = AtomConnects(ap);
3332                                 for (j = 0; j < ap->nconnects; j++) {
3333                                         cp[j] = old2new[cp[j]];
3334                                 }
3335                         }
3336                         for (i = 0; i < mp->nbonds * 2; i++) {
3337                                 mp->bonds[i] = old2new[mp->bonds[i]];
3338                         }
3339                 }
3340                 retval = MoleculeRebuildTablesFromConnects(mp);
3341                 if (retval != 0) {
3342                         /*  This error may not happen  */
3343                         snprintf(errbuf, errbufsize, "Cannot build angle/dihedral/improper tables");
3344                         retval = 1;
3345                         goto abort;
3346                 }
3347                 /*  Undo action: delete all atoms  */
3348                 {
3349                         MolAction *act;
3350                         ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3351                         act = MolActionNew(gMolActionUnmergeMolecule, ig);
3352                         act->frame = mp->cframe;
3353                         MolActionCallback_registerUndo(mp, act);
3354                         MolActionRelease(act);
3355                         IntGroupRelease(ig);
3356                 }
3357         } else {
3358                 /*  Set the new atom positions  */
3359                 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3360                 MolActionCreateAndPerform(mp, gMolActionSetAtomPositions, ig, mp->natoms, vp);
3361                 IntGroupRelease(ig);
3362                 free(vp);
3363                 vp = NULL;
3364         }
3365         mp->nframes = -1;  /*  Should be recalculated later  */
3366         if (entries == 0)
3367                 return 1;  /*  No atoms  */
3368         return 0;
3369         abort:
3370         if (fp != NULL) {
3371         /*      funlockfile(fp); */
3372                 fclose(fp);
3373         }
3374         if (vp != NULL)
3375                 free(vp);
3376         if (entries == 0)
3377                 return 1;  /*  Maybe different format?  */
3378         return retval;
3379 }
3380
3381 int
3382 MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3383 {
3384         DcdRecord dcd;
3385         SFloat32 *xp, *yp, *zp;
3386         Vector *vp, *cp;
3387         IntGroup *ig;
3388         int n;
3389         errbuf[0] = 0;
3390         if (mp == NULL || mp->natoms == 0) {
3391                 snprintf(errbuf, errbufsize, "Molecule is empty");
3392                 return 1;
3393         }
3394         n = DcdOpen(fname, &dcd);
3395         if (n != 0) {
3396                 switch (n) {
3397                         case -2: snprintf(errbuf, errbufsize, "Cannot open file"); break;
3398                         case 1:  snprintf(errbuf, errbufsize, "Premature EOF encountered"); break;
3399                         case 2:  snprintf(errbuf, errbufsize, "Bad block length of the first section"); break;
3400                         case 3:  snprintf(errbuf, errbufsize, "\"CORD\" signature is missing"); break;
3401                         case 4:  snprintf(errbuf, errbufsize, "Bad termination of the first section"); break;
3402                         case 5:  snprintf(errbuf, errbufsize, "The title section is not correct"); break;
3403                         case 6:  snprintf(errbuf, errbufsize, "The atom number section is not correct"); break;
3404                         default: snprintf(errbuf, errbufsize, "Read error in dcd file"); break;
3405                 }
3406         } else {
3407                 if (dcd.natoms == 0)
3408                         snprintf(errbuf, errbufsize, "No atoms were found in the dcd file");
3409                 else if (dcd.nframes == 0)
3410                         snprintf(errbuf, errbufsize, "No frames were found in the dcd file");
3411         }
3412         if (errbuf[0] != 0) {
3413                 if (n == 0)
3414                         DcdClose(&dcd);
3415                 return 1;
3416         }
3417
3418         vp = (Vector *)calloc(sizeof(Vector), mp->natoms * dcd.nframes);
3419         if (dcd.nextra)
3420                 cp = (Vector *)calloc(sizeof(Vector), dcd.nframes * 4);
3421         else cp = NULL;
3422         xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3423         yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3424         zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3425         ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mp), dcd.nframes, -1);
3426         if (vp == NULL || xp == NULL || yp == NULL || zp == NULL || ig == NULL) {
3427                 snprintf(errbuf, errbufsize, "Cannot allocate memory");
3428                 if (vp) free(vp);
3429                 if (cp) free(cp);
3430                 if (xp) free(xp);
3431                 if (yp) free(yp);
3432                 if (zp) free(zp);
3433                 if (ig) IntGroupRelease(ig);
3434                 return 1;
3435         }
3436         for (n = 0; n < dcd.nframes; n++) {
3437                 int i;
3438                 Vector *vpp;
3439                 SFloat32 dcdcell[6];
3440                 if (DcdReadFrame(&dcd, n, xp, yp, zp, dcdcell)) {
3441                         snprintf(errbuf, errbufsize, "Read error in dcd file");
3442                         goto exit;
3443                 }
3444                 for (i = 0, vpp = &vp[n * mp->natoms]; i < dcd.natoms && i < mp->natoms; i++, vpp++) {
3445                         vpp->x = xp[i];
3446                         vpp->y = yp[i];
3447                         vpp->z = zp[i];
3448                 }
3449                 if (cp != NULL) {
3450                         Double sing;
3451                         vpp = &cp[n * 4];
3452                         /*  dcdcell = {a, gamma, b, beta, alpha, c} */
3453                         /*  angles are described either in cosines (Charmm and NAMD > 2.5) or degrees (NAMD 2.5)  */
3454                         if (dcdcell[1] < -1.0 || dcdcell[1] > 1.0 || dcdcell[3] < -1.0 || dcdcell[3] > 1.0 || dcdcell[4] < -1.0 || dcdcell[4] > 1.0) {
3455                                 dcdcell[4] = cos(dcdcell[4] * kDeg2Rad);  /*  cos(alpha)  */
3456                                 dcdcell[3] = cos(dcdcell[3] * kDeg2Rad);  /*  cos(beta)  */
3457                                 dcdcell[1] = cos(dcdcell[1] * kDeg2Rad);  /*  cos(gamma)  */
3458                         }
3459                         /*  a axis lies along the cartesian x axis  */
3460                         sing = sqrt(1 - dcdcell[1] * dcdcell[1]);
3461                         vpp[0].x = dcdcell[0];
3462                         vpp[0].y = 0;
3463                         vpp[0].z = 0;
3464                         vpp[1].x = dcdcell[2] * dcdcell[1];
3465                         vpp[1].y = dcdcell[2] * sing;
3466                         vpp[1].z = 0;
3467                         vpp[2].x = dcdcell[5] * dcdcell[3];
3468                         vpp[2].y = dcdcell[5] * (dcdcell[4] - dcdcell[3] * dcdcell[1]) / sing;
3469                         vpp[2].z = sqrt(dcdcell[5] * dcdcell[5] - vpp[2].x * vpp[2].x - vpp[2].y * vpp[2].y);
3470                         vpp[3].x = vpp[3].y = vpp[3].z = 0.0;
3471                         if (mp->cell == NULL) {
3472                                 /*  Create periodicity if not present  */
3473                                 MolActionCreateAndPerform(mp, gMolActionSetBox, &vpp[0], &vpp[1], &vpp[2], &vpp[3], 7);
3474                         }
3475                 }
3476         }
3477         if (MolActionCreateAndPerform(mp, gMolActionInsertFrames, ig, mp->natoms * dcd.nframes, vp, (cp == NULL ? 0 : dcd.nframes * 4), cp) != 0)
3478                 snprintf(errbuf, errbufsize, "Cannot insert frames");
3479         mp->startStep = dcd.nstart;
3480         mp->stepsPerFrame = dcd.ninterval;
3481         mp->psPerStep = dcd.delta;
3482 exit:
3483         DcdClose(&dcd);
3484         if (cp != NULL)
3485                 free(cp);
3486         free(vp);
3487         free(xp);
3488         free(yp);
3489         free(zp);
3490         IntGroupRelease(ig);
3491         if (errbuf[0] == 0)
3492                 return 0;
3493         else return 1;
3494 }
3495
3496 int
3497 MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3498 {
3499         FILE *fp;
3500         char buf[1024];
3501         int lineNumber;
3502         int i, retval;
3503         Vector v[3], vv;
3504         double d[3];
3505         int n, flag;
3506         char flags[3];
3507         fp = fopen(fname, "rb");
3508         if (fp == NULL) {
3509                 snprintf(errbuf, errbufsize, "Cannot open file");
3510                 return -1;
3511         }
3512         errbuf[0] = 0;
3513         lineNumber = 0;
3514         retval = 0;
3515         flags[0] = flags[1] = flags[2] = 0;
3516         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3517                 if (strncmp(buf, "Bounding box:", 13) == 0) {
3518                         for (i = 0; i < 3; i++) {
3519                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3520                                         snprintf(errbuf, errbufsize, "line %d: missing %d component of the bounding box", lineNumber, i + 1);
3521                                         retval = 1;
3522                                         goto abort;
3523                                 }
3524                                 n = sscanf(buf, "%lf %lf %lf %d", &d[0], &d[1], &d[2], &flag);
3525                                 if (n < 3) {
3526                                         vv.x = vv.y = vv.z = 0.0;
3527                                         switch (i) {
3528                                                 case 0: vv.x = d[0]; break;
3529                                                 case 1: vv.y = d[0]; break;
3530                                                 case 2: vv.z = d[0]; break;
3531                                         }
3532                                         if (n == 1 || (n == 2 && d[1] != 0.0))
3533                                                 flags[i] = 1;
3534                                 } else {
3535                                         vv.x = d[0];
3536                                         vv.y = d[1];
3537                                         vv.z = d[2];
3538                                         if (n == 4)
3539                                                 flags[i] = (flag != 0);
3540                                         else
3541                                                 flags[i] = (VecLength2(vv) != 0);
3542                                 }
3543                                 v[i] = vv;
3544                         }
3545                         if (mp->cell != NULL)
3546                                 vv = mp->cell->origin;
3547                         else
3548                                 vv.x = vv.y = vv.z = 0.0;
3549                         MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags);
3550                 } else if (strncmp(buf, "Bounding box origin:", 20) == 0) {
3551                         if (mp->cell != NULL) {
3552                                 v[0] = mp->cell->axes[0];
3553                                 v[1] = mp->cell->axes[1];
3554                                 v[2] = mp->cell->axes[2];
3555                                 memmove(flags, mp->cell->flags, 3);
3556                         } else {
3557                                 v[0].x = 1.0; v[0].y = v[0].z = 0.0;
3558                                 v[1].y = 1.0; v[1].x = v[1].z = 0.0;
3559                                 v[2].z = 1.0; v[2].x = v[2].y = 0.0;
3560                                 flags[0] = flags[1] = flags[2] = 1.0;
3561                         }
3562                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0 || (n = sscanf(buf, "%lf %lf %lf", &d[0], &d[1], &d[2]) < 3)) {
3563                                 snprintf(errbuf, errbufsize, "line %d: wrong format for the bounding box origin", lineNumber);
3564                                 retval = 1;
3565                                 goto abort;
3566                         }
3567                         vv.x = d[0];
3568                         vv.y = d[1];
3569                         vv.z = d[2];
3570                         MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags);
3571                 }
3572         }
3573         fclose(fp);
3574         return 0;
3575 abort:
3576         if (fp != NULL)
3577                 fclose(fp);
3578         return retval;
3579 }
3580                         
3581 int
3582 MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
3583 {
3584         int retval;
3585         if (ftype == NULL || *ftype == 0) {
3586                 const char *cp;
3587                 cp = strrchr(fname, '.');
3588                 if (cp != NULL)
3589                         ftype = cp + 1;
3590                 else {
3591                         cp = guessMoleculeType(fname);
3592                         if (strcmp(cp, "???") != 0)
3593                                 ftype = cp;
3594                 }
3595         }
3596         if (strcasecmp(ftype, "psf") == 0) {
3597                 retval = MoleculeWriteToPsfFile(mp, fname, errbuf, errbufsize);
3598         } else if (strcasecmp(ftype, "pdb") == 0) {
3599                 retval = MoleculeWriteToPdbFile(mp, fname, errbuf, errbufsize);
3600         } else if (strcasecmp(ftype, "tep") == 0) {
3601                 retval = MoleculeWriteToTepFile(mp, fname, errbuf, errbufsize);
3602         } else {
3603                 snprintf(errbuf, errbufsize, "The file format should be specified");
3604                 retval = 1;
3605         }
3606         if (retval == 0)
3607                 MoleculeSetPath(mp, fname);
3608         return retval;
3609 }
3610
3611 int
3612 MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3613 {
3614         FILE *fp;
3615         int i, j, k, n1, n2, n3, n_aniso;
3616         Atom *ap;
3617         char bufs[6][8];
3618
3619         fp = fopen(fname, "wb");
3620         if (fp == NULL) {
3621                 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
3622                 return 1;
3623         }
3624         errbuf[0] = 0;
3625
3626         fprintf(fp, "!:atoms\n");
3627         fprintf(fp, "! idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge\n");
3628         n1 = n2 = n3 = n_aniso = 0;
3629         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3630                 strncpy(bufs[0], ap->segName, 4);
3631                 bufs[0][4] = 0;
3632                 strncpy(bufs[1], ap->resName, 4);
3633                 bufs[1][4] = 0;
3634                 strncpy(bufs[2], ap->aname, 4);
3635                 bufs[2][4] = 0;
3636                 AtomTypeDecodeToString(ap->type, bufs[3]);
3637                 bufs[3][6] = 0;
3638                 strncpy(bufs[4], ap->element, 4);
3639                 bufs[4][2] = 0;
3640                 for (j = 0; j < 5; j++) {
3641                         if (bufs[j][0] == 0) {
3642                                 bufs[j][0] = '_';
3643                                 bufs[j][1] = 0;
3644                         }
3645                         for (k = 0; k < 6; k++) {
3646                                 if (bufs[j][k] == 0)
3647                                         break;
3648                                 if (bufs[j][k] > 0 && bufs[j][k] < ' ')
3649                                         bufs[j][k] = '_';
3650                         }
3651                 }
3652                 if (SYMOP_ALIVE(ap->symop))
3653                         n1++;
3654                 if (ap->fix_force != 0)
3655                         n2++;
3656                 if (ap->mm_exclude || ap->periodic_exclude)
3657                         n3++;
3658                 if (ap->aniso != NULL)
3659                         n_aniso++;
3660                 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);
3661         }
3662         fprintf(fp, "\n");
3663         
3664         if (n1 > 0) {
3665                 fprintf(fp, "!:atoms_symop\n");
3666                 fprintf(fp, "! idx symop symbase\n");
3667                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3668                         int n;
3669                         n = ap->symop.sym * 1000000 + ap->symop.dx * 10000 + ap->symop.dy * 100 + ap->symop.dz;
3670                         fprintf(fp, "%d %d %d\n", i, n, ap->symbase);
3671                 }
3672                 fprintf(fp, "\n");
3673         }
3674         
3675         if (n2 > 0) {
3676                 fprintf(fp, "!:atoms_fix\n");
3677                 fprintf(fp, "! idx fix_force fix_pos\n");
3678                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3679                         fprintf(fp, "%d %f %f %f %f\n", i, ap->fix_force, ap->fix_pos.x, ap->fix_pos.y, ap->fix_pos.z);
3680                 }
3681                 fprintf(fp, "\n");
3682         }
3683         
3684         if (n3 > 0) {
3685                 fprintf(fp, "!:mm_exclude\n");
3686                 fprintf(fp, "! idx mm_exclude periodic_exclude\n");
3687                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3688                         fprintf(fp, "%d %d %d\n", i, ap->mm_exclude, ap->periodic_exclude);
3689                 }
3690                 fprintf(fp, "\n");
3691         }
3692         
3693         if ((n1 = MoleculeGetNumberOfFrames(mp)) > 0)
3694                 n2 = mp->cframe;
3695         else
3696                 n2 = 0;
3697         for (i = 0; (i == n2 || i < n1); i++) {
3698                 fprintf(fp, "!:positions ; frame %d\n", i);
3699                 fprintf(fp, "! idx x y z [sx sy sz]\n");
3700                 for (j = 0, ap = mp->atoms; j < mp->natoms; j++, ap = ATOM_NEXT(ap)) {
3701                         Vector *vp;
3702                         Byte sig_flag = 0;
3703                         if (i != n2 && i < ap->nframes)
3704                                 vp = ap->frames + i;
3705                         else {
3706                                 vp = &(ap->r);
3707                                 if (ap->sigma.x != 0.0 || ap->sigma.y != 0.0 || ap->sigma.z != 0.0)
3708                                         sig_flag = 1;
3709                         }
3710                         fprintf(fp, "%d %.8f %.8f %.8f", j, vp->x, vp->y, vp->z);
3711                         if (sig_flag) {
3712                                 fprintf(fp, " %.8f %.8f %.8f", ap->sigma.x, ap->sigma.y, ap->sigma.z);
3713                         }
3714                         fprintf(fp, "\n");
3715                 }
3716                 fprintf(fp, "\n");
3717         }
3718         
3719         if (mp->nbonds > 0) {
3720                 fprintf(fp, "!:bonds\n");
3721                 fprintf(fp, "! from1 to1 from2 to2 from3 to3 from4 to4\n");
3722                 for (i = 0; i < mp->nbonds; i++) {
3723                         fprintf(fp, "%d %d%c", mp->bonds[i * 2], mp->bonds[i * 2 + 1], (i % 4 == 3 || i == mp->nbonds - 1 ? '\n' : ' '));
3724                 }
3725                 fprintf(fp, "\n");
3726         }
3727         
3728         if (mp->nangles > 0) {
3729                 fprintf(fp, "!:angles\n");
3730                 fprintf(fp, "! a1 b1 c1 a2 b2 c2 a3 b3 c3\n");
3731                 for (i = 0; i < mp->nangles; i++) {
3732                         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' : ' '));
3733                 }
3734                 fprintf(fp, "\n");
3735         }
3736         
3737         if (mp->ndihedrals > 0) {
3738                 fprintf(fp, "!:dihedrals\n");
3739                 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3740                 for (i = 0; i < mp->ndihedrals; i++) {
3741                         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' : ' '));
3742                 }
3743                 fprintf(fp, "\n");
3744         }
3745         
3746         if (mp->nimpropers > 0) {
3747                 fprintf(fp, "!:impropers\n");
3748                 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3749                 for (i = 0; i < mp->nimpropers; i++) {
3750                         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' : ' '));
3751                 }
3752                 fprintf(fp, "\n");
3753         }
3754         
3755         if (mp->cell != NULL) {
3756                 fprintf(fp, "!:xtalcell\n");
3757                 fprintf(fp, "! a b c alpha beta gamma\n");
3758                 fprintf(fp, "! This information is redundant and overridden by the following periodic_box info\n");
3759                 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]);
3760                 fprintf(fp, "\n");
3761
3762                 fprintf(fp, "!:periodic_box\n");
3763                 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz; fa fb fc [sigma; sa sb sc s_alpha s_beta s_gamma]\n");
3764                 for (i = 0; i < 3; i++)
3765                         fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->axes[i].x, mp->cell->axes[i].y, mp->cell->axes[i].z);
3766                 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->origin.x, mp->cell->origin.y, mp->cell->origin.z);
3767                 fprintf(fp, "%d %d %d%s\n", mp->cell->flags[0], mp->cell->flags[1], mp->cell->flags[2], (mp->cell->has_sigma ? " 1" : ""));
3768                 if (mp->cell->has_sigma) {
3769                         fprintf(fp, "%f %f %f %f %f %f\n", mp->cell->cellsigma[0], mp->cell->cellsigma[1], mp->cell->cellsigma[2], mp->cell->cellsigma[3], mp->cell->cellsigma[4], mp->cell->cellsigma[5]);
3770                 }
3771                 fprintf(fp, "\n");
3772         }
3773         
3774         if (mp->frame_cells != NULL) {
3775                 fprintf(fp, "!:frame_periodic_boxes\n");
3776                 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz\n");
3777                 for (i = 0; i < mp->nframe_cells * 4; i++) {
3778                         fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->frame_cells[i].x, mp->frame_cells[i].y, mp->frame_cells[i].z);
3779                 }
3780                 fprintf(fp, "\n");
3781         }
3782         
3783         if (mp->nsyms > 0) {
3784                 fprintf(fp, "!:symmetry_operations\n");
3785                 fprintf(fp, "! a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3\n");
3786                 for (i = 0; i < mp->nsyms; i++) {
3787                         Transform *tp = mp->syms + i;
3788                         const unsigned char s_index_order[12] = {0, 3, 6, 1, 4, 7, 2, 5, 8, 9, 10, 11};
3789                         for (j = 0; j < 12; j++)
3790                                 fprintf(fp, "%11.6f%c", (*tp)[s_index_order[j]], (j % 3 == 2 ? '\n' : ' '));
3791                 }
3792                 fprintf(fp, "\n");
3793         }
3794         
3795         if (n_aniso > 0) {
3796                 fprintf(fp, "!:anisotropic_thermal_parameters\n");
3797                 fprintf(fp, "! b11 b22 b33 b12 b13 b23 [sigma; sb11 sb22 sb33 sb12 sb13 sb23]\n");
3798                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3799                         if (ap->aniso != NULL) {
3800                                 Double *bp = ap->aniso->bij;
3801                                 fprintf(fp, "%14.8g %14.8g %14.8g %14.8g %14.8g %14.8g%s\n", bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], (ap->aniso->has_bsig ? " 1" : ""));
3802                                 if (ap->aniso->has_bsig) {
3803                                         bp = ap->aniso->bsig;
3804                                         fprintf(fp, "%14.8g %14.8g %14.8g %14.8g %14.8g %14.8g\n", bp[0], bp[1], bp[2], bp[3], bp[4], bp[5]);
3805                                 }
3806                         } else {
3807                                 fprintf(fp, "0 0 0 0 0 0\n");
3808                         }
3809                 }
3810                 fprintf(fp, "\n");              
3811         }
3812         
3813         if (mp->arena != NULL) {
3814                 MDArena *arena = mp->arena;
3815                 fprintf(fp, "!:md_parameters\n");
3816                 fprintf(fp, "log_file %s\n", arena->log_result_name);
3817                 fprintf(fp, "coord_file %s\n", arena->coord_result_name);
3818                 fprintf(fp, "vel_file %s\n", arena->vel_result_name);
3819                 fprintf(fp, "force_file %s\n", arena->force_result_name);
3820                 fprintf(fp, "debug_file %s\n", arena->debug_result_name);
3821                 fprintf(fp, "debug_output_level %d\n", arena->debug_output_level);
3822                 fprintf(fp, "step %d\n", arena->step);
3823                 fprintf(fp, "coord_output_freq %d\n", arena->coord_output_freq);
3824                 fprintf(fp, "energy_output_freq %d\n", arena->energy_output_freq);
3825                 fprintf(fp, "coord_frame %d\n", arena->coord_result_frame);
3826                 fprintf(fp, "timestep %g\n", arena->timestep);
3827                 fprintf(fp, "cutoff %g\n", arena->cutoff);
3828                 fprintf(fp, "electro_cutoff %g\n", arena->electro_cutoff);
3829                 fprintf(fp, "pairlist_distance %g\n", arena->pairlist_distance);
3830                 fprintf(fp, "temperature %g\n", arena->temperature);
3831                 fprintf(fp, "andersen_freq %d\n", arena->andersen_thermo_freq);
3832                 fprintf(fp, "andersen_coupling %g\n", arena->andersen_thermo_coupling);
3833                 fprintf(fp, "random_seed %d\n", arena->random_seed);
3834                 fprintf(fp, "dielectric %g\n", arena->dielectric);
3835                 fprintf(fp, "gradient_convergence %g\n", arena->gradient_convergence);
3836                 fprintf(fp, "coordinate_convergence %g\n", arena->coordinate_convergence);
3837                 fprintf(fp, "use_xplor_shift %d\n", arena->use_xplor_shift);
3838                 fprintf(fp, "scale14_vdw %g\n", arena->scale14_vdw);
3839                 fprintf(fp, "scale14_elect %g\n", arena->scale14_elect);
3840                 fprintf(fp, "relocate_center %d\n", arena->relocate_center);
3841                 fprintf(fp, "surface_probe_radius %g\n", arena->probe_radius);
3842                 fprintf(fp, "surface_tension %g\n", arena->surface_tension);
3843                 fprintf(fp, "surface_potential_freq %d\n", arena->surface_potential_freq);
3844                 fprintf(fp, "use_graphite %d\n", arena->use_graphite);
3845                 fprintf(fp, "alchemical_lambda %g\n", arena->alchem_lambda);
3846                 fprintf(fp, "alchemical_delta_lambda %g\n", arena->alchem_dlambda);
3847                 if (arena->nalchem_flags > 0) {
3848                         fprintf(fp, "alchem_flags %d", arena->nalchem_flags);
3849                         for (i = 0; i < arena->nalchem_flags; i++) {
3850                                 if (i % 60 == 0)
3851                                         fputc('\n', fp);
3852                                 else if (i % 10 == 0)
3853                                         fputc(' ', fp);
3854                                 fputc('0' + arena->alchem_flags[i], fp);
3855                         }
3856                         fputc('\n', fp);
3857                 }
3858                 if (arena->pressure != NULL) {
3859                         Double *dp;
3860                         fprintf(fp, "pressure_freq %d\n", arena->pressure->freq);
3861                         fprintf(fp, "pressure_coupling %g\n", arena->pressure->coupling);
3862                         dp = arena->pressure->apply;
3863                         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]);
3864                         dp = arena->pressure->cell_flexibility;
3865                         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]);
3866                         fprintf(fp, "pressure_fluctuate_cell_origin %g\n", arena->pressure->fluctuate_cell_origin);
3867                         fprintf(fp, "pressure_fluctuate_cell_orientation %g\n", arena->pressure->fluctuate_cell_orientation);
3868                 }
3869                 fprintf(fp, "\n");
3870
3871                 if (mp->par != NULL) {
3872                         Parameter *par = mp->par;
3873                         fprintf(fp, "!:parameters\n");
3874                         ParameterAppendToFile(par, fp);
3875                         fprintf(fp, "\n");
3876                 }
3877                 
3878                 fprintf(fp, "!:velocity\n");
3879                 fprintf(fp, "! idx vx vy vz\n");
3880                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3881                         fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->v.x, ap->v.y, ap->v.z);
3882                 }
3883                 fprintf(fp, "\n");
3884
3885                 fprintf(fp, "!:force\n");
3886                 fprintf(fp, "! idx fx fy fz\n");
3887                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3888                         fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->f.x, ap->f.y, ap->f.z);
3889                 }
3890                 fprintf(fp, "\n");
3891         }
3892         
3893         if (mp->mview != NULL) {
3894                 float f[4];
3895                 if (mp->mview->track != NULL) {
3896                         fprintf(fp, "!:trackball\n");
3897                         fprintf(fp, "! scale; trx try trz; theta_deg x y z\n");
3898                         f[0] = TrackballGetScale(mp->mview->track);
3899                         fprintf(fp, "%f\n", f[0]);
3900                         TrackballGetTranslate(mp->mview->track, f);
3901                         fprintf(fp, "%f %f %f\n", f[0], f[1], f[2]);
3902                         TrackballGetRotate(mp->mview->track, f);
3903                         fprintf(fp, "%f %f %f %f\n", f[0], f[1], f[2], f[3]);
3904                         fprintf(fp, "\n");
3905                 }
3906                 fprintf(fp, "!:view\n");
3907                 fprintf(fp, "show_unit_cell %d\n", mp->mview->showUnitCell);
3908                 fprintf(fp, "show_periodic_box %d\n", mp->mview->showPeriodicBox);
3909                 fprintf(fp, "show_expanded_atoms %d\n", mp->mview->showExpandedAtoms);
3910                 fprintf(fp, "show_ellipsoids %d\n", mp->mview->showEllipsoids);
3911                 fprintf(fp, "show_hydrogens %d\n", mp->mview->showHydrogens);
3912                 fprintf(fp, "show_dummy_atoms %d\n", mp->mview->showDummyAtoms);
3913                 fprintf(fp, "show_rotation_center %d\n", mp->mview->showRotationCenter);
3914                 fprintf(fp, "show_graphite_flag %d\n", mp->mview->showGraphiteFlag);
3915                 fprintf(fp, "show_graphite %d\n", mp->mview->showGraphite);
3916                 fprintf(fp, "show_periodic_image_flag %d\n", mp->mview->showPeriodicImageFlag);
3917                 fprintf(fp, "show_periodic_image %d %d %d %d %d %d\n",
3918                                 mp->mview->showPeriodicImage[0], mp->mview->showPeriodicImage[1],
3919                                 mp->mview->showPeriodicImage[2], mp->mview->showPeriodicImage[3],
3920                                 mp->mview->showPeriodicImage[4], mp->mview->showPeriodicImage[5]);
3921                 fprintf(fp, "\n");
3922         }
3923
3924         fclose(fp);
3925         return 0;
3926 }
3927
3928 int
3929 MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3930 {
3931         FILE *fp;
3932         int i;
3933         Atom *ap;
3934         fp = fopen(fname, "wb");
3935         if (fp == NULL) {
3936                 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
3937                 return 1;
3938         }
3939         errbuf[0] = 0;
3940         fprintf(fp, "PSF\n\n");
3941         fprintf(fp, "       1 !NTITLE\n");
3942         fprintf(fp, " REMARKS FILENAME=\n");
3943         fprintf(fp, "\n");
3944         
3945         /*  Atoms  */
3946         fprintf(fp, "%8d !NATOM\n", mp->natoms);
3947         for (i = 0; i < mp->natoms; i++) {
3948                 const char *fmt;
3949                 ap = ATOM_AT_INDEX(mp->atoms, i);
3950                 fprintf(fp, "%8d ", i + 1);
3951                 if (ap->resSeq >= 10000) {
3952                         fmt = "%-3.3s %-5d ";
3953                 } else {
3954                         fmt = "%-4.4s %-4d ";
3955                 }
3956                 fprintf(fp, fmt, ap->segName, ap->resSeq);
3957                 fprintf(fp, "%-3.3s  %-4.4s %-4.4s   %12.6f  %8.4f           0\n",
3958                         ap->resName, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->charge, ap->weight);
3959         }
3960         fprintf(fp, "\n");
3961         
3962         /*  Bonds  */
3963         fprintf(fp, "%8d !NBOND: bonds\n", mp->nbonds);
3964         for (i = 0; i < mp->nbonds * 2; i++) {
3965                 fprintf(fp, "%8d", mp->bonds[i] + 1);
3966                 if (i % 8 == 7)
3967                         fprintf(fp, "\n");
3968         }
3969         if (i % 8 != 0)
3970                 fprintf(fp, "\n");
3971         fprintf(fp, "\n");
3972         
3973         /*  Angles  */
3974         fprintf(fp, "%8d !NTHETA: angles\n", mp->nangles);
3975         for (i = 0; i < mp->nangles * 3; i++) {
3976                 fprintf(fp, "%8d", mp->angles[i] + 1);
3977                 if (i % 9 == 8)
3978                         fprintf(fp, "\n");
3979         }
3980         if (i % 9 != 0)
3981                 fprintf(fp, "\n");
3982         fprintf(fp, "\n");
3983         
3984         /*  Dihedrals  */
3985         fprintf(fp, "%8d !NPHI: dihedrals\n", mp->ndihedrals);
3986         for (i = 0; i < mp->ndihedrals * 4; i++) {
3987                 fprintf(fp, "%8d", mp->dihedrals[i] + 1);
3988                 if (i % 8 == 7)
3989                         fprintf(fp, "\n");
3990         }
3991         if (i % 8 != 0)
3992                 fprintf(fp, "\n");
3993         fprintf(fp, "\n");
3994         
3995         /*  Dihedrals  */
3996         fprintf(fp, "%8d !NIMPHI: impropers\n", mp->nimpropers);
3997         for (i = 0; i < mp->nimpropers * 4; i++) {
3998                 fprintf(fp, "%8d", mp->impropers[i] + 1);
3999                 if (i % 8 == 7)
4000                         fprintf(fp, "\n");
4001         }
4002         if (i % 8 != 0)
4003                 fprintf(fp, "\n");
4004         fprintf(fp, "\n");
4005         
4006         fprintf(fp, "%8d !NDON: donors\n\n", 0);
4007         fprintf(fp, "%8d !NACC: acceptors\n\n", 0);
4008         fprintf(fp, "%8d !NNB: non-bonding exclusions\n\n", 0);
4009         for (i = 0; i < mp->natoms; i++) {
4010                 fprintf(fp, "%8d", 0);
4011                 if (i % 8 == 7)
4012                         fprintf(fp, "\n");
4013         }
4014         if (i % 8 != 0)
4015                 fprintf(fp, "\n");
4016         fprintf(fp, "\n");
4017         fprintf(fp, "%8d !NGRP: groups\n", 1);
4018         fprintf(fp, "       0       0       0\n");
4019         fprintf(fp, "\n");
4020         
4021         i = strlen(fname);
4022         if (i > 5 && strcmp(fname + i - 5, ".psfx") == 0) {
4023                 /*  Extended psf (with coordinates and other info)  */
4024                 fprintf(fp, "%8d !COORD: coordinates\n", mp->natoms);
4025                 for (i = 0; i < mp->natoms; i++) {
4026                         Vector r;
4027                         ap = ATOM_AT_INDEX(mp->atoms, i);
4028                         r = ap->r;
4029                         fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->aname);
4030                 }
4031                 fprintf(fp, "\n");
4032 #if 0
4033                 if (mp->nframes > 0) {
4034                         int fn;  /*  Frame number  */
4035                         for (fn = 0; fn < ap->nframes; fn++) {
4036                                 fprintf(fp, "%8d !COORD: coordinates for frame %d\n", mp->natoms, fn);
4037                                 for (i = 0; i < mp->natoms; i++) {
4038                                         Vector r;
4039                                         ap = ATOM_AT_INDEX(mp->atoms, i);
4040                                         if (ap->frames == NULL || fn >= ap->nframes)
4041                                                 r = ap->r;
4042                                         else
4043                                                 r = ap->frames[fn];
4044                                         fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->name);
4045                                 }
4046                                 fprintf(fp, "\n");
4047                         }
4048                 }
4049 #endif
4050         }
4051                 
4052         fclose(fp);
4053         return 0;
4054 }
4055
4056 int
4057 MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
4058 {
4059         FILE *fp;
4060         int i, j;
4061         Atom *ap;
4062         fp = fopen(fname, "wb");
4063         if (fp == NULL) {
4064                 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
4065                 return 1;
4066         }
4067         errbuf[0] = 0;
4068         for (i = 0; i < mp->natoms; i++) {
4069                 char buf[6];
4070                 ap = ATOM_AT_INDEX(mp->atoms, i);
4071                 if (ap->resSeq >= 10000) {
4072                         snprintf(buf, sizeof buf, "%c%03d", 'A' + (ap->resSeq - 10000) / 1000, ap->resSeq % 1000);
4073                 } else {
4074                         snprintf(buf, sizeof buf, "%4d", ap->resSeq);
4075                 }
4076                 fprintf(fp, "ATOM  %5d %-4.4s%1.1s%-3.3s %1.1s%4.4s%1.1s   "
4077                                         "%8.3f%8.3f%8.3f %5.2f %5.2f      "
4078                                         "%-4.4s%-2.2s%-2d\n",
4079                         i + 1, ap->aname, " ", ap->resName, " ", buf, " ",
4080                         ap->r.x, ap->r.y, ap->r.z, ap->occupancy, ap->tempFactor,
4081                         ap->segName, ap->element, ap->intCharge);
4082         }
4083         for (i = 0; i < mp->natoms; i++) {
4084                 Int *cp;
4085                 ap = ATOM_AT_INDEX(mp->atoms, i);
4086                 cp = AtomConnects(ap);
4087                 for (j = 0; j < ap->nconnects; j++) {
4088                         if (j % 4 == 0) {
4089                                 if (j > 0)
4090                                         fprintf(fp, "\n");
4091                                 fprintf(fp, "CONECT%5d", i + 1);
4092                         }
4093                         fprintf(fp, "%5d", cp[j] + 1);
4094                 }
4095                 if (j > 0)
4096                         fprintf(fp, "\n");
4097         }
4098         fprintf(fp, "END\n");
4099         fclose(fp);
4100         return 0;
4101 }
4102
4103 int
4104 MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
4105 {
4106         DcdRecord dcd;
4107         SFloat32 *xp, *yp, *zp;
4108         int n;
4109         errbuf[0] = 0;
4110         if (mp == NULL || mp->natoms == 0) {
4111                 snprintf(errbuf, errbufsize, "Molecule is empty");
4112                 return 1;
4113         }
4114         memset(&dcd, 0, sizeof(dcd));
4115         dcd.natoms = mp->natoms;
4116         dcd.nframes = MoleculeGetNumberOfFrames(mp);
4117         if (dcd.nframes == 0) {
4118                 snprintf(errbuf, errbufsize, "no frame is present");
4119                 return 1;
4120         }
4121         dcd.nstart = mp->startStep;
4122         dcd.ninterval = mp->stepsPerFrame;
4123         if (dcd.ninterval == 0)
4124                 dcd.ninterval = 1;
4125         dcd.nend = dcd.nstart + (dcd.nframes - 1) * dcd.ninterval;
4126         if (mp->cell != NULL)
4127                 dcd.nextra = 1;
4128         dcd.delta = mp->psPerStep;
4129         if (dcd.delta == 0.0)
4130                 dcd.delta = 1.0;
4131         dcd.ncharmver = 24;
4132         n = DcdCreate(fname, &dcd);
4133         if (n != 0) {
4134                 if (n < 0)
4135                         snprintf(errbuf, errbufsize, "Cannot create dcd file");
4136                 else
4137                         snprintf(errbuf, errbufsize, "Cannot write dcd header");
4138                 DcdClose(&dcd);
4139                 return 1;
4140         }
4141         
4142         xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4143         yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4144         zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4145         if (xp == NULL || yp == NULL || zp == NULL) {
4146                 snprintf(errbuf, errbufsize, "Cannot allocate memory");
4147                 if (xp) free(xp);
4148                 if (yp) free(yp);
4149                 if (zp) free(zp);
4150                 DcdClose(&dcd);
4151                 return 1;
4152         }
4153         for (n = 0; n < dcd.nframes; n++) {
4154                 int i;
4155                 Atom *ap;
4156                 for (i = 0, ap = mp->atoms; i < dcd.natoms && mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4157                         Vector r;
4158                         if (ap->frames == NULL || n >= ap->nframes)
4159                                 r = ap->r;
4160                         else
4161                                 r = ap->frames[n];
4162                         xp[i] = r.x;
4163                         yp[i] = r.y;
4164                         zp[i] = r.z;
4165                 }
4166                 if (i < dcd.natoms) {
4167                         size_t sz = (dcd.natoms - i) * sizeof(SFloat32);
4168                         memset(xp + i, 0, sz);
4169                         memset(yp + i, 0, sz);
4170                         memset(zp + i, 0, sz);
4171                 }
4172                 if (n < mp->nframe_cells && mp->frame_cells != NULL) {
4173                         Vector *cp = &(mp->frame_cells[n * 4]);
4174                         dcd.globalcell[0] = VecLength(cp[0]);
4175                         dcd.globalcell[2] = VecLength(cp[1]);
4176                         dcd.globalcell[5] = VecLength(cp[2]);
4177                         dcd.globalcell[1] = VecDot(cp[0], cp[1]) / (dcd.globalcell[0] * dcd.globalcell[2]);
4178                         dcd.globalcell[3] = VecDot(cp[0], cp[2]) / (dcd.globalcell[0] * dcd.globalcell[5]);
4179                         dcd.globalcell[4] = VecDot(cp[1], cp[2]) / (dcd.globalcell[2] * dcd.globalcell[5]);                     
4180                 }                       
4181                 if (DcdWriteFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
4182                         snprintf(errbuf, errbufsize, "Write error in dcd file");
4183                         goto exit;
4184                 }
4185         }
4186         
4187 exit:
4188         DcdClose(&dcd);
4189         free(xp);
4190         free(yp);
4191         free(zp);
4192         if (errbuf[0] == 0)
4193                 return 0;
4194         else return 1;
4195 }
4196
4197 int
4198 MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
4199 {
4200         FILE *fp;
4201         int i;
4202         Vector v;
4203         errbuf[0] = 0;
4204         fp = fopen(fname, "wb");
4205         if (fp == NULL) {
4206                 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
4207                 return 1;
4208         }
4209         if (mp->cell != NULL) {
4210                 fprintf(fp, "Bounding box:\n");
4211                 for (i = 0; i < 3; i++) {
4212                         v = mp->cell->axes[i];
4213                         fprintf(fp, "%.3f %.3f %.3f %d\n", v.x, v.y, v.z, mp->cell->flags[i]);
4214                 }
4215                 fprintf(fp, "Bounding box origin:\n");
4216                 v = mp->cell->origin;
4217                 fprintf(fp, "%.3f %.3f %.3f\n", v.x, v.y, v.z);
4218         }
4219         fclose(fp);
4220         return 0;
4221 }
4222                 
4223  static int
4224 sCompareByElement(const void *ap, const void *bp)
4225 {
4226         return ((*(Atom **)bp)->atomicNumber - (*(Atom **)ap)->atomicNumber);
4227 }
4228
4229 static int
4230 sMakeAdc(int n, int base, Symop symop)
4231 {
4232         int an, sym;
4233         if (SYMOP_ALIVE(symop)) {
4234                 an = base;
4235                 sym = (symop.dx + 5) * 10000 + (symop.dy + 5) * 1000 + (symop.dz + 5) * 100 + symop.sym + 1;
4236         } else {
4237                 an = n;
4238                 sym = 55501;
4239         }
4240         return (an + 1) * 100000 + sym;
4241 }
4242
4243 static int
4244 sCompareAdc(const void *ap, const void *bp)
4245 {
4246         int n = *((Int *)ap) % 100000 - *((Int *)bp) % 100000;
4247         if (n == 0)
4248                 n = *((Int *)ap) / 100000 - *((Int *)bp) / 100000;
4249         return n;
4250 }
4251
4252 static void
4253 sOutputAtomListInstructions(FILE *fp, int natoms, Atom *atoms)
4254 {
4255         int i, j, k, an, sym;
4256         Atom *ap;
4257         Int *adc;
4258         adc = (Int *)malloc(sizeof(Int) * natoms);
4259         if (adc == NULL)
4260                 return;
4261         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4262                 if (ap->exflags & kAtomHiddenFlag)
4263                         continue;
4264                 adc[i] = sMakeAdc(i, ap->symbase, ap->symop);
4265         }
4266         mergesort(adc, natoms, sizeof(Int), sCompareAdc);
4267         
4268         /*  Create the atom list  */
4269         an = sym = -1;
4270         for (i = j = k = 0; i < natoms; i++) {
4271                 int an1 = adc[i] / 100000;
4272                 int sym1 = adc[i] % 100000;
4273                 if (sym == sym1 && an1 == an + 1) {
4274                         /*  Continuous  */
4275                         an = an1;
4276                         k++;
4277                         continue;
4278                 }
4279                 if (k > 0)
4280                         /*  Output the last atom with a minus sign  */
4281                         adc[j++] = -(an * 100000 + sym);
4282                 /*  Output this atom  */
4283                 adc[j++] = adc[i];
4284                 an = an1;
4285                 sym = sym1;
4286                 k = 0;
4287         }
4288         if (k > 0)
4289                 adc[j++] = -(an * 100000 + sym);
4290         
4291         /*  Create the instruction cards  */
4292         for (i = k = 0; i < j; i++) {
4293                 if (k == 0)
4294                         fprintf(fp, "      401");
4295                 fprintf(fp, "%9d", adc[i]);
4296                 k++;
4297                 if (i == j - 1 || k == 6 || (k == 5 && i < j - 2 && adc[i + 2] < 0)) {
4298                         fprintf(fp, "\n");
4299                         k = 0;
4300                 }
4301         }
4302         free(adc);
4303 }
4304
4305 static int
4306 sEllipsoidType(int an)
4307 {
4308         return (an >= 18 ? 3 : (an >= 2 && an != 6 ? 2 : (an > 0 ? 1 : 0)));
4309 }
4310
4311 static void
4312 sOutputAtomTypeInstructions(FILE *fp, int natoms, Atom *atoms)
4313 {
4314         int i;
4315         Atom *ap;
4316         int etype, elast, istart, ilast, n1, n2;
4317         elast = istart = ilast = -1;
4318         for (i = 0, ap = atoms; i <= natoms; i++, ap++) {
4319                 if (i < natoms) {
4320                         if (SYMOP_ALIVE(ap->symop))
4321                                 continue;
4322                         if (ap->exflags & kAtomHiddenFlag)
4323                                 continue;
4324                         etype = sEllipsoidType(ap->atomicNumber);
4325                         if (elast < 0) {
4326                                 istart = ilast = i;
4327                                 elast = etype;
4328                                 continue;
4329                         } else if (elast == etype && ilast == i - 1) {
4330                                 ilast++;
4331                                 continue;
4332                         }
4333                 }
4334                 /*  Output the instruction card for the 'last' block of atoms  */
4335                 switch (etype) {
4336                         case 2:
4337                                 n1 = 4; n2 = 0; break;
4338                         case 3:
4339                                 n1 = 4; n2 = 5; break;
4340                         default:
4341                                 n1 = 1; n2 = 0; break;
4342                 }
4343                 fprintf(fp, "  1   715 %8d        0 %8d        0    0.100    0.000    0.000\n", n1, n2);
4344                 fprintf(fp, "                           %9d%9d\n", istart + 1, ilast + 1);
4345                 elast = etype;
4346                 ilast = istart = i;
4347         }
4348 }
4349
4350 static int
4351 sCompareBondType(const void *ap, const void *bp)
4352 {
4353         /*  Descending order  */
4354         return *((int *)bp) - *((int *)ap);
4355 }
4356
4357 static void
4358 sOutputBondInstructions(FILE *fp, int natoms, Atom *atoms, int overlap_correction)
4359 {
4360         Atom *ap, *ap2;
4361         char buf[96];
4362         int i, j, n[5], an, count, n1, n2, k;
4363         Int *cp;
4364         Int nexbonds;
4365         Int *exbonds;
4366         static const float sBondRad[4] = {0.060, 0.060, 0.060, 0.040};
4367         static const int sBondShade[4] = {5, 3, 1, 1};
4368
4369         n[0] = n[1] = n[2] = n[3] = 0;  /*  Start index of 3rd row atoms (and higher), 2nd row, 1st row, and H */
4370         n[4] = natoms;
4371         for (i = natoms - 1, ap = atoms + i; i >= 0; i--, ap--) {
4372                 an = ap->atomicNumber;
4373                 if (an < 2)
4374                         n[3] = i;
4375                 if (an < 10)
4376                         n[2] = i;
4377                 if (an < 18)
4378                         n[1] = i;
4379         }
4380         nexbonds = 0;
4381         exbonds = NULL;
4382         count = 0;
4383
4384         if (overlap_correction)
4385                 strcpy(buf, "  2  1001    0.000\n");
4386         else
4387                 strcpy(buf, "  2   812\n");
4388         
4389         for (i = 0; i < 4; i++) {
4390                 for (j = i; j < 4; j++) {
4391                         /*  Examine bonds between "group i" and "group j"  */
4392                         Vector dr;
4393                         double d;
4394                         double min_bond = 10000.0;     /*  Minimum distance between bound atoms  */
4395                         double min_nonbond = 10000.0;  /*  Minimum distance between non-bound atoms  */
4396                         double max_bond = -10000.0;    /*  Maximum distance between bound atoms  */
4397                         int count_exbond = 0;          /*  Number of explicit bonds in this group  */
4398                         for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
4399                                 for (n2 = n[j], ap2 = atoms + n2; n2 < n[j + 1]; n2++, ap2++) {
4400                                         if (n1 == n2)
4401                                                 continue;
4402                                         VecSub(dr, ap->r, ap2->r);
4403                                         d = VecLength(dr);
4404                                         cp = AtomConnects(ap);
4405                                         for (k = ap->nconnects - 1; k >= 0; k--) {
4406                                                 if (cp[k] == n2)
4407                                                         break;
4408                                         }
4409                                         if (k >= 0) {
4410                                                 /*  n1 and n2 are bound  */
4411                                                 if (d < min_bond)
4412                                                         min_bond = d;
4413                                                 if (d > max_bond)
4414                                                         max_bond = d;
4415                                         } else {
4416                                                 /*  n1 and n2 are not bound  */
4417                                                 if (d < min_nonbond)
4418                                                         min_nonbond = d;
4419                                         }
4420                                 }
4421                         }
4422                         if (min_bond == 10000.0)
4423                                 continue;  /*  No bonds between these groups  */
4424                         min_bond *= 0.9;
4425                         if (max_bond + 0.002 < min_nonbond)
4426                                 max_bond += 0.002;
4427                         else {
4428                                 max_bond = min_nonbond - 0.002;
4429                                 /*  Some bonds may be omitted, so scan all bonds again  */
4430                                 for (n1 = n[i], ap = ATOM_AT_INDEX(atoms, n1); n1 < n[i + 1]; n1++, ap = ATOM_NEXT(ap)) {
4431                                         cp = AtomConnects(ap);
4432                                         for (k = ap->nconnects - 1; k >= 0; k--) {
4433                                                 n2 = cp[k];
4434                                                 if (n2 < n[j] || n2 >= n[j + 1])
4435                                                         continue;
4436                                                 ap2 = atoms + n2;
4437                                                 VecSub(dr, ap->r, ap2->r);
4438                                                 d = VecLength(dr);
4439                                                 if (d > max_bond) {
4440                                                         /*  This bond should be explicitly defined  */
4441                                                         Int adc1, adc2;
4442                                                         if (count_exbond == 0) {
4443                                                                 adc1 = -(i + 1);  /*  Bond type  */
4444                                                                 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4445                                                         }
4446                                                         adc1 = sMakeAdc(n1, ap->symbase, ap->symop);
4447                                                         adc2 = sMakeAdc(n2, ap2->symbase, ap2->symop);
4448                                                         AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4449                                                         AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc2);
4450                                                         count_exbond++;
4451                                                 }
4452                                         }
4453                                 }
4454                         }
4455                         /*  Output the last instruction card  */
4456                         fputs(buf, fp);
4457                         /*  Make a new trailer card  */
4458                         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]);
4459                         count++;
4460                 }
4461         }
4462         if (count > 0) {
4463                 /*  Output the last trailer card  */
4464                 buf[2] = ' ';
4465                 fputs(buf, fp);
4466         }
4467         if (nexbonds > 0) {
4468                 if (count == 0 && overlap_correction) {
4469                         /*  1001 card is not yet written, so write it  */
4470                         buf[2] = ' ';
4471                         fputs(buf, fp);
4472                 }
4473                 snprintf(buf, sizeof(buf), "  1   %3d", (overlap_correction ? 821 : 811));
4474                 k = -exbonds[0] - 1;  /*  Bond type for the first block  */
4475                 i = 1;  /*  Index for exbonds[]  */
4476                 j = 0;  /*  Count in this block  */
4477                 while (i <= nexbonds) {
4478                         if (j >= 29 || i == nexbonds || exbonds[i] < 0) {
4479                                 /*  End of block  */
4480                                 buf[2] = '2';
4481                                 fputs(buf, fp);
4482                                 /*  The trailer card  */
4483                                 fprintf(fp, "                     %3d            %6.3f\n", sBondShade[k], sBondRad[k]);
4484                                 if (i == nexbonds)
4485                                         break;
4486                                 if (exbonds[i] < 0)
4487                                         k = -exbonds[i++] - 1;  /*  The new bond type  */
4488                                 j = 0;
4489                         } else if (j > 0 && j % 3 == 0) {
4490                                 buf[2] = '1';
4491                                 fputs(buf, fp);
4492                         }
4493                         n1 = exbonds[i++];
4494                         n2 = exbonds[i++];
4495                         snprintf(buf + 9 + (j % 3) * 18, sizeof(buf) - 9 - (j % 3) * 18, "%9d%9d\n", n1, n2);
4496                         j++;
4497                 }
4498                 free(exbonds);
4499         }
4500 }
4501         
4502 #if 0
4503 {
4504         /*  Explicit bond table, sorted by bond type  */
4505         for (i = j = 0; i < mp->nbonds; i++) {
4506                 n1 = mp->bonds[i * 2];
4507                 n2 = mp->bonds[i * 2 + 1];
4508                 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
4509                 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
4510                 if ((ap1->exflags & kAtomHiddenFlag) || (ap2->exflags & kAtomHiddenFlag))
4511                         continue;
4512                 if (ap1->atomicNumber > 18 || ap2->atomicNumber > 18) {
4513                         type = 3;
4514                 } else if (ap1->atomicNumber > 1 && ap1->atomicNumber > 1) {
4515                         type = 2;
4516                 } else {
4517                         type = 1;
4518                 }
4519                 ip[j * 3] = type;
4520                 ip[j * 3 + 1] = sMakeAdc(n1, ap1->symbase, ap1->symop);
4521                 ip[j * 3 + 2] = sMakeAdc(n2, ap2->symbase, ap2->symop);
4522                 j++;
4523         }
4524         mergesort(ip, j, sizeof(int) * 3, sCompareBondType);
4525         
4526         /*  Output instruction cards  */
4527         strcpy(buf, "  1   811");
4528         for (i = n1 = 0; i < j; i++) {
4529                 n2 = (n1 % 3) * 18 + 9;
4530                 snprintf(buf + n2, 80 - n2, "%9d%9d\n", ip[i * 3 + 1], ip[i * 3 + 2]);
4531                 if (i == j - 1 || n1 >= 29 || ip[i * 3] != ip[i * 3 + 3]) {
4532                         /*  End of this instruction  */
4533                         buf[2] = '2';
4534                         fputs(buf, fp);
4535                         switch (ip[i * 3]) {
4536                                 case 3: rad = 0.06; nshades = 5; break;
4537                                 case 2: rad = 0.06; nshades = 1; break;
4538                                 default: rad = 0.04; nshades = 1; break;
4539                         }
4540                         fprintf(fp, "                     %3d            %6.3f\n", nshades, rad);
4541                         strcpy(buf, "  1   811");
4542                         n1 = 0;
4543                         continue;
4544                 } else if (n1 % 3 == 2) {
4545                         fputs(buf, fp);
4546                         strcpy(buf, "  1      ");
4547                 }
4548                 n1++;
4549         }
4550         free(ip);
4551 }
4552 #endif
4553
4554 int
4555 MoleculeWriteToTepFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
4556 {
4557         FILE *fp;
4558         int i, j, natoms, *ip;
4559         Int *cp;
4560         Atom *ap, *atoms, **app;
4561         Double *dp;
4562         static Double sUnit[] = {1, 1, 1, 90, 90, 90};
4563         
4564         errbuf[0] = 0;
4565
4566         /*  Create sorted array of atoms  */
4567         natoms = mp->natoms;
4568         atoms = (Atom *)calloc(sizeof(Atom), natoms);
4569         app = (Atom **)calloc(sizeof(Atom *), natoms);
4570         ip = (int *)calloc(sizeof(int), natoms);
4571         if (atoms == NULL || app == NULL || ip == NULL) {
4572                 snprintf(errbuf, errbufsize, "Cannot allocate memory");
4573                 return 1;
4574         }
4575         /*  Sort the atom pointer by atomic number  */
4576         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
4577                 app[i] = ap;
4578         mergesort(app, natoms, sizeof(Atom *), sCompareByElement);
4579         for (i = 0; i < natoms; i++) {
4580                 /*  ip[old_index] is new_index  */
4581                 ip[app[i] - mp->atoms] = i;
4582         }
4583         /*  Copy the atom record to atoms[]  */
4584         /*  The 'v' member contains crystallographic coordinates  */
4585         /*  The connection table and symbase are renumbered  */
4586         /*  Hidden flags are modified to reflect the visibility in the MainView  */
4587         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4588                 AtomDuplicateNoFrame(ap, app[i]);
4589         /*      memmove(ap, app[i], gSizeOfAtomRecord); */
4590                 MoleculeCartesianToXtal(mp, &(ap->v), &(ap->r));
4591                 cp = AtomConnects(ap);
4592                 for (j = ap->nconnects - 1; j >= 0; j--) {
4593                         cp[j] = ip[cp[j]];
4594                 }
4595                 if (SYMOP_ALIVE(ap->symop))
4596                         ap->symbase = ip[ap->symbase];
4597                 if (MainView_isAtomHidden(mp->mview, i)) {
4598                         ap->exflags |= kAtomHiddenFlag;
4599                 } else {
4600                         ap->exflags &= ~kAtomHiddenFlag;
4601                 }
4602         }
4603         free(ip);
4604         free(app);
4605         
4606         fp = fopen(fname, "wb");
4607         if (fp == NULL) {
4608                 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
4609                 return 1;
4610         }
4611         errbuf[0] = 0;
4612
4613         /*  Title line  */
4614         fprintf(fp, "Generated by Molby\n");
4615         
4616         /*  XtalCell  */
4617         if (mp->cell != NULL) {
4618                 dp = mp->cell->cell;
4619         } else {
4620                 dp = sUnit;
4621         }
4622         fprintf(fp, "%9.3f%9.3f%9.3f%9.3f%9.3f%9.3f\n", dp[0], dp[1], dp[2], dp[3], dp[4], dp[5]);
4623         
4624         /*  Symmetry operations  */
4625         if (mp->nsyms > 0) {
4626                 for (i = 0; i < mp->nsyms; i++) {
4627                         dp = mp->syms[i];
4628                         fprintf(fp, "%c%14g%3g%3g%3g%15g%3g%3g%3g%15g%3g%3g%3g\n", (i == mp->nsyms - 1 ? '1' : ' '), dp[9], dp[0], dp[1], dp[2], dp[10], dp[3], dp[4], dp[5], dp[11], dp[6], dp[7], dp[8]);
4629                 }
4630         } else {
4631                 fprintf(fp, "1             0  1  0  0              0  0  1  0              0  0  0  1\n");
4632         }
4633         
4634         /*  Atoms  */
4635         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4636                 /*  The 'v' field contains crystallographic coordinates  */
4637                 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);
4638                 if (ap->aniso != NULL) {
4639                         dp = ap->aniso->bij;
4640                         fprintf(fp, " %8.5f%9.6f%9.6f%9.6f%9.6f%9.6f%9d\n", dp[0], dp[1], dp[2], dp[3], dp[4], dp[5], 0);
4641                 } else {
4642                         Double temp = ap->tempFactor;
4643                         if (temp <= 0)
4644                                 temp = 1.2;
4645                         fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", temp, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4646                 }
4647         }
4648         /*  Special points  */
4649         {
4650                 Vector camera, lookat, up, xvec, yvec, zvec;
4651                 MainView_getCamera(mp->mview, &camera, &lookat, &up);
4652                 VecSub(zvec, lookat, camera);
4653                 VecCross(xvec, zvec, up);
4654                 NormalizeVec(&xvec, &xvec);
4655                 NormalizeVec(&yvec, &up);
4656                 VecInc(xvec, lookat);
4657                 VecInc(yvec, lookat);
4658                 MoleculeCartesianToXtal(mp, &lookat, &lookat);
4659                 MoleculeCartesianToXtal(mp, &xvec, &xvec);
4660                 MoleculeCartesianToXtal(mp, &yvec, &yvec);
4661                 fprintf(fp, " ORGN                      %9g%9g%9g        0\n", 0.0, 0.0, 0.0);
4662                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4663                 fprintf(fp, " CNTR                      %9g%9g%9g        0\n", lookat.x, lookat.y, lookat.z);
4664                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4665                 fprintf(fp, " X                         %9g%9g%9g        0\n", xvec.x, xvec.y, xvec.z);
4666                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4667                 fprintf(fp, " Y                         %9g%9g%9g        0\n", yvec.x, yvec.y, yvec.z);
4668                 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);
4669         }
4670         
4671         /*  Instructions  */
4672         fprintf(fp, "      201\n");
4673         fprintf(fp, "      205       12\n");
4674         fprintf(fp, "      301      6.6      6.6        0      0.8\n");
4675         sOutputAtomListInstructions(fp, natoms, atoms);
4676         fprintf(fp, "      501%4d55501%4d55501%4d55501%4d55501%4d55501                 1\n", natoms + 2, natoms + 2, natoms + 3, natoms + 2, natoms + 4);
4677         fprintf(fp, "      502        1      0.0        2      0.0        3      0.0\n");
4678         fprintf(fp, "      604                               1.538\n");
4679
4680         sOutputBondInstructions(fp, natoms, atoms, 1);
4681         sOutputAtomTypeInstructions(fp, natoms, atoms);
4682         sOutputBondInstructions(fp, natoms, atoms, 0);
4683
4684         for (i = 0; i < natoms; i++) {
4685                 AtomClean(atoms + i);
4686         }
4687         free(atoms);
4688
4689         fprintf(fp, "      202\n");
4690         fprintf(fp, "  0    -1\n");
4691         fclose(fp);
4692         return 0;
4693 }
4694
4695 void
4696 MoleculeDump(Molecule *mol)
4697 {
4698         int i, j;
4699         Int *cp;
4700         Atom *ap;
4701         for (i = 0; i < mol->natoms; i++) {
4702                 char buf1[8];
4703                 ap = ATOM_AT_INDEX(mol->atoms, i);
4704                 snprintf(buf1, sizeof buf1, "%3.4s.%d", ap->resName, ap->resSeq);
4705                 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);
4706                 cp = AtomConnects(ap);
4707                 for (j = 0; j < ap->nconnects; j++) {
4708                         fprintf(stderr, "%s%d", (j > 0 ? "," : ""), cp[j]);
4709                 }
4710                 fprintf(stderr, "]\n");
4711         }
4712 }
4713
4714 #pragma mark ====== MD support (including modification of Molecule) ======
4715
4716 /*  Call md_prepare for the MDArena. If MDArena has not been created, a new arena is created.
4717         If something goes wrong, returns 1 (for missing parameters) or -1 (more serious error).
4718     If retmsg is not NULL, a message describing the problem is returned there. This message
4719     must be free'd by the caller.  */
4720 int
4721 MoleculePrepareMDArena(Molecule *mol, int check_only, char **retmsg)
4722 {
4723         const char *msg;
4724         Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
4725         Int missing = 0;
4726         IntGroup *ig1, *ig2, *ig3;
4727         MDArena *arena = mol->arena;
4728
4729         if (arena == NULL) {
4730                 md_arena_new(mol);
4731                 arena = mol->arena;
4732         } else if (arena->xmol != mol)
4733                 md_arena_set_molecule(arena, mol);
4734
4735         arena->is_initialized = 0;
4736         
4737         /*  Rebuild the tables  */
4738         ig1 = ig2 = ig3 = NULL;
4739         nangles = MoleculeFindMissingAngles(mol, &angles);
4740         ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
4741         nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
4742         if (nangles > 0) {
4743                 ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
4744                 MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
4745                 free(angles);
4746                 IntGroupRelease(ig1);
4747         }
4748         if (ndihedrals > 0) {
4749                 ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
4750                 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
4751                 free(dihedrals);
4752                 IntGroupRelease(ig2);
4753         }
4754         if (nimpropers > 0) {
4755                 ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
4756                 MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
4757                 free(impropers);
4758                 IntGroupRelease(ig3);
4759         }
4760         
4761         /*  Prepare parameters and internal information  */
4762         msg = md_prepare(arena, check_only);
4763         
4764         /*  Some parameters are missing?  */
4765         if (msg != NULL) {
4766                 if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
4767                         missing = 1;
4768                 else {
4769                         if (retmsg != NULL)
4770                                 asprintf(retmsg, "cannot initialize for MD: %s", msg);
4771                         return -1;
4772                 }
4773         }
4774         
4775         /*  The local parameter list is updated  */
4776         {
4777                 Int parType, idx;
4778                 if (mol->par == NULL)
4779                         mol->par = ParameterNew();
4780                 for (parType = kFirstParType; parType <= kLastParType; parType++) {
4781                         /*  Delete global and undefined parameters  */
4782                         UnionPar *up, *upbuf;
4783                         Int nparams, count;
4784                         ig1 = IntGroupNew();
4785                         for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
4786                                 if (up->bond.src != 0)
4787                                         IntGroupAdd(ig1, idx, 1);
4788                         }
4789                         if (IntGroupGetCount(ig1) > 0)
4790                                 MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
4791                         IntGroupRelease(ig1);
4792                         /*  Copy global and undefined parameters from arena and insert to mol->par  */
4793                         nparams = ParameterGetCountForType(arena->par, parType);
4794                         if (nparams == 0)
4795                                 continue;
4796                         upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
4797                         ig1 = IntGroupNew();
4798                         ig2 = IntGroupNew();
4799                         for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
4800                                 if (up->bond.src > 0)
4801                                         IntGroupAdd(ig1, idx, 1); /* Global parameter */
4802                                 else if (up->bond.src < 0)
4803                                         IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
4804                         }
4805                         if ((count = IntGroupGetCount(ig1)) > 0) {
4806                                 /*  Insert global parameters (at the top)  */
4807                                 ParameterCopy(arena->par, parType, upbuf, ig1);
4808                                 ig3 = IntGroupNewWithPoints(0, count, -1);
4809                                 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
4810                                 IntGroupRelease(ig3);
4811                         }
4812                         if ((count = IntGroupGetCount(ig2)) > 0) {
4813                                 /*  Insert undefined parameters (at the bottom)  */
4814                                 ParameterCopy(arena->par, parType, upbuf, ig2);
4815                                 idx = ParameterGetCountForType(mol->par, parType);
4816                                 ig3 = IntGroupNewWithPoints(idx, count, -1);
4817                                 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
4818                                 IntGroupRelease(ig3);
4819                         }
4820                         IntGroupRelease(ig2);
4821                         IntGroupRelease(ig1);
4822                         free(upbuf);
4823                 }
4824                 mol->needsMDRebuild = 0;  /*  We know the "modified" parameters are consistent with the MDArena  */
4825         }
4826         
4827         if (missing) {
4828                 if (retmsg != NULL)
4829                         *retmsg = strdup(msg);
4830                 return 1;
4831         } else return 0;
4832 }
4833
4834 #pragma mark ====== Serialize ======
4835
4836 Molecule *
4837 MoleculeDeserialize(const char *data, Int length, Int *timep)
4838 {
4839         Molecule *mp;
4840         Parameter *par;
4841 /*      int result; */
4842
4843         mp = MoleculeNew();
4844         if (mp == NULL)
4845                 goto out_of_memory;
4846         par = ParameterNew();
4847         if (par == NULL)
4848                 goto out_of_memory;
4849
4850         while (length >= 12) {
4851                 const char *ptr = data + 8 + sizeof(Int);
4852                 int len = *((const Int *)(data + 8));
4853                 int i, j, n;
4854                 if (strcmp(data, "ATOM") == 0) {
4855                         n = len / gSizeOfAtomRecord;
4856                         NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, n);
4857                         memmove(mp->atoms, ptr, len);
4858                 } else if (strcmp(data, "ANISO") == 0) {
4859                         Atom *ap;
4860                         n = len / (sizeof(Int) + sizeof(Aniso));
4861                         for (i = 0; i < n; i++) {
4862                                 j = *((const Int *)ptr);
4863                                 if (j < 0 || j >= mp->natoms)
4864                                         goto bad_format;
4865                                 ap = ATOM_AT_INDEX(mp->atoms, j);
4866                                 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
4867                                 if (ap->aniso == NULL)
4868                                         goto out_of_memory;
4869                                 *(ap->aniso) = *((Aniso *)(ptr + sizeof(Int)));
4870                                 ptr += sizeof(Int) + sizeof(Aniso);
4871                         }
4872                 } else if (strcmp(data, "FRAME") == 0) {
4873                         Atom *ap;
4874                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4875                                 if (ap->nframes == 0)
4876                                         continue;
4877                                 ap->frames = (Vector *)malloc(sizeof(Vector) * ap->nframes);
4878                                 if (ap->frames == NULL)
4879                                         goto out_of_memory;
4880                                 memmove(ap->frames, ptr, sizeof(Vector) * ap->nframes);
4881                                 ptr += sizeof(Vector) * ap->nframes;
4882                         }
4883                 } else if (strcmp(data, "BOND") == 0) {
4884                         n = len / (sizeof(Int) * 2);
4885                         NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, n);
4886                         memmove(mp->bonds, ptr, len);
4887                 } else if (strcmp(data, "ANGLE") == 0) {
4888                         n = len / (sizeof(Int) * 3);
4889                         NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, n);
4890                         memmove(mp->angles, ptr, len);
4891                 } else if (strcmp(data, "DIHED") == 0) {
4892                         n = len / (sizeof(Int) * 4);
4893                         NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, n);
4894                         memmove(mp->dihedrals, ptr, len);
4895                 } else if (strcmp(data, "IMPROP") == 0) {
4896                         n = len / (sizeof(Int) * 4);
4897                         NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, n);
4898                         memmove(mp->impropers, ptr, len);
4899                 } else if (strcmp(data, "RESIDUE") == 0) {
4900                         n = len / 4;
4901                         NewArray(&mp->residues, &mp->nresidues, 4, n);
4902                         memmove(mp->residues, ptr, len);
4903                 } else if (strcmp(data, "CELL") == 0) {
4904                         mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
4905                         if (mp->cell == NULL)
4906                                 goto out_of_memory;
4907                         memmove(mp->cell, ptr, sizeof(XtalCell));
4908                 } else if (strcmp(data, "SYMOP") == 0) {
4909                         n = len / sizeof(Transform);
4910                         NewArray(&mp->syms, &mp->nsyms, sizeof(Transform), n);
4911                         memmove(mp->syms, ptr, len);
4912                 } else if (strcmp(data, "EXATOM") == 0) {
4913                         n = len / sizeof(ExAtom);
4914                         NewArray(&mp->exatoms, &mp->nexatoms, sizeof(ExAtom), n);
4915                         memmove(mp->exatoms, ptr, len);
4916                 } else if (strcmp(data, "EXBOND") == 0) {
4917                         n = len / (sizeof(Int) * 2);
4918                         NewArray(&mp->exbonds, &mp->nexbonds, sizeof(Int) * 2, n);
4919                         memmove(mp->exbonds, ptr, len);
4920                 } else if (strcmp(data, "TIME") == 0) {
4921                         if (timep != NULL)
4922                                 *timep = *((Int *)ptr);
4923                 } else if (strcmp(data, "BONDPAR") == 0) {
4924                         mp->par = par;
4925                         n = len / sizeof(BondPar);
4926                         NewArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), n);
4927                         memmove(par->bondPars, ptr, len);
4928                 } else if (strcmp(data, "ANGPAR") == 0) {
4929                         mp->par = par;
4930                         n = len / sizeof(AnglePar);
4931                         NewArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), n);
4932                         memmove(par->anglePars, ptr, len);
4933                 } else if (strcmp(data, "DIHEPAR") == 0) {
4934                         mp->par = par;
4935                         n = len / sizeof(TorsionPar);
4936                         NewArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), n);
4937                         memmove(par->dihedralPars, ptr, len);
4938                 } else if (strcmp(data, "IMPRPAR") == 0) {
4939                         mp->par = par;
4940                         n = len / sizeof(TorsionPar);
4941                         NewArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), n);
4942                         memmove(par->improperPars, ptr, len);
4943                 } else if (strcmp(data, "VDWPAR") == 0) {
4944                         mp->par = par;
4945                         n = len / sizeof(VdwPar);
4946                         NewArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), n);
4947                         memmove(par->vdwPars, ptr, len);
4948                 } else if (strcmp(data, "VDWPPAR") == 0) {
4949                         mp->par = par;
4950                         n = len / sizeof(VdwPairPar);
4951                         NewArray(&par->vdwpPars, &par->nvdwpPars, sizeof(VdwPairPar), n);
4952                         memmove(par->vdwpPars, ptr, len);
4953                 } else if (strcmp(data, "VCUTPAR") == 0) {
4954                         mp->par = par;
4955                         n = len / sizeof(VdwCutoffPar);
4956                         NewArray(&par->vdwCutoffPars, &par->nvdwCutoffPars, sizeof(VdwCutoffPar), n);
4957                         memmove(par->vdwCutoffPars, ptr, len);
4958                 }
4959                 len += 8 + sizeof(Int);
4960                 data += len;
4961                 length -= len;
4962         }
4963         if (mp->par == NULL)
4964                 ParameterRelease(par);
4965 /*      result = MoleculeRebuildTablesFromConnects(mp);
4966         if (result != 0)
4967                 goto bad_format; */
4968         return mp;
4969         
4970   out_of_memory:
4971         Panic("Low memory while deserializing molecule data");
4972         return NULL; /* Not reached */
4973
4974   bad_format:
4975         Panic("internal error: bad format during deserializing molecule data");
4976         return NULL; /* Not reached */
4977 }
4978
4979 char *
4980 MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep)
4981 {
4982         char *ptr, *p;
4983         int len, len_all, i, naniso, nframes;
4984
4985         /*  Array of atoms  */
4986         len = 8 + sizeof(Int) + gSizeOfAtomRecord * mp->natoms;
4987         ptr = (char *)malloc(len);
4988         if (ptr == NULL)
4989                 goto out_of_memory;
4990         memmove(ptr, "ATOM\0\0\0\0", 8);
4991         *((Int *)(ptr + 8)) = gSizeOfAtomRecord * mp->natoms;
4992         p = ptr + 8 + sizeof(Int);
4993         memmove(p, mp->atoms, gSizeOfAtomRecord * mp->natoms);
4994         naniso = nframes = 0;
4995         for (i = 0; i < mp->natoms; i++) {
4996                 Atom *ap = ATOM_AT_INDEX(p, i);
4997                 if (ap->aniso != NULL) {
4998                         naniso++;
4999                         ap->aniso = NULL;
5000                 }
5001                 if (ap->frames != NULL) {
5002                         nframes += ap->nframes;
5003                         ap->frames = NULL;
5004                 }
5005         }
5006         len_all = len;
5007
5008         /*  Array of aniso  */
5009         if (naniso > 0) {
5010                 len = 8 + sizeof(Int) + (sizeof(Int) + sizeof(Aniso)) * naniso;
5011                 ptr = (char *)realloc(ptr, len_all + len);
5012                 if (ptr == NULL)
5013                         goto out_of_memory;
5014                 p = ptr + len_all;
5015                 memmove(p, "ANISO\0\0\0", 8);
5016                 *((Int *)(p + 8)) = (sizeof(Int) + sizeof(Aniso)) * naniso;
5017                 p += 8 + sizeof(Int);
5018                 for (i = 0; i < mp->natoms; i++) {
5019                         Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
5020                         if (ap->aniso != NULL) {
5021                                 *((Int *)p) = i;
5022                                 *((Aniso *)(p + sizeof(Int))) = *(ap->aniso);
5023                                 p += sizeof(Int) + sizeof(Aniso);
5024                         }
5025                 }
5026                 len_all += len;
5027         }
5028         
5029         /*  Array of frames  */
5030         if (nframes > 0) {
5031                 len = 8 + sizeof(Int) + sizeof(Vector) * nframes;
5032                 ptr = (char *)realloc(ptr, len_all + len);
5033                 if (ptr == NULL)
5034                         goto out_of_memory;
5035                 p = ptr + len_all;
5036                 memmove(p, "FRAME\0\0\0", 8);
5037                 *((Int *)(p + 8)) = sizeof(Vector) * nframes;
5038                 p += 8 + sizeof(Int);
5039                 for (i = 0; i < mp->natoms; i++) {
5040                         Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
5041                         if (ap->frames != NULL) {
5042                                 memmove(p, ap->frames, sizeof(Vector) * ap->nframes);
5043                                 p += sizeof(Vector) * ap->nframes;
5044                         }
5045                 }
5046                 len_all += len;
5047         }
5048         
5049         /*  Bonds, angles, dihedrals, impropers  */
5050         if (mp->nbonds > 0) {
5051                 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nbonds;
5052                 ptr = (char *)realloc(ptr, len_all + len);
5053                 if (ptr == NULL)
5054                         goto out_of_memory;
5055                 p = ptr + len_all;
5056                 memmove(p, "BOND\0\0\0\0", 8);
5057                 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nbonds;
5058                 p += 8 + sizeof(Int);
5059                 memmove(p, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
5060                 len_all += len;
5061         }
5062         if (mp->nangles > 0) {
5063                 len = 8 + sizeof(Int) + sizeof(Int) * 3 * mp->nangles;
5064                 ptr = (char *)realloc(ptr, len_all + len);
5065                 if (ptr == NULL)
5066                         goto out_of_memory;
5067                 p = ptr + len_all;
5068                 memmove(p, "ANGLE\0\0\0", 8);
5069                 *((Int *)(p + 8)) = sizeof(Int) * 3 * mp->nangles;
5070                 p += 8 + sizeof(Int);
5071                 memmove(p, mp->angles, sizeof(Int) * 3 * mp->nangles);
5072                 len_all += len;
5073         }
5074         if (mp->ndihedrals > 0) {
5075                 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->ndihedrals;
5076                 ptr = (char *)realloc(ptr, len_all + len);
5077                 if (ptr == NULL)
5078                         goto out_of_memory;
5079                 p = ptr + len_all;
5080                 memmove(p, "DIHED\0\0\0", 8);
5081                 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->ndihedrals;
5082                 p += 8 + sizeof(Int);
5083                 memmove(p, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
5084                 len_all += len;
5085         }
5086         if (mp->nimpropers > 0) {
5087                 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->nimpropers;
5088                 ptr = (char *)realloc(ptr, len_all + len);
5089                 if (ptr == NULL)
5090                         goto out_of_memory;
5091                 p = ptr + len_all;
5092                 memmove(p, "IMPROP\0\0", 8);
5093                 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->nimpropers;
5094                 p += 8 + sizeof(Int);
5095                 memmove(p, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
5096                 len_all += len;
5097         }
5098         
5099         /*  Array of residues  */
5100         if (mp->nresidues > 0) {
5101                 len = 8 + sizeof(Int) + 4 * mp->nresidues;
5102                 ptr = (char *)realloc(ptr, len_all + len);
5103                 if (ptr == NULL)
5104                         goto out_of_memory;
5105                 p = ptr + len_all;
5106                 memmove(p, "RESIDUE\0", 8);
5107                 *((Int *)(p + 8)) = 4 * mp->nresidues;
5108                 p += 8 + sizeof(Int);
5109                 memmove(p, mp->residues, 4 * mp->nresidues);
5110                 len_all += len;
5111         }
5112
5113         /*  Unit cell  */
5114         if (mp->cell != NULL) {
5115                 len = 8 + sizeof(Int) + sizeof(XtalCell);
5116                 ptr = (char *)realloc(ptr, len_all + len);
5117                 if (ptr == NULL)
5118                         goto out_of_memory;
5119                 p = ptr + len_all;
5120                 memmove(p, "CELL\0\0\0\0", 8);
5121                 *((Int *)(p + 8)) = sizeof(XtalCell);
5122                 p += 8 + sizeof(Int);
5123                 memmove(p, mp->cell, sizeof(XtalCell));
5124                 len_all += len;
5125         }
5126         
5127         /*  Symmetry operations  */
5128         if (mp->nsyms > 0) {
5129                 len = 8 + sizeof(Int) + sizeof(Transform) * mp->nsyms;
5130                 ptr = (char *)realloc(ptr, len_all + len);
5131                 if (ptr == NULL)
5132                         goto out_of_memory;
5133                 p = ptr + len_all;
5134                 memmove(p, "SYMOP\0\0\0", 8);
5135                 *((Int *)(p + 8)) = sizeof(Transform) * mp->nsyms;
5136                 p += 8 + sizeof(Int);
5137                 memmove(p, mp->syms, sizeof(Transform) * mp->nsyms);
5138                 len_all += len;
5139         }
5140         
5141         /*  Expanded atoms  */
5142         if (mp->nexatoms > 0) {
5143                 len = 8 + sizeof(Int) + sizeof(ExAtom) * mp->nexatoms;
5144                 ptr = (char *)realloc(ptr, len_all + len);
5145                 if (ptr == NULL)
5146                         goto out_of_memory;
5147                 p = ptr + len_all;
5148                 memmove(p, "EXATOM\0\0", 8);
5149                 *((Int *)(p + 8)) = sizeof(ExAtom) * mp->nexatoms;
5150                 p += 8 + sizeof(Int);
5151                 memmove(p, mp->exatoms, sizeof(ExAtom) * mp->nexatoms);
5152                 for (i = 0; i < mp->nexatoms; i++) {
5153                         /*  Clear label id  */
5154                         ((ExAtom *)p)[i].labelid = 0;
5155                 }
5156                 len_all += len;
5157         }
5158         
5159         /*  Expanded bonds  */
5160         if (mp->nexbonds > 0) {
5161                 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nexbonds;
5162                 ptr = (char *)realloc(ptr, len_all + len);
5163                 if (ptr == NULL)
5164                         goto out_of_memory;
5165                 p = ptr + len_all;
5166                 memmove(p, "EXBOND\0\0", 8);
5167                 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nexbonds;
5168                 p += 8 + sizeof(Int);
5169                 memmove(p, mp->exbonds, sizeof(Int) * 2 * mp->nexbonds);
5170                 len_all += len;
5171         }
5172         
5173         /*  Parameters  */
5174         if (mp->par != NULL) {
5175                 int type;
5176                 for (type = kFirstParType; type <= kLastParType; type++) {
5177                         const char *parname;
5178                         Int parsize, parcount;
5179                         void *parptr;
5180                         switch (type) {
5181                                 case kBondParType:
5182                                         parname = "BONDPAR\0";
5183                                         parsize = sizeof(BondPar);
5184                                         parcount = mp->par->nbondPars;
5185                                         parptr = mp->par->bondPars;
5186                                         break;
5187                                 case kAngleParType:
5188                                         parname = "ANGPAR\0\0";
5189                                         parsize = sizeof(AnglePar);
5190                                         parcount = mp->par->nanglePars;
5191                                         parptr = mp->par->anglePars;
5192                                         break;
5193                                 case kDihedralParType:
5194                                         parname = "DIHEPAR\0";
5195                                         parsize = sizeof(TorsionPar);
5196                                         parcount = mp->par->ndihedralPars;
5197                                         parptr = mp->par->dihedralPars;
5198                                         break;
5199                                 case kImproperParType:
5200                                         parname = "IMPRPAR\0";
5201                                         parsize = sizeof(TorsionPar);
5202                                         parcount = mp->par->nimproperPars;
5203                                         parptr = mp->par->improperPars;
5204                                         break;
5205                                 case kVdwParType:
5206                                         parname = "VDWPAR\0\0";
5207                                         parsize = sizeof(VdwPar);
5208                                         parcount = mp->par->nvdwPars;
5209                                         parptr = mp->par->vdwPars;
5210                                         break;
5211                                 case kVdwPairParType:
5212                                         parname = "VDWPPAR\0";
5213                                         parsize = sizeof(VdwPairPar);
5214                                         parcount = mp->par->nvdwpPars;
5215                                         parptr = mp->par->vdwpPars;
5216                                         break;
5217                                 case kVdwCutoffParType:
5218                                         parname = "VCUTPAR\0";
5219                                         parsize = sizeof(VdwCutoffPar);
5220                                         parcount = mp->par->nvdwCutoffPars;
5221                                         parptr = mp->par->vdwCutoffPars;
5222                                         break;
5223                                 default:
5224                                         continue;
5225                         }
5226                         if (parcount > 0) {
5227                                 len = 8 + sizeof(Int) + parsize * parcount;
5228                                 ptr = (char *)realloc(ptr, len_all + len);
5229                                 if (ptr == NULL)
5230                                         goto out_of_memory;
5231                                 p = ptr + len_all;
5232                                 memmove(p, parname, 8);
5233                                 *((Int *)(p + 8)) = parsize * parcount;
5234                                 p += 8 + sizeof(Int);
5235                                 memmove(p, parptr, parsize * parcount);
5236                                 len_all += len;
5237                         }
5238                 }
5239         }
5240         
5241         /*  Time stamp  */
5242         {
5243                 time_t tm = time(NULL);
5244                 len = 8 + sizeof(Int) + sizeof(Int);
5245                 ptr = (char *)realloc(ptr, len_all + len);
5246                 if (ptr == NULL)
5247                         goto out_of_memory;
5248                 p = ptr + len_all;
5249                 memmove(p, "TIME\0\0\0\0", 8);
5250                 *((Int *)(p + 8)) = sizeof(Int);
5251                 p += 8 + sizeof(Int);
5252                 *((Int *)p) = (Int)tm;
5253                 len_all += len;
5254                 if (timep != NULL)
5255                         *timep = (Int)tm;
5256         }
5257         
5258         if (outLength != NULL)
5259                 *outLength = len_all;
5260         return ptr;
5261
5262   out_of_memory:
5263     Panic("Low memory while serializing a molecule data");
5264         return NULL; /* Not reached */  
5265 }
5266
5267 #pragma mark ====== Search for bonds, angles, dihedrals, impropers ======
5268
5269 static IntGroup *
5270 sMoleculeSearchIncludingAtoms(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5271 {
5272         int i, j;
5273         Int *ip;
5274         IntGroup *gp = NULL;
5275         if (atomgroup == NULL)
5276                 return NULL;
5277         for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5278                 for (j = 0; j < nsize; j++) {
5279                         if (IntGroupLookup(atomgroup, ip[j], NULL) != 0) {
5280                                 if (gp == NULL)
5281                                         gp = IntGroupNew();
5282                                 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5283                                         Panic("Low memory while searching %s", msg);
5284                                 break;
5285                         }
5286                 }
5287         }
5288         return gp;
5289 }
5290
5291 IntGroup *
5292 MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5293 {
5294         if (mp == NULL)
5295                 return NULL;
5296         return sMoleculeSearchIncludingAtoms(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5297 }
5298
5299 IntGroup *
5300 MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5301 {
5302         if (mp == NULL)
5303                 return NULL;
5304         return sMoleculeSearchIncludingAtoms(mp->nangles, mp->angles, 3, atomgroup, "angles");
5305 }
5306
5307 IntGroup *
5308 MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5309 {
5310         if (mp == NULL)
5311                 return NULL;
5312         return sMoleculeSearchIncludingAtoms(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5313 }
5314
5315 IntGroup *
5316 MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5317 {
5318         if (mp == NULL)
5319                 return NULL;
5320         return sMoleculeSearchIncludingAtoms(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5321 }
5322
5323 static IntGroup *
5324 sMoleculeSearchAcrossAtomGroup(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5325 {
5326         int i, j;
5327         Int *ip;
5328         IntGroup *gp = NULL;
5329         if (atomgroup == NULL)
5330                 return NULL;
5331         for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5332                 int k = -1;
5333                 for (j = 0; j < nsize; j++) {
5334                         int kk;
5335                         kk = (IntGroupLookup(atomgroup, ip[j], NULL) != 0);
5336                         if (k < 0)
5337                                 k = kk;
5338                         else if (k != kk) {
5339                                 /*  This bond etc. crosses the atom group border  */
5340                                 if (gp == NULL)
5341                                         gp = IntGroupNew();
5342                                 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5343                                         Panic("Low memory while searching %s", msg);
5344                                 break;
5345                         }
5346                 }
5347         }
5348         return gp;
5349 }
5350
5351 IntGroup *
5352 MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5353 {
5354         if (mp == NULL)
5355                 return NULL;
5356         return sMoleculeSearchAcrossAtomGroup(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5357 }
5358
5359 IntGroup *
5360 MoleculeSearchAnglesAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5361 {
5362         if (mp == NULL)
5363                 return NULL;
5364         return sMoleculeSearchAcrossAtomGroup(mp->nangles, mp->angles, 3, atomgroup, "angles");
5365 }
5366
5367 IntGroup *
5368 MoleculeSearchDihedralsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5369 {
5370         if (mp == NULL)
5371                 return NULL;
5372         return sMoleculeSearchAcrossAtomGroup(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5373 }
5374
5375 IntGroup *
5376 MoleculeSearchImpropersAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5377 {
5378         if (mp == NULL)
5379                 return NULL;
5380         return sMoleculeSearchAcrossAtomGroup(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5381 }
5382
5383 /*  Subroutine for MoleculeGuessBonds. It can be also used independently, but make sure that *outNbonds/*outBonds 
5384     _correctly_ represents an array of two integers (as in mp->nbonds/mp->bonds).  */
5385 /*  Find atoms within the given "distance" from the given atom.  */
5386 /*  If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5387  the threshold distance is given by the sum of van der Waals radii times limit.  */
5388 /*  If triangle is non-zero, then only atoms with lower indexes than index are looked for.  */
5389 int
5390 MoleculeFindCloseAtoms(Molecule *mp, Int index, Double limit, Int *outNbonds, Int **outBonds, Int triangle)
5391 {
5392         Int n1, n2, j, nlim, newbond[2];
5393         Double a1, a2, alim;
5394         Vector dr, r1, r2;
5395         Atom *ap = ATOM_AT_INDEX(mp->atoms, index);
5396         n1 = ap->atomicNumber;
5397         if (n1 >= 0 && n1 < gCountElementParameters)
5398                 a1 = gElementParameters[n1].radius;
5399         else a1 = gElementParameters[6].radius;
5400         r1 = ap->r;
5401         nlim = (triangle ? index : mp->natoms);
5402         for (j = 0; j < nlim; j++) {
5403                 Atom *bp = ATOM_AT_INDEX(mp->atoms, j);
5404                 if (index == j)
5405                         continue;
5406                 n2 = bp->atomicNumber;
5407                 if (n2 >= 0 && n2 < gCountElementParameters)
5408                         a2 = gElementParameters[n2].radius;
5409                 else a2 = gElementParameters[6].radius;
5410                 r2 = bp->r;
5411                 VecSub(dr, r1, r2);
5412                 if (limit < 0)
5413                         alim = -limit;
5414                 else
5415                         alim = limit * (a1 + a2);
5416                 if (VecLength2(dr) < alim * alim) {
5417                         newbond[0] = index;
5418                         newbond[1] = j;
5419                         /*      MoleculeAddBonds(mp, 1, newbonds); */
5420                         AssignArray(outBonds, outNbonds, sizeof(Int) * 2, *outNbonds, newbond);
5421                 }
5422         }
5423         return 0;
5424 }
5425
5426 /*  Guess the bonds from the coordinates  */
5427 /*  If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5428     the threshold distance is given by the sum of van der Waals radii times limit.  */
5429 int
5430 MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds)
5431 {
5432         Int nbonds, *bonds, i, newbond[2];
5433 /*      int i, j, n1, n2;
5434         Atom *ap, *bp;
5435         Vector r1, r2, dr;
5436         Double a1, a2, alim;
5437         Int newbond[2];
5438         ElementPar *p = gElementParameters; */
5439         nbonds = 0;
5440         bonds = NULL;
5441         for (i = 0; i < mp->natoms; i++) {
5442                 MoleculeFindCloseAtoms(mp, i, limit, &nbonds, &bonds, 1);
5443                 /*
5444                 ap = ATOM_AT_INDEX(mp->atoms, i);
5445                 n1 = ap->atomicNumber;
5446                 if (n1 >= 0 && n1 < gCountElementParameters)
5447                         a1 = p[n1].radius;
5448                 else a1 = p[6].radius;
5449                 r1 = ap->r;
5450                 for (j = 0; j < i; j++) {
5451                         bp = ATOM_AT_INDEX(mp->atoms, j);
5452                         n2 = bp->atomicNumber;
5453                         if (n2 >= 0 && n2 < gCountElementParameters)
5454                                 a2 = p[n2].radius;
5455                         else a2 = p[6].radius;
5456                         r2 = bp->r;
5457                         VecSub(dr, r1, r2);
5458                         if (limit < 0)
5459                                 alim = -limit;
5460                         else
5461                                 alim = limit * (a1 + a2);
5462                         if (VecLength2(dr) < alim * alim) {
5463                                 newbond[0] = i;
5464                                 newbond[1] = j;
5465                                 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5466                         }
5467                 }
5468                 */
5469         }
5470         if (nbonds > 0) {
5471                 newbond[0] = kInvalidIndex;
5472                 newbond[1] = 0;
5473                 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5474                 nbonds--;
5475         }
5476         if (outNbonds != NULL)
5477                 *outNbonds = nbonds;
5478         if (outBonds != NULL)
5479                 *outBonds = bonds;
5480         return 0;
5481 }
5482
5483 /*  Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information  */
5484 int
5485 MoleculeRebuildTablesFromConnects(Molecule *mp)
5486 {
5487         int i, j, k, retval;
5488         Atom *ap;
5489         Int ibuf[6], *cp;
5490         
5491         __MoleculeLock(mp);
5492
5493         /*  Find bonds   */
5494         if (mp->nbonds == 0) {
5495                 for (i = 0; i < mp->natoms; i++) {
5496                         ap = ATOM_AT_INDEX(mp->atoms, i);
5497                         cp = AtomConnects(ap);
5498                         for (j = 0; j < ap->nconnects; j++) {
5499                                 k = cp[j];
5500                                 if (i >= k)
5501                                         continue;
5502                                 ibuf[0] = i;
5503                                 ibuf[1] = k;
5504                                 /*  MoleculeAddBonds() should not be used, because it assumes connects[] and
5505                                     bonds are already in sync  */
5506                                 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
5507                         /*      retval = MoleculeAddBonds(mp, 1, ibuf);
5508                                 if (retval != 0)
5509                                         goto abort; */
5510                         }
5511                 }
5512         }
5513         
5514         /*  Find angles  */
5515         if (mp->nangles == 0) {
5516                 for (i = 0; i < mp->natoms; i++) {
5517                         ap = ATOM_AT_INDEX(mp->atoms, i);
5518                         cp = AtomConnects(ap);
5519                         for (j = 0; j < ap->nconnects; j++) {
5520                                 for (k = j + 1; k < ap->nconnects; k++) {
5521                                         ibuf[0] = cp[j];
5522                                         ibuf[1] = i;
5523                                         ibuf[2] = cp[k];
5524                                         ibuf[3] = -1;
5525                                         retval = MoleculeAddAngles(mp, ibuf, NULL);
5526                                         if (retval < 0)
5527                                                 goto abort;
5528                                 }
5529                         }
5530                 }
5531         }
5532         
5533         /*  Find dihedrals  */
5534         if (mp->ndihedrals == 0) {
5535                 for (i = 0; i < mp->natoms; i++) {
5536                         ap = ATOM_AT_INDEX(mp->atoms, i);
5537                         cp = AtomConnects(ap);
5538                         for (j = 0; j < ap->nconnects; j++) {
5539                                 int jj, kk, mm, m;
5540                                 Atom *apjj;
5541                                 Int *cpjj;
5542                                 jj = cp[j];
5543                                 if (i >= jj)
5544                                         continue;
5545                                 apjj = ATOM_AT_INDEX(mp->atoms, jj);
5546                                 cpjj = AtomConnects(apjj);
5547                                 for (k = 0; k < ap->nconnects; k++) {
5548                                         if (k == j)
5549                                                 continue;
5550                                         kk = cp[k];
5551                                         for (m = 0; m < apjj->nconnects; m++) {
5552                                                 mm = cpjj[m];
5553                                                 if (mm == i || mm == kk)
5554                                                         continue;
5555                                                 ibuf[0] = kk;
5556                                                 ibuf[1] = i;
5557                                                 ibuf[2] = jj;
5558                                                 ibuf[3] = mm;
5559                                                 ibuf[4] = -1;
5560                                                 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
5561                                                 if (retval < 0)
5562                                                         goto abort;
5563                                         }
5564                                 }
5565                         }
5566                 }
5567         }
5568         
5569         /*  Find impropers  */
5570         if (mp->nimpropers == 0) {
5571                 for (i = 0; i < mp->natoms; i++) {
5572                         int i1, i2, i4, n1, n2, n4;
5573                         ap = ATOM_AT_INDEX(mp->atoms, i);
5574                         cp = AtomConnects(ap);
5575                         for (i1 = 0; i1 < ap->nconnects; i1++) {
5576                                 n1 = cp[i1];
5577                                 for (i2 = i1 + 1; i2 < ap->nconnects; i2++) {
5578                                         n2 = cp[i2];
5579                                         for (i4 = i2 + 1; i4 < ap->nconnects; i4++) {
5580                                                 n4 = cp[i4];
5581                                                 ibuf[0] = n1;
5582                                                 ibuf[1] = n2;
5583                                                 ibuf[2] = i;
5584                                                 ibuf[3] = n4;
5585                                                 ibuf[4] = -1;
5586                                                 retval = MoleculeAddImpropers(mp, ibuf, NULL);
5587                                                 if (retval < 0)
5588                                                         goto abort;
5589                                         }
5590                                 }
5591                         }
5592                 }
5593         }
5594
5595         mp->needsMDRebuild = 1;
5596         __MoleculeUnlock(mp);
5597         return 0;
5598
5599   abort:
5600         __MoleculeUnlock(mp);
5601         return retval;
5602 }
5603
5604 #pragma mark ====== Atom names ======
5605
5606 /*  Look for the n1-th atom in resno-th residue (n1 is 0-based)  */
5607 int
5608 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
5609 {
5610         int i, j, lasti;
5611         Atom *ap;
5612         if (mp == NULL || mp->natoms == 0)
5613                 return -1;
5614         lasti = -1;
5615         for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5616                 if (ap->resSeq == resno) {
5617                         lasti = i;
5618                         if (j++ == n1)
5619                                 return i;
5620                 }
5621         }
5622         if (n1 == -1)
5623                 return lasti; /* max */
5624         return -1;
5625 }
5626
5627 int
5628 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
5629 {
5630     int n;
5631     char *p;
5632         n = strtol(s, &p, 0);
5633         if (p > s) {
5634                 while (isspace(*p))
5635                         p++;
5636                 if (*p == 0) {
5637                   resName[0] = 0;
5638                   *resSeq = -1;
5639                   atomName[0] = 0;
5640                   return n;
5641                 }
5642         }
5643
5644         if ((p = strchr(s, ':')) != NULL) {
5645                 /*  Residue is specified  */
5646                 char *pp;
5647                 if ((pp = strchr(s, '.')) != NULL && pp < p) {
5648                         /*  Residue number is also specified  */
5649                         char *ppp;
5650                         n = pp - s;
5651                         *resSeq = strtol(pp + 1, &ppp, 0);
5652                         if (ppp == pp + 1)
5653                                 return -2;  /*  Bad format  */
5654                         while (isspace(*ppp))
5655                                 ppp++;
5656                         if (ppp != p)
5657                                 return -2;  /*  Bad format  */
5658                 } else {
5659                         *resSeq = -1;
5660                         /*  Check whether the "residue name" is an integer  */
5661                         n = strtol(s, &pp, 0);
5662                         if (pp > s) {
5663                                 while (isspace(*pp))
5664                                         pp++;
5665                                 if (*pp == 0 || *pp == ':') {
5666                                         *resSeq = n;
5667                                         if (*resSeq < 0)
5668                                                 return -2;  /*  Bad format  */
5669                                 }
5670                         }
5671                         if (*resSeq >= 0)
5672                                 n = 0;
5673                         else
5674                                 n = p - s;
5675                 }
5676                 if (n >= sizeof(resName))
5677                         n = sizeof(resName) - 1;
5678                 strncpy(resName, s, n);
5679                 resName[n] = 0;
5680                 p++;
5681         } else {
5682                 resName[0] = 0;
5683                 *resSeq = -1;
5684                 p = (char *)s;
5685         }
5686         strncpy(atomName, p, 4);
5687         atomName[4] = 0;
5688         return 0;
5689 }
5690
5691 /*  Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer  */
5692 int
5693 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
5694 {
5695         char resName[6];
5696         int resSeq, n;
5697         char atomName[6];
5698         /*      char *p; */
5699
5700         n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
5701         if (atomName[0] == 0) {
5702           if (n >= mp->natoms)
5703             n = -1;  /* Out of range */
5704           return n;
5705         }
5706         for (n = 0; n < mp->natoms; n++) {
5707                 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
5708                 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
5709                         && (resSeq < 0 || ap->resSeq == resSeq)
5710                         && strncmp(atomName, ap->aname, 4) == 0) {
5711                         return n;
5712                 }
5713         }
5714         return -1;  /*  Not found  */
5715 }
5716
5717 int
5718 MoleculeAreAtomsConnected(Molecule *mp, int n1, int n2)
5719 {
5720         Atom *ap;
5721         Int i, *cp;
5722         if (mp == NULL || n1 < 0 || n1 >= mp->natoms || n2 < 0 || n2 >= mp->natoms)
5723                 return 0;
5724         ap = ATOM_AT_INDEX(mp->atoms, n1);
5725         cp = AtomConnects(ap);
5726         for (i = 0; i < ap->nconnects; i++)
5727                 if (cp[i] == n2)
5728                         return 1;
5729         return 0;
5730 }
5731
5732
5733 void
5734 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
5735 {
5736         Atom *ap;
5737         int n;
5738         if (mp == NULL || index < 0 || index >= mp->natoms) {
5739                 buf[0] = 0;
5740                 return;
5741         }
5742         ap = mp->atoms + index;
5743         if (ap->resSeq != 0) {
5744                 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
5745                 buf += n;
5746                 bufsize -= n;
5747         }
5748         snprintf(buf, bufsize, "%.4s", ap->aname);
5749 }
5750
5751 #pragma mark ====== Selection ======
5752
5753 static void
5754 sMoleculeNotifyChangeSelection(Molecule *mp)
5755 {
5756         /*  TODO: Finer control of notification types may be necessary  */
5757         MoleculeCallback_notifyModification(mp, 0);
5758 }
5759
5760 void
5761 MoleculeSetSelection(Molecule *mp, IntGroup *select)
5762 {
5763         if (mp == NULL)
5764                 return;
5765         if (select != NULL)
5766                 IntGroupRetain(select);
5767         if (mp->selection != NULL)
5768                 IntGroupRelease(mp->selection);
5769         mp->selection = select;
5770         sMoleculeNotifyChangeSelection(mp);
5771 }
5772
5773 IntGroup *
5774 MoleculeGetSelection(Molecule *mp)
5775 {
5776         if (mp == NULL)
5777                 return NULL;
5778         else return mp->selection;
5779 }
5780
5781 void
5782 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
5783 {
5784         if (mp->selection == NULL)
5785                 mp->selection = IntGroupNew();
5786         if (!extending)
5787                 IntGroupClear(mp->selection);
5788         IntGroupAdd(mp->selection, n1, 1);
5789         sMoleculeNotifyChangeSelection(mp);
5790 }
5791
5792 void
5793 MoleculeUnselectAtom(Molecule *mp, int n1)
5794 {
5795         if (mp->selection != NULL)
5796                 IntGroupRemove(mp->selection, n1, 1);
5797         sMoleculeNotifyChangeSelection(mp);
5798 }
5799
5800 void
5801 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
5802 {
5803         if (mp->selection == NULL)
5804                 mp->selection = IntGroupNew();
5805         IntGroupReverse(mp->selection, n1, 1);
5806         sMoleculeNotifyChangeSelection(mp);
5807 }
5808
5809 int
5810 MoleculeIsAtomSelected(Molecule *mp, int n1)
5811 {
5812         if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
5813                 return 1;
5814         else return 0;
5815 }
5816
5817 int
5818 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
5819 {
5820         if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
5821                 return 1;
5822         else return 0;
5823 }
5824
5825 IntGroup *
5826 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
5827 {
5828         int status;
5829         IntGroup *remain, *ig1, *ig2;
5830         ig1 = ig2 = NULL;
5831         remain = IntGroupNewFromIntGroup(remove);
5832         if (remain == NULL)
5833                 status = -1;
5834         else
5835                 status = IntGroupReverse(remain, 0, mp->natoms);
5836         if (status == 0) {
5837                 ig1 = IntGroupNew();
5838                 if (ig1 == NULL)
5839                         status = -1;
5840                 else
5841                         status = IntGroupDifference(selection, remove, ig1);
5842         }
5843         if (status == 0) {
5844                 ig2 = IntGroupNew();
5845                 if (ig2 == NULL)
5846                         status = -1;
5847                 else
5848                         status = IntGroupDeconvolute(ig1, remain, ig2);
5849         }
5850         if (remain != NULL)
5851                 IntGroupRelease(remain);
5852         if (ig1 != NULL)
5853                 IntGroupRelease(ig1);
5854         if (status == 0)
5855                 return ig2;
5856         else {
5857                 if (ig2 != NULL)
5858                         IntGroupRelease(ig2);
5859                 return NULL;
5860         }
5861 }
5862
5863 #pragma mark ====== Atom Equivalence ======
5864
5865 struct sEqList {
5866         int i[2];
5867         struct sEqList *next;
5868         struct sEqList *link;
5869 };
5870
5871 static struct sEqList *sListBase = NULL;
5872 static struct sEqList *sListFree = NULL;
5873
5874 static struct sEqList *
5875 sAllocEqList(void)
5876 {
5877         struct sEqList *lp;
5878         if (sListFree != NULL) {
5879                 lp = sListFree;
5880                 sListFree = lp->next;
5881                 lp->i[0] = lp->i[1] = 0;
5882                 lp->next = NULL;
5883                 return lp;
5884         }
5885         lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
5886         lp->link = sListBase;
5887         sListBase = lp;
5888         return lp;
5889 }
5890
5891 static void
5892 sFreeEqList(struct sEqList *list)
5893 {
5894         list->next = sListFree;
5895         sListFree = list;
5896 }
5897
5898 static void
5899 sDeallocateEqLists(void)
5900 {
5901         struct sEqList *lp, *lp_link;
5902         for (lp = sListBase; lp != NULL; lp = lp_link) {
5903                 lp_link = lp->link;
5904                 free(lp);
5905         }
5906         sListBase = NULL;
5907         sListFree = NULL;
5908 }
5909
5910 static int
5911 sExistInEqList(int i, int idx, struct sEqList *list)
5912 {
5913         while (list != NULL) {
5914                 if (list->i[idx] == i)
5915                         return 1;
5916                 list = list->next;
5917         }
5918         return 0;
5919 }
5920
5921 static struct sEqList *
5922 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
5923 {
5924         Atom *api, *apj;
5925         struct sEqList *list1, *list2;
5926         Int ii, jj, ni, nj, *cpi, *cpj;
5927         api = ATOM_AT_INDEX(mol->atoms, i);
5928         apj = ATOM_AT_INDEX(mol->atoms, j);
5929         if (api->atomicNumber != apj->atomicNumber)
5930                 return NULL;
5931         list1 = sAllocEqList();
5932         if (list1 == NULL)
5933                 return NULL;
5934         list1->i[0] = i;
5935         list1->i[1] = j;
5936         list1->next = list;
5937         if (i == j || (db[i] != NULL && db[i] == db[j]))
5938                 return list1;
5939         cpi = AtomConnects(api);
5940         cpj = AtomConnects(apj);
5941         for (ni = 0; ni < api->nconnects; ni++) {
5942                 ii = cpi[ni];
5943                 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
5944                         continue;
5945                 if (sExistInEqList(ii, 0, list1))
5946                         continue;
5947                 list2 = NULL;
5948                 for (nj = 0; nj < apj->nconnects; nj++) {
5949                         jj = cpj[nj];
5950                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
5951                                 continue;
5952                         if (sExistInEqList(jj, 1, list1))
5953                                 continue;
5954                         list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
5955                         if (list2 != NULL)
5956                                 break;
5957                 }
5958                 if (list2 == NULL) {
5959                         sFreeEqList(list1);
5960                         return NULL;    /*  No equivalent to ii  */
5961                 }
5962                 list1 = list2;      /*  ii is OK, try next  */
5963         }
5964         return list1;
5965 }
5966
5967 int
5968 sDBInclude(Int *ip, int i)
5969 {
5970         int j;
5971         if (ip == NULL)
5972                 return -1;
5973         for (j = ip[0] - 1; j >= 0; j--) {
5974                 if (ip[j] == i)
5975                         return j;
5976         }
5977         return -1;
5978 }
5979
5980 Int *
5981 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
5982 {
5983         Int **db;  /*  List of equivalents for each atom  */
5984         Int *ip, *result;
5985         Atom *api, *apj, *apk;
5986         Int *cpi, *cpj, *ibuf, nibuf;
5987         int i, j, k, ii, jj, kk;
5988         if (mol == NULL || mol->natoms == 0)
5989                 return NULL;
5990         db = (Int **)calloc(sizeof(Int *), mol->natoms);
5991         ibuf = NULL;
5992         nibuf = 0;
5993
5994         /*  Find the equivalent univalent atoms  */
5995         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
5996                 if (api->nconnects < 2)
5997                         continue;
5998                 cpi = AtomConnects(api);
5999                 for (j = 0; j < api->nconnects; j++) {
6000                         Int n;
6001                         n = 0;
6002                         jj = cpi[j];
6003                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6004                                 continue;
6005                         AssignArray(&ibuf, &nibuf, sizeof(Int), n, &jj);
6006                         n++;
6007                         apj = ATOM_AT_INDEX(mol->atoms, jj);
6008                         if (apj->nconnects != 1 || db[jj] != NULL)
6009                                 continue;
6010                         cpj = AtomConnects(apj);
6011                         for (k = j + 1; k < api->nconnects; k++) {
6012                                 kk = cpj[k];
6013                                 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
6014                                         continue;
6015                                 apk = ATOM_AT_INDEX(mol->atoms, kk);
6016                                 if (apk->nconnects != 1 || db[kk] != NULL)
6017                                         continue;
6018                                 if (apj->atomicNumber == apk->atomicNumber) {
6019                                         AssignArray(&ibuf, &nibuf, sizeof(Int), n, &kk);
6020                                         n++;
6021                                 }
6022                         }
6023                         if (n > 1) {
6024                                 ip = (Int *)calloc(sizeof(Int), n + 1);
6025                                 if (ip == NULL)
6026                                         return NULL;
6027                                 ip[0] = n;
6028                                 memmove(ip + 1, ibuf, sizeof(Int) * n);
6029                                 for (k = 0; k < n; k++)
6030                                         db[ip[k + 1]] = ip;
6031                         }
6032                 }
6033         }
6034         if (ibuf != NULL) {
6035                 free(ibuf);
6036                 ibuf = NULL;
6037         }
6038         
6039         /*  Try matching (i,j) pair  */
6040         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6041                 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
6042                         continue;
6043                 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
6044                         struct sEqList *list;
6045                         if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
6046                                 continue;
6047                         if (api->atomicNumber != apj->atomicNumber)
6048                                 continue;  /*  Different elements do not match  */
6049                         if (db[i] != NULL && db[i] == db[j])
6050                                 continue;  /*  Already equivalent  */
6051                         list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
6052                         if (list == NULL)
6053                                 continue;  /*  (i,j) do not match  */
6054                         while (list != NULL) {
6055                                 ii = list->i[0];
6056                                 jj = list->i[1];
6057                                 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
6058                                         /*  Merge db[ii] and db[jj]  */
6059                                         k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
6060                                         ip = (Int *)calloc(sizeof(Int), k + 1);
6061                                         if (ip == NULL)
6062                                                 return NULL;  /*  Out of memory  */
6063                                         if (db[ii] == NULL) {
6064                                                 ip[1] = ii;
6065                                                 k = 2;
6066                                         } else {
6067                                                 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
6068                                                 k = db[ii][0] + 1;
6069                                         }
6070                                         if (db[jj] == NULL) {
6071                                                 ip[k++] = jj;
6072                                         } else {
6073                                                 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
6074                                                 k += db[jj][0];
6075                                         }
6076                                         ip[0] = k - 1;
6077                                         /*  Free old ones  */
6078                                         if (db[ii] != NULL)
6079                                                 free(db[ii]);
6080                                         if (db[jj] != NULL)
6081                                                 free(db[jj]);
6082                                         for (k = 0; k < ip[0]; k++)
6083                                                 db[ip[k + 1]] = ip;
6084                                         if (0) {
6085                                                 /*  For debug  */
6086                                                 printf("(%d,%d) matched: ", ii, jj);
6087                                                 for (k = 0; k < ip[0]; k++) {
6088                                                         printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
6089                                                 }
6090                                                 printf("]\n");
6091                                         }
6092                                 }
6093                                 list = list->next;
6094                         }
6095                 }
6096         }
6097         
6098         /*  Record the equivalent atoms with the lowest index for each atom  */
6099         result = (Int *)calloc(sizeof(Int), mol->natoms);
6100         for (i = 0; i < mol->natoms; i++)
6101                 result[i] = -1;
6102         for (i = 0; i < mol->natoms; i++) {
6103                 if (result[i] >= 0 || (ip = db[i]) == NULL)
6104                         continue;
6105                 k = mol->natoms;
6106                 for (j = 0; j < ip[0]; j++) {
6107                         kk = ip[j + 1];
6108                         if (kk < k)
6109                                 k = kk;
6110                 }
6111                 for (j = 0; j < ip[0]; j++) {
6112                         result[ip[j + 1]] = k;
6113                         db[ip[j + 1]] = NULL;
6114                 }
6115                 free(ip);
6116         }
6117         sDeallocateEqLists();
6118         return result;
6119 }
6120
6121 #pragma mark ====== Symmetry expansion ======
6122
6123 int
6124 MoleculeGetTransformForSymop(Molecule *mp, Symop symop, Transform *tf, int is_cartesian)
6125 {
6126         Transform t;
6127         if (mp == NULL || mp->cell == NULL)
6128                 return -1;
6129         if (symop.sym >= mp->nsyms)
6130                 return -2;
6131         memmove(*tf, mp->syms[symop.sym], sizeof(Transform));
6132         (*tf)[9] += symop.dx;
6133         (*tf)[10] += symop.dy;
6134         (*tf)[11] += symop.dz;
6135         if (is_cartesian) {
6136                 TransformMul(t, *tf, mp->cell->rtr);
6137                 TransformMul(*tf, mp->cell->tr, t);
6138         }
6139         return 0;
6140 }
6141
6142 int
6143 MoleculeGetSymopForTransform(Molecule *mp, const Transform tf, Symop *symop, int is_cartesian)
6144 {
6145         Transform t;
6146         int i, j, n[3];
6147         if (mp == NULL || mp->cell == NULL)
6148                 return -1;
6149         if (mp->nsyms == 0)
6150                 return -2;
6151         if (is_cartesian) {
6152                 TransformMul(t, tf, mp->cell->tr);
6153                 TransformMul(t, mp->cell->rtr, t);
6154         } else {
6155                 memmove(t, tf, sizeof(Transform));
6156         }
6157         for (i = 0; i < mp->nsyms; i++) {
6158                 Transform *tp = mp->syms + i;
6159                 for (j = 0; j < 9; j++) {
6160                         if (fabs((*tp)[j] - t[j]) > 1e-4)
6161                                 break;
6162                 }
6163                 if (j == 9) {
6164                         for (j = 9; j < 12; j++) {
6165                                 double f1 = t[j] - (*tp)[j];
6166                                 double f2 = floor(f1 + 0.5);
6167                                 if (fabs(f1 - f2) > 1e-4)
6168                                         break;
6169                                 n[j - 9] = f2;
6170                         }
6171                         if (j == 12) {
6172                                 /*  Found  */
6173                                 symop->sym = i;
6174                                 symop->dx = n[0];
6175                                 symop->dy = n[1];
6176                                 symop->dz = n[2];
6177                                 symop->alive = (SYMOP_ALIVE((*symop)) != 0);
6178                                 return 0;
6179                         }
6180                 }
6181         }
6182         return -3;  /*  Not found  */
6183 }
6184
6185 int
6186 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
6187 {
6188         if (mp == NULL)
6189                 return 1;
6190         if (symop.sym >= mp->nsyms)
6191                 return 2;
6192         if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
6193                 TransformVec(vpout, mp->cell->rtr, vpin);
6194                 TransformVec(vpout, mp->syms[symop.sym], vpout);
6195                 vpout->x += symop.dx;
6196                 vpout->y += symop.dy;
6197                 vpout->z += symop.dz;
6198                 TransformVec(vpout, mp->cell->tr, vpout);
6199         } else {
6200                 TransformVec(vpout, mp->syms[symop.sym], vpin);
6201                 vpout->x += symop.dx;
6202                 vpout->y += symop.dy;
6203                 vpout->z += symop.dz;
6204         }
6205         return 0;
6206 }
6207
6208 /*  Add expanded atoms. Returns the number of newly created atoms.
6209         If indices is non-NULL, it should be an array of Int with at least 
6210         IntGroupGetCount(group) entries, and on return it contains the
6211     indices of the expanded atoms (may be existing atoms if the expanded
6212     atoms are already present)  */
6213 int
6214 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group, Int *indices)
6215 {
6216         int i, n, n0, n1, count, *table;
6217         Atom *ap;
6218         IntGroupIterator iter;
6219         Transform tr;
6220
6221         if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
6222                 return -1;
6223         if (symop.sym >= mp->nsyms)
6224                 return -2;
6225
6226         /*  Create atoms, with avoiding duplicates  */
6227         n0 = n1 = mp->natoms;
6228         table = (int *)malloc(sizeof(int) * n0);
6229         if (table == NULL)
6230                 return -3;
6231         for (i = 0; i < n0; i++)
6232                 table[i] = -1;
6233         IntGroupIteratorInit(group, &iter);
6234         MoleculeGetTransformForSymop(mp, symop, &tr, 0);
6235         __MoleculeLock(mp);
6236         for (i = 0; i < count; i++) {
6237                 int n2, base;
6238                 Symop symop1;
6239                 Atom *ap2;
6240                 Vector nr, dr;
6241                 n = IntGroupIteratorNext(&iter);
6242                 ap = ATOM_AT_INDEX(mp->atoms, n);
6243                 if (SYMOP_ALIVE(ap->symop)) {
6244                         /*  Calculate the cumulative symop  */
6245                         Transform t1;
6246                         MoleculeGetTransformForSymop(mp, ap->symop, &t1, 0);
6247                         TransformMul(t1, tr, t1);
6248                         if (MoleculeGetSymopForTransform(mp, t1, &symop1, 0) != 0) {
6249                                 if (indices != NULL)
6250                                         indices[i] = -1;
6251                                 continue;  /*  Skip this atom  */
6252                         }
6253                         base = ap->symbase;
6254                 } else {
6255                         symop1 = symop;
6256                         base = n;
6257                 }
6258                 /*  Is this expansion already present?  */
6259                 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
6260                         if (ap2->symbase == base && SYMOP_EQUAL(symop1, ap2->symop))
6261                                 break;
6262                 }
6263                 if (n2 < n0) {
6264                         /*  If yes, then skip it  */
6265                         if (indices != NULL)
6266                                 indices[i] = n2;
6267                         continue;
6268                 }
6269                 /*  Is the expanded position coincides with itself?  */
6270                 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
6271                 VecSub(dr, ap->r, nr);
6272                 if (VecLength2(dr) < 1e-6) {
6273                         /*  If yes, then this atom is included but no new atom is created  */
6274                         table[n] = n;
6275                         if (indices != NULL)
6276                                 indices[i] = n;
6277                 } else {
6278                         /*  Create a new atom  */
6279                         Atom newAtom;
6280                         AtomDuplicate(&newAtom, ap);
6281                         MoleculeCreateAnAtom(mp, &newAtom, -1);
6282                         AtomClean(&newAtom);
6283                         ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
6284                         ap2->r = nr;
6285                         ap2->symbase = n;
6286                         ap2->symop = symop;
6287                         ap2->symop.alive = (symop.dx != 0 || symop.dy != 0 || symop.dz != 0 || symop.sym != 0);
6288                         table[n] = n1;  /*  The index of the new atom  */
6289                         if (indices != NULL)
6290                                 indices[i] = n1;
6291                         n1++;
6292                 }
6293         }
6294         IntGroupIteratorRelease(&iter);
6295
6296         /*  Create bonds  */
6297         for (i = 0; i < n0; i++) {
6298                 int b[2];
6299                 Int *cp;
6300                 b[0] = table[i];
6301                 if (b[0] < 0 || b[0] == i)
6302                         continue;
6303                 ap = ATOM_AT_INDEX(mp->atoms, i);
6304                 cp = AtomConnects(ap);
6305                 for (n = 0; n < ap->nconnects; n++) {
6306                         b[1] = table[cp[n]];
6307                         if (b[1] < 0)
6308                                 continue;
6309                         if (b[1] > n0 && b[0] > b[1])
6310                                 continue;
6311                         MoleculeAddBonds(mp, 1, b);
6312                 }
6313         }
6314         mp->needsMDRebuild = 1;
6315         __MoleculeUnlock(mp);
6316         free(table);
6317         return n1 - n0;  /*  The number of added atoms  */
6318 }
6319
6320 /*  Recalculate the coordinates of symmetry expanded atoms.
6321         Returns the number of affected atoms.
6322     If group is non-NULL, only the expanded atoms whose base atoms are in the
6323     given group are considered.
6324         If groupout and vpout are non-NULL, the indices of the affected atoms
6325         and the original positions are returned (for undo operation).
6326         The pointers returned in *groupout and *vpout must be released and 
6327         free()'ed by the caller  */
6328 int
6329 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
6330 {
6331         int i, count;
6332         Atom *ap, *bp;
6333         if (mp == NULL || mp->natoms == 0 || mp->nsyms == 0)
6334                 return 0;
6335         if (groupout != NULL && vpout != NULL) {
6336                 *groupout = IntGroupNew();
6337                 if (*groupout == NULL)
6338                         return -1;
6339                 *vpout = (Vector *)malloc(sizeof(Vector) * mp->natoms);
6340                 if (*vpout == NULL) {
6341                         IntGroupRelease(*groupout);
6342                         return -1;
6343                 }
6344         } else groupout = NULL; /* To simplify test for validity of groupout/vpout */
6345         
6346         __MoleculeLock(mp);
6347         count = 0;
6348         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6349                 Vector nr, dr;
6350                 if (!SYMOP_ALIVE(ap->symop))
6351                         continue;
6352                 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
6353                         continue;
6354                 bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6355                 MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
6356                 VecSub(dr, nr, ap->r);
6357                 if (VecLength2(dr) < 1e-20)
6358                         continue;
6359                 if (groupout != NULL) {
6360                         (*vpout)[count] = ap->r;
6361                         IntGroupAdd(*groupout, i, 1);
6362                 }
6363                 ap->r = nr;
6364                 count++;
6365         }
6366         mp->needsMDCopyCoordinates = 1;
6367         __MoleculeUnlock(mp);
6368         if (groupout != NULL) {
6369                 if (count == 0) {
6370                         free(*vpout);
6371                         *vpout = NULL;
6372                         IntGroupRelease(*groupout);
6373                         *groupout = NULL;
6374                 } else {
6375                         *vpout = (Vector *)realloc(*vpout, sizeof(Vector) * count);
6376                 }
6377         }
6378         return count;
6379 }
6380
6381 #pragma mark ====== Show/hide atoms ======
6382
6383 static void
6384 sMoleculeNotifyChangeAppearance(Molecule *mp)
6385 {
6386         /*  TODO: Finer control of notification types may be necessary  */
6387         MoleculeCallback_notifyModification(mp, 0);
6388 }
6389
6390
6391 static void
6392 sMoleculeUnselectHiddenAtoms(Molecule *mp)
6393 {
6394         int i;
6395         if (mp == NULL || mp->selection == NULL)
6396                 return;
6397         for (i = 0; i < mp->natoms; i++) {
6398                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6399                 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
6400                         IntGroupRemove(mp->selection, i, 1);
6401         }
6402         sMoleculeNotifyChangeAppearance(mp);
6403 }
6404
6405 int
6406 MoleculeShowAllAtoms(Molecule *mp)
6407 {
6408         int i;
6409         if (mp == NULL)
6410                 return 0;
6411         for (i = 0; i < mp->natoms; i++) {
6412                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6413                 ap->exflags &= ~kAtomHiddenFlag;
6414         }
6415         sMoleculeNotifyChangeAppearance(mp);
6416         return 1;
6417 }
6418
6419 int
6420 MoleculeShowReverse(Molecule *mp)
6421 {
6422         int i;
6423         if (mp == NULL)
6424                 return 0;
6425         for (i = 0; i < mp->natoms; i++) {
6426                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6427                 ap->exflags ^= kAtomHiddenFlag;
6428         }
6429         sMoleculeUnselectHiddenAtoms(mp);
6430         sMoleculeNotifyChangeAppearance(mp);
6431         return 1;
6432 }
6433
6434 int
6435 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
6436 {
6437         int i;
6438         if (mp == NULL || ig == NULL)
6439                 return 0;
6440         for (i = 0; i < mp->natoms; i++) {
6441                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6442                 if (ap->exflags & kAtomHiddenFlag)
6443                         continue;  /*  Already hidden  */
6444                 if (IntGroupLookupPoint(ig, i) >= 0)
6445                         ap->exflags |= kAtomHiddenFlag;
6446         }
6447         sMoleculeUnselectHiddenAtoms(mp);
6448         sMoleculeNotifyChangeAppearance(mp);
6449         return 1;
6450 }
6451
6452 #pragma mark ====== Reversible Editing ======
6453
6454 /*
6455 static void
6456 sMoleculeNotifyModification(Molecule *mp)
6457 {
6458         **  TODO: Finer control of notification types may be necessary  **
6459         MoleculeCallback_notifyModification(mp, 0);
6460 }
6461 */
6462
6463 /*  Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup  */
6464 int
6465 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
6466 {
6467         int n1, n2, n3, i;
6468         if (where == NULL) {
6469                 /*  Append the new objects at the end  */
6470                 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
6471                 return 0;
6472         }
6473         n1 = IntGroupGetCount(where);  /*  Position to get new object  */
6474         n2 = nobjs;                    /*  Position to get old object  */
6475         n3 = n1 + n2;                  /*  Position to place new/old object  */
6476         for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
6477                 int start = IntGroupGetStartPoint(where, i);
6478                 int end = IntGroupGetEndPoint(where, i);
6479                 if (end < n3) {
6480                         /*  old[end-(n3-n2)..n2-1] is moved to old[end..n3-1]  */
6481                         memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
6482                         n2 = end - (n3 - n2);
6483                         n3 = end;
6484                 }
6485                 /*  new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1]  */
6486                 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
6487                 n3 -= end - start;
6488                 n1 -= end - start;
6489         }
6490         return 0;
6491 }
6492
6493 /*  Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
6494 int
6495 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6496 {
6497         int n1, n2, n3, start, end, i;
6498         if (objs == NULL || where == NULL)
6499                 return 1;  /*  Bad argument  */
6500         n1 = 0;  /*  Position to move remaining elements to */
6501         n2 = 0;  /*  Position to move remaining elements from  */
6502         n3 = 0;  /*  Position to move removed elements to  */
6503         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6504                 end = IntGroupGetEndPoint(where, i);
6505                 if (n2 < start) {
6506                         /*  Move (start - n2) elements from objs[n2] to objs[n1]  */
6507                         if (n1 < n2)
6508                                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
6509                         n1 += start - n2;
6510                         n2 = start;
6511                 }
6512                 /*  Move (end - start) elements from objs[n2] to clip[n3]  */
6513                 if (clip != NULL)
6514                         memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
6515                 n3 += (end - start);
6516                 n2 += (end - start);
6517         }
6518         /*  Move (nobjs - n2) elements from objs[n2] to objs[n1]  */
6519         if (nobjs > n2)
6520                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
6521         return 0;
6522 }
6523
6524 /*  Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
6525 int
6526 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6527 {
6528         int n1, start, end, i;
6529         if (objs == NULL || where == NULL)
6530                 return 1;  /*  Bad argument  */
6531         n1 = 0;  /*  Position to move removed elements to  */
6532         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6533                 end = IntGroupGetEndPoint(where, i);
6534                 /*  Copy (end - start) elements from objs[start] to clip[n1]  */
6535                 if (clip != NULL)
6536                         memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
6537                 n1 += (end - start);
6538         }
6539         return 0;
6540 }
6541
6542 /*  Create a new atom with no bonding information. ap must _not_ be inside the given molecule
6543    (Use AtomDuplicate() first) */
6544 int
6545 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
6546 {
6547     Atom *ap1, *api;
6548         int i;
6549         if (mp == NULL || ap == NULL || mp->noModifyTopology)
6550                 return -1;
6551         __MoleculeLock(mp);
6552         if (pos < 0 || pos >= mp->natoms)
6553                 pos = mp->natoms;
6554         ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
6555         if (ap1 == NULL)
6556                 goto error;  /*  Out of memory  */
6557         ap1 = ATOM_AT_INDEX(mp->atoms, pos);
6558         if (pos < mp->natoms - 1) {
6559                 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6560         }
6561         if (AtomDuplicate(ap1, ap) == NULL) {
6562                 /*  Cannot duplicate: restore the original state  */
6563                 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6564                 mp->natoms--;
6565                 goto error;
6566         }
6567         ap1->nconnects = 0;
6568         if (ap1->resSeq >= mp->nresidues)
6569                 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
6570         if (ap1->resName[0] == 0)
6571           strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
6572         if (ap1->segName[0] == 0)
6573           strncpy(ap1->segName, "MAIN", 4);
6574         if (pos < mp->natoms - 1) {
6575                 /*  Renumber the connect table, bonds, angles, etc. */
6576                 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
6577                         int j;
6578                         Int *cp;
6579                         for (j = 0; j < api->nconnects; j++) {
6580                                 cp = AtomConnects(api);
6581                                 if (cp[j] >= pos)
6582                                         cp[j]++;
6583                         }
6584                 }
6585                 for (i = 0; i < mp->nbonds * 2; i++) {
6586                         if (mp->bonds[i] >= pos)
6587                                 mp->bonds[i]++;
6588                 }
6589                 for (i = 0; i < mp->nangles * 3; i++) {
6590                         if (mp->angles[i] >= pos)
6591                                 mp->angles[i]++;
6592                 }
6593                 for (i = 0; i < mp->ndihedrals * 4; i++) {
6594                         if (mp->dihedrals[i] >= pos)
6595                                 mp->dihedrals[i]++;
6596                 }
6597                 for (i = 0; i < mp->nimpropers * 4; i++) {
6598                         if (mp->impropers[i] >= pos)
6599                                 mp->impropers[i]++;
6600                 }
6601         }
6602         mp->nframes = -1;  /*  Should be recalculated later  */
6603         MoleculeIncrementModifyCount(mp);
6604         mp->needsMDRebuild = 1;
6605         __MoleculeUnlock(mp);
6606         return pos;
6607 error:
6608         __MoleculeUnlock(mp);
6609         return -1;
6610 }
6611
6612 /*  Merge two molecules. We use this procedure for all add-atom operations.  */
6613 /*  resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
6614 int
6615 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, int resSeqOffset)
6616 {
6617         Int nsrc, ndst;
6618         Int i, j, n1, n2, n3, n4, *cp;
6619         Int *new2old, *old2new;
6620         Atom *ap;
6621         if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
6622                 return 0;  /*  Do nothing  */
6623
6624         if (dst->noModifyTopology)
6625                 return 1;  /*  Prohibited operation  */
6626
6627         if (where != NULL && IntGroupGetCount(where) != src->natoms)
6628                 return 1;  /*  Bad parameter  */
6629
6630         __MoleculeLock(dst);
6631         nsrc = src->natoms;
6632         ndst = dst->natoms;
6633         if (resSeqOffset < 0)
6634                 resSeqOffset = 0;
6635
6636         /*  Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
6637             and ndst..ndst+nsrc-1 are for atoms in src.  */ 
6638         new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
6639         if (new2old == NULL)
6640                 goto panic;
6641         old2new = new2old + ndst + nsrc;
6642         n1 = 0;  /*  dst index  */
6643         n2 = 0;  /*  src index  */
6644         n3 = 0;  /*  "merged" index  */
6645         i = 0;
6646         while (n1 < ndst || n2 < nsrc) {
6647                 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
6648                         n4 = ndst - n1;
6649                 else n4 -= n3;
6650                 /*  n4 elements from dst[n1] will go to merged[n3]  */
6651                 for (j = 0; j < n4; j++) {
6652                         old2new[n1 + j] = n3 + j;
6653                         new2old[n3 + j] = n1 + j;
6654                 }
6655                 n3 += n4;
6656                 n1 += n4;
6657                 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
6658                         n4 = nsrc - n2;
6659                 /*  n4 elements from src[n2] will go to merged[n3]  */
6660                 for (j = 0; j < n4; j++) {
6661                         old2new[ndst + n2 + j] = n3 + j;
6662                         new2old[n3 + j] = ndst + n2 + j;
6663                 }
6664                 n3 += n4;
6665                 n2 += n4;
6666                 i++;
6667         }
6668
6669         /*  Expand the destination array  */
6670         if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
6671                 goto panic;
6672
6673         /*  Move the atoms  */
6674         if (where == NULL) {
6675                 /*  Duplicate atoms to the end of the destination array  */
6676                 for (i = 0; i < nsrc; i++) {
6677                         if (AtomDuplicate(ATOM_AT_INDEX(dst->atoms, ndst + i), ATOM_AT_INDEX(src->atoms, i)) == NULL)
6678                                 goto panic;
6679                 }
6680         //      memmove(ATOM_AT_INDEX(dst->atoms, ndst), src->atoms, gSizeOfAtomRecord * nsrc);
6681         } else {
6682                 /*  Duplicate to a temporary storage and then insert  */
6683                 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
6684                 if (tempatoms == NULL)
6685                         goto panic;
6686                 for (i = 0; i < nsrc; i++) {
6687                         if (AtomDuplicate(ATOM_AT_INDEX(tempatoms, i), ATOM_AT_INDEX(src->atoms, i)) == NULL)
6688                                 goto panic;
6689                 }
6690                 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
6691                         goto panic;
6692                 free(tempatoms);
6693         }
6694         dst->natoms = ndst + nsrc;
6695
6696         /*  Renumber the atom indices in connect[] and symbase, and modify the residue numbers  */
6697         for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
6698                 if (new2old[i] < ndst) {
6699                         /*  This atom is from dst  */
6700                         n1 = 0;
6701                 } else {
6702                         /*  This atom is from src  */
6703                         n1 = ndst;  /*  Offset to the internal number  */
6704                         if (ap->resSeq != 0)
6705                                 ap->resSeq += resSeqOffset;  /*  Modify residue number  */
6706                 }
6707                 cp = AtomConnects(ap);
6708                 for (j = 0; j < ap->nconnects; j++)
6709                         cp[j] = old2new[cp[j] + n1];
6710                 if (SYMOP_ALIVE(ap->symop))
6711                         ap->symbase = old2new[ap->symbase + n1];
6712         }
6713         
6714         /*  Move the bonds, angles, dihedrals, impropers  */
6715         for (i = 0; i < 4; i++) {
6716                 Int *nitems, *nitems_src;
6717                 Int **items, **items_src;
6718                 Int nsize;  /*  Number of Ints in one element  */
6719                 switch (i) {
6720                         case 0:
6721                                 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
6722                         case 1:
6723                                 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
6724                         case 2:
6725                                 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
6726                         case 3:
6727                                 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
6728                 }
6729                 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
6730                 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
6731                 /*  Keep the old number of entries in dst, because it is updated by AssignArray()  */
6732                 n1 = *nitems;
6733                 /*  Also keep the old number of entries in src, in case src and dst point the same molecule  */
6734                 n2 = *nitems_src;
6735                 /*  Expand the array  */
6736                 if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
6737                         goto panic;
6738                 /*  Copy the items  */
6739                 memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
6740                 /*  Renumber  */
6741                 for (j = 0; j < n1 * nsize; j++)
6742                         (*items)[j] = old2new[(*items)[j]];
6743                 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
6744                         (*items)[j] = old2new[(*items)[j] + ndst];
6745         }
6746         
6747         /*  Merge parameters  */
6748         if (src->par != NULL) {
6749                 UnionPar *up1, *up2;
6750                 int type;
6751                 IntGroup *ig;
6752                 if (dst->par == NULL)
6753                         dst->par = ParameterNew();
6754                 else {
6755                         /*  Renumber existing parameters  */
6756                         for (type = kFirstParType; type <= kLastParType; type++) {
6757                                 n1 = ParameterGetCountForType(dst->par, type);
6758                                 for (i = 0; i < n1; i++) {
6759                                         up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
6760                                         ParameterRenumberAtoms(type, up1, ndst, old2new);
6761                                 }
6762                         }
6763                 }
6764                 ig = IntGroupNew();
6765                 for (type = kFirstParType; type <= kLastParType; type++) {
6766                         n1 = ParameterGetCountForType(src->par, type);
6767                         n2 = ParameterGetCountForType(dst->par, type);
6768                         if (n1 == 0)
6769                                 continue;
6770                         /*  Determine which parameter should be copied from src to dst  */
6771                         for (i = 0; i < n1; i++) {
6772                                 UInt types[4];
6773                                 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
6774                                 n3 = ParameterGetAtomTypes(type, up1, types);
6775                                 for (j = 0; j < n3; j++) {
6776                                         /*  If it includes explicit atom index, then it should be copied  */
6777                                         if (types[j] < kAtomTypeMinimum) {
6778                                                 IntGroupAdd(ig, i, 1);
6779                                                 break;
6780                                         }
6781                                 }
6782                                 if (j == n3) {
6783                                         for (j = 0; j < n2; j++) {
6784                                                 up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
6785                                                 if (ParameterCompare(up1, up2, type))
6786                                                         break;
6787                                         }
6788                                         if (j >= n2)
6789                                                 /*  This is an unknown parameter; should be copied  */
6790                                                 IntGroupAdd(ig, i, 1);
6791                                 }
6792                         }
6793                         n1 = IntGroupGetCount(ig);
6794                         if (n1 == 0)
6795                                 continue;
6796                         up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
6797                         if (up1 == NULL)
6798                                 goto panic;
6799                         /*  Copy parameters and renumber indices if necessary  */
6800                         for (i = 0; i < n1; i++) {
6801                                 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
6802                                 if (up2 == NULL)
6803                                         continue;
6804                                 up1[i] = *up2;
6805                                 ParameterRenumberAtoms(type, up1 + i, nsrc, old2new + ndst);
6806                         }
6807                         /*  Merge parameters  */
6808                         IntGroupClear(ig);
6809                         IntGroupAdd(ig, n2, n1);
6810                         if (ParameterInsert(dst->par, type, up1, ig) < n1)
6811                                 goto panic;
6812                         IntGroupClear(ig);
6813                         free(up1);
6814                 }
6815                 IntGroupRelease(ig);
6816         }
6817         
6818         /*  Copy the residues if necessary  */
6819         /*  src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
6820             However, 1+resSeqOffset should not overwrite the existing residue in dst;
6821                 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1].  */
6822         n1 = dst->nresidues;
6823         if (1 + resSeqOffset < n1) {
6824                 n2 = n1;
6825         } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
6826         if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
6827                 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
6828                         goto panic;
6829                 memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
6830         }
6831
6832         MoleculeCleanUpResidueTable(dst);
6833         
6834         free(new2old);
6835         dst->nframes = -1;  /*  Should be recalculated later  */
6836
6837         MoleculeIncrementModifyCount(dst);
6838         dst->needsMDRebuild = 1;
6839         __MoleculeUnlock(dst);
6840         return 0;
6841
6842   panic:
6843         __MoleculeUnlock(dst);
6844     Panic("Low memory while adding atoms");
6845         return 1;  /*  Not reached  */
6846 }
6847
6848 static int
6849 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag)
6850 {
6851         Int nsrc, ndst, nsrcnew;
6852         Int i, j, n1, n2, n3, n4, *cp;
6853         Int *new2old, *old2new;
6854         IntGroup *move_g, *del_g, *remain_g, *dst_par_g, *remove_par_g;
6855         Molecule *dst;
6856         Atom *ap, *dst_ap;
6857         UnionPar *up;
6858         
6859         if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
6860                 /*  Do nothing  */
6861                 if (dstp != NULL)
6862                         *dstp = NULL;
6863                 return 0;
6864         }
6865         
6866         if (src->noModifyTopology && moveFlag)
6867                 return 1;  /*  Prohibit editing  */
6868
6869         if ((ndst = IntGroupGetCount(where)) > src->natoms)
6870                 return 1;  /*  Bad parameter  */
6871
6872         __MoleculeLock(src);
6873         
6874         nsrc = src->natoms;
6875         nsrcnew = nsrc - ndst;
6876         if (resSeqOffset < 0)
6877                 resSeqOffset = 0;
6878
6879         /*  Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
6880             and nsrcnew..nsrc-1 are for atoms moved into dst.  */ 
6881         new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
6882         if (new2old == NULL)
6883                 goto panic;
6884         old2new = new2old + nsrc;
6885         n1 = 0;  /*  src index  */
6886         n2 = 0;  /*  dst index  */
6887         n3 = 0;  /*  src index after "unmerge"  */
6888         i = 0;
6889         while (n1 < nsrc || n2 < ndst) {
6890                 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
6891                         n4 = nsrc - n1;
6892                 else n4 -= n1;
6893                 /*  n4 elements from src[n1] will go to unmerged[n3]  */
6894                 for (j = 0; j < n4; j++) {
6895                         old2new[n1 + j] = n3 + j;
6896                         new2old[n3 + j] = n1 + j;
6897                 }
6898                 n3 += n4;
6899                 n1 += n4;
6900                 if ((n4 = IntGroupGetInterval(where, i)) < 0)
6901                         n4 = nsrc - n1;
6902                 /*  n4 elements from src[n1] will go to dst[n2]  */
6903                 for (j = 0; j < n4; j++) {
6904                         old2new[n1 + j] = nsrcnew + n2 + j;
6905                         new2old[nsrcnew + n2 + j] = n1 + j;
6906                 }
6907                 n1 += n4;
6908                 n2 += n4;
6909                 i++;
6910         }
6911
6912         /*  Atoms to remain in the source group  */
6913         if (moveFlag) {
6914                 remain_g = IntGroupNewWithPoints(0, nsrc, -1);
6915                 IntGroupRemoveIntGroup(remain_g, where);
6916         } else remain_g = NULL;
6917         
6918         /*  Find parameters to be moved to the dst (dst_par_g), and to be removed from the src (remove_par_g) */
6919         if (src->par != NULL) {
6920                 dst_par_g = IntGroupNew();
6921                 if (moveFlag)
6922                         remove_par_g = IntGroupNew();
6923                 else remove_par_g = NULL;
6924                 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
6925                         n2 = ParameterGetCountForType(src->par, n1);
6926                         if (n2 == 0)
6927                                 continue;
6928                         for (i = 0; i < n2; i++) {
6929                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
6930                                 if (ParameterIsRelevantToAtomGroup(n1, up, src->atoms, where)) {
6931                                         /*  This parameter is to be copied to dst  */
6932                                         IntGroupAdd(dst_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
6933                                 }
6934                                 if (moveFlag && !ParameterIsRelevantToAtomGroup(n1, up, src->atoms, remain_g)) {
6935                                         /*  This parameter is to be removed  */
6936                                         IntGroupAdd(remove_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
6937                                 }
6938                         }
6939                 }
6940         } else dst_par_g = remove_par_g = NULL;
6941                 
6942         /*  Make a new molecule  */
6943         if (dstp != NULL) {
6944                 dst = MoleculeNew();
6945                 if (dst == NULL)
6946                         goto panic;
6947                 /*  Expand the destination array  */
6948                 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
6949                         goto panic;
6950                 dst_ap = dst->atoms;
6951         } else {
6952                 dst = NULL;
6953                 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
6954                 if (dst_ap == NULL)
6955                         goto panic;
6956         }
6957         
6958         /*  Move the atoms  */
6959         if (moveFlag) {
6960                 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
6961                         goto panic;
6962                 src->natoms = nsrcnew;
6963                 if (dst == NULL) {
6964                         /*  The atom record must be deallocated correctly  */
6965                         for (i = 0; i < ndst; i++)
6966                                 AtomClean(ATOM_AT_INDEX(dst_ap, i));
6967                 }
6968         } else {
6969                 if (dst != NULL) {
6970                         for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
6971                                 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
6972                 }
6973         }
6974         
6975         if (dst == NULL) {
6976                 /*  The dummy destination array is no longer needed  */
6977                 free(dst_ap);
6978                 dst_ap = NULL;
6979         }
6980         
6981         /*  Renumber the atom indices in connect[]  */
6982         if (moveFlag) {
6983                 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
6984                         cp = AtomConnects(ap);
6985                         for (j = n1 = 0; j < ap->nconnects; j++) {
6986                                 n2 = old2new[cp[j]];
6987                                 if (n2 < nsrcnew)
6988                                         cp[n1++] = n2;
6989                         }
6990                         AtomResizeConnects(ap, n1);
6991                 }
6992         }
6993         
6994         /*  Renumber the atom indices in connect[] and the residue indices  */
6995         if (dst != NULL) {
6996                 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
6997                         if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
6998                                 ap->resSeq -= resSeqOffset;
6999                         else ap->resSeq = 0;
7000                         cp = AtomConnects(ap);
7001                         for (j = n1 = 0; j < ap->nconnects; j++) {
7002                                 n2 = old2new[cp[j]] - nsrcnew;
7003                                 if (n2 >= 0)
7004                                         cp[n1++] = n2;
7005                         }
7006                         AtomResizeConnects(ap, n1);
7007                 }
7008         }
7009
7010         /*  Separate the bonds, angles, dihedrals, impropers  */
7011         /*  TODO: Improper torsions should also be copied!  */
7012         move_g = IntGroupNew();
7013         del_g = IntGroupNew();
7014         if (move_g == NULL || del_g == NULL)
7015                 goto panic;
7016         for (i = 0; i < 4; i++) {
7017                 Int *nitems, *nitems_dst;
7018                 Int **items, **items_dst;
7019                 Int nsize;  /*  Number of Ints in one element  */
7020                 unsigned char *counts;
7021                 switch (i) {
7022                         case 0:
7023                                 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
7024                         case 1:
7025                                 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
7026                         case 2:
7027                                 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
7028                         case 3:
7029                                 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
7030                         default:
7031                                 nitems = NULL; items = NULL; nsize = 0; break;  /*  Not reached  */
7032                 }
7033                 if (dst != NULL) {
7034                         nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
7035                         items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
7036                 } else {
7037                         nitems_dst = NULL;
7038                         items_dst = NULL;
7039                 }
7040                 counts = (unsigned char *)calloc(1, *nitems);
7041                 /*  Find the entries that should be moved to dst  */
7042                 n2 = 0;
7043                 for (j = 0; j < *nitems * nsize; j++) {
7044                         n1 = old2new[(*items)[j]];
7045                         if (n1 >= nsrcnew)
7046                                 counts[j / nsize]++; /* Count the atom belonging to dst */ 
7047                 /*      if (n1 >= nsrcnew) {
7048                                 n1 -= nsrcnew;
7049                                 if (j % nsize == 0) {
7050                                         if (IntGroupAdd(sep, j / nsize, 1) != 0)
7051                                                 goto panic;
7052                                         n2++;
7053                                 }
7054                         }
7055                         (*items)[j] = n1; */
7056                 }
7057                 for (j = n2 = n3 = 0; j < *nitems; j++) {
7058                         if (counts[j] > 0) {
7059                                 /*  Remove from src  */
7060                                 n2++;
7061                                 if (IntGroupAdd(del_g, j, 1) != 0)
7062                                         goto panic;
7063                                 if (counts[j] == nsize) {
7064                                         /*  Move to dst  */
7065                                         n3++;
7066                                         if (IntGroupAdd(move_g, j, 1) != 0)
7067                                                 goto panic;
7068                                 }
7069                         }
7070                 }
7071                 if (n2 > 0) {
7072                         /*  Expand the destination array  */
7073                         if (items_dst != NULL && n3 > 0) {
7074                                 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
7075                                         goto panic;
7076                                 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
7077                                         goto panic;
7078                         }
7079                         /*  Remove from src  */
7080                         if (moveFlag) {
7081                                 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
7082                                         goto panic;
7083                                 (*nitems) -= n2;
7084                         }
7085                 }
7086                 /*  Renumber the entries  */
7087                 if (moveFlag) {
7088                         for (j = 0; j < *nitems * nsize; j++) {
7089                                 (*items)[j] = old2new[(*items)[j]];
7090                         }
7091                 }
7092                 if (items_dst != NULL) {
7093                         for (j = 0; j < *nitems_dst * nsize; j++) {
7094                                 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
7095                         }
7096                 }
7097                 free(counts);
7098                 IntGroupClear(move_g);
7099                 IntGroupClear(del_g);
7100         }
7101         IntGroupRelease(move_g);
7102         IntGroupRelease(del_g);
7103         
7104         /*  Copy the residues  */
7105         if (dst != NULL) {
7106                 /*  src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset)  */
7107                 n1 = src->nresidues - resSeqOffset;  /*  This will be dst->nresidues (if >0)  */
7108                 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
7109                         goto panic;
7110                 if (n1 > 1) {
7111                         memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
7112                 }
7113         }
7114
7115         /*  Copy the parameters to dst */
7116         if (dst != NULL && dst_par_g != NULL && (n2 = IntGroupGetCount(dst_par_g)) > 0) {
7117                 IntGroup *dst_new_g = IntGroupNew();
7118                 Int dst_par_count[kLastParType - kFirstParType + 1];
7119                 if (dst_new_g == NULL)
7120                         goto panic;
7121                 for (i = 0; i <= kLastParType - kFirstParType; i++)
7122                         dst_par_count[i] = 0;
7123                 up = (UnionPar *)calloc(sizeof(UnionPar), n2);
7124                 if (up == NULL)
7125                         goto panic;
7126                 if (ParameterCopy(src->par, kFirstParType, up, dst_par_g) < n2)
7127                         goto panic;
7128                 /*  Renumber the explicit atom indices  */
7129                 for (i = 0; i < nsrc; i++)
7130                         old2new[i] -= nsrcnew;  /*  new indices for atoms in dst; otherwise negative numbers  */
7131                 for (i = 0; i < n2; i++) {
7132                         /*  Renumber the indices, and count the number of parameters for each type  */
7133                         n1 = kFirstParType + IntGroupGetNthPoint(dst_par_g, i) / kParameterIndexOffset;
7134                         dst_par_count[n1 - kFirstParType]++;
7135                         ParameterRenumberAtoms(n1, up + i, nsrc, old2new);
7136                 }
7137                 for (i = 0; i < nsrc; i++)
7138                         old2new[i] += nsrcnew;
7139                 if (dst->par == NULL)
7140                         dst->par = ParameterNew();
7141                 for (i = 0; i <= kLastParType - kFirstParType; i++) {
7142                         if (dst_par_count[i] > 0)
7143                                 IntGroupAdd(dst_new_g, i * kParameterIndexOffset, dst_par_count[i]);
7144                 }
7145                 if (ParameterInsert(dst->par, kFirstParType, up, dst_new_g) < n2)
7146                         goto panic;
7147                 free(up);
7148                 IntGroupRelease(dst_new_g);
7149         }
7150         
7151         /*  Remove the unused parameter. Note: the parameters that are in remove_par_g and not in 
7152             dst_par_g will disappear. To support undo, these parameters should be taken care separately.  */
7153         if (remove_par_g != NULL && (n2 = IntGroupGetCount(remove_par_g)) > 0) {
7154                 ParameterDelete(src->par, kFirstParType, NULL, remove_par_g);
7155         }
7156         
7157         /*  Renumber the parameter records remaining in the src  */
7158         if (moveFlag) {
7159                 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7160                         n2 = ParameterGetCountForType(src->par, n1);
7161                         for (i = 0; i < n2; i++) {
7162                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7163                                 ParameterRenumberAtoms(n1, up, nsrc, old2new);
7164                         }
7165                 }
7166         }
7167         
7168         /*  Clean up  */
7169         MoleculeCleanUpResidueTable(src);
7170         if (dst != NULL)
7171                 MoleculeCleanUpResidueTable(dst);
7172         free(new2old);
7173
7174         src->nframes = -1;  /*  Should be recalculated later  */
7175         if (dst != NULL)
7176                 dst->nframes = -1;  /*  Should be recalculated later  */
7177
7178         
7179         if (dstp != NULL)
7180                 *dstp = dst;
7181
7182         MoleculeIncrementModifyCount(src);
7183         src->needsMDRebuild = 1;
7184         __MoleculeUnlock(src);
7185         
7186         return 0;
7187
7188   panic:
7189         __MoleculeUnlock(src);
7190 /*    Panic("Low memory while removing atoms"); */
7191         return -1;
7192 }
7193
7194 /*  Separate molecule into two parts. The atoms specified by 'where' are moved
7195     from src to a new molecule, which is returned as *dstp. Dstp can be NULL, 
7196         in which case the moved atoms are discarded.  */
7197 int
7198 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset)
7199 {
7200         return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1);
7201 }
7202
7203 /*  Extract atoms from a given molecule into two parts. The atoms specified by 
7204         'where' are copied from src to a new molecule, which is returned as *dstp.
7205     If dummyFlag is non-zero, then the atoms that are not included in the group 
7206         but are connected to any atoms in the group are converted to "dummy" atoms 
7207         (i.e. with element "Du" and names beginning with an underscore) and included 
7208         in the new molecule object.  */
7209 int
7210 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
7211 {
7212         int retval;
7213
7214         /*  Extract the fragment  */
7215         retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0);
7216         if (retval != 0)
7217                 return retval;
7218
7219         if (dummyFlag) {
7220
7221                 /*  Search bonds crossing the molecule border  */
7222                 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
7223                 if (ig != NULL) {
7224                         IntGroupIterator iter;
7225                         Int i, idx;
7226                         idx = 1;
7227                         IntGroupIteratorInit(ig, &iter);
7228                         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7229                                 /*  The atoms at the border  */
7230                                 Int n1, n2, nn[3];
7231                                 Atom a, *ap;
7232                                 n1 = src->bonds[i*2];
7233                                 n2 = src->bonds[i*2+1];
7234                                 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
7235                                         int w = n1;
7236                                         n1 = n2;
7237                                         n2 = w;
7238                                         if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
7239                                                 continue;  /*  Actually this is an internal error  */
7240                                 }
7241                                 /*  n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule  */
7242                                 /*  Create a new dummy atom with the same segment/residue info with n1
7243                                     and the same position as n2  */
7244                                 ap = ATOM_AT_INDEX(src->atoms, n1);
7245                                 memset(&a, 0, gSizeOfAtomRecord);
7246                                 a.segSeq = ap->segSeq;
7247                                 memmove(a.segName, ap->segName, 4);
7248                                 a.resSeq = ap->resSeq;
7249                                 memmove(a.resName, ap->resName, 4);
7250                                 ElementToString(0, a.element);  /*  "Du"  */
7251                                 snprintf(a.aname, 4, "_%d", idx++);
7252                                 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
7253                                 /*  Add the dummy atom to the new molecule; nn[1] is the index
7254                                     of the new dummy atom in the new molecule  */
7255                                 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
7256                                 /*  Connect nn1 and nn2  */
7257                                 nn[2] = kInvalidIndex;
7258                                 MoleculeAddBonds(*dstp, 1, nn);
7259                         }
7260                         IntGroupIteratorRelease(&iter);
7261                         IntGroupRelease(ig);
7262                 }
7263         }
7264         
7265         return 0;
7266 }
7267
7268 int
7269 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds)
7270 {
7271         int i, j, n1, n2, n;
7272         Atom *ap;
7273         Int *bonds_tmp, *cp;
7274
7275         if (mp == NULL || bonds == NULL || nbonds <= 0)
7276                 return 0;
7277         if (mp->noModifyTopology)
7278                 return -4;  /*  Prohibited operation  */
7279
7280         /*  Check the bonds  */
7281         bonds_tmp = (Int *)malloc(sizeof(Int) * nbonds * 2);
7282         if (bonds_tmp == NULL)
7283                 return -4;  /*  Out of memory  */
7284         n = 0;
7285         for (i = 0; i < nbonds; i++) {
7286                 n1 = bonds[i * 2];
7287                 n2 = bonds[i * 2 + 1];
7288                 if (n1 < 0 || n1 >= mp->natoms || n2 < 0 || n2 >= mp->natoms)
7289                         return -1;  /*  Bad bond specification  */
7290                 if (n1 == n2)
7291                         return -5;
7292                 ap = ATOM_AT_INDEX(mp->atoms, n1);
7293         /*      if (ap->nconnects >= ATOMS_MAX_CONNECTS - 1 || ATOM_AT_INDEX(mp->atoms, n2)->nconnects >= ATOMS_MAX_CONNECTS - 1)
7294                         return -2;  *//*  Too many bonds  */
7295                 /*  Check duplicates  */
7296                 cp = AtomConnects(ap);
7297                 for (j = 0; j < ap->nconnects; j++) {
7298                         if (cp[j] == n2)
7299                                 break;
7300                 }
7301                 if (j == ap->nconnects) {
7302                         bonds_tmp[n * 2] = n1;
7303                         bonds_tmp[n * 2 + 1] = n2;
7304                         n++;
7305                 }
7306         }
7307         if (n == 0) {
7308                 /*  No bonds to add  */
7309                 free(bonds_tmp);
7310                 return 0;
7311         }
7312         
7313         __MoleculeLock(mp);
7314
7315         /*  Add connects[]  */
7316         for (i = 0; i < n; i++) {
7317                 n1 = bonds_tmp[i * 2];
7318                 n2 = bonds_tmp[i * 2 + 1];
7319                 ap = ATOM_AT_INDEX(mp->atoms, n1);
7320                 AtomInsertConnectEntry(ap, ap->nconnects, n2);
7321                 ap = ATOM_AT_INDEX(mp->atoms, n2);
7322                 AtomInsertConnectEntry(ap, ap->nconnects, n1);
7323         }
7324         
7325         /*  Expand the array and insert  */
7326         n1 = mp->nbonds;
7327 /*      if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, mp->nbonds + nb - 1, NULL) == NULL
7328         || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nb, sizeof(Int) * 2, where) != 0) */
7329         if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, mp->nbonds + n - 1, NULL) == NULL)
7330                 goto panic;
7331         memmove(mp->bonds + n1 * 2, bonds_tmp, sizeof(Int) * 2 * n);
7332
7333         /*  Add angles, dihedrals, impropers  */
7334         {
7335                 Int nangles, ndihedrals, nimpropers;
7336                 Int *angles, *dihedrals, *impropers;
7337                 Int k, n3, n4;
7338                 Int *ip, *cp1, *cp2;
7339                 Int temp[4];
7340                 Atom *ap1, *ap2;
7341
7342                 angles = dihedrals = impropers = NULL;
7343                 nangles = ndihedrals = nimpropers = 0;
7344
7345                 for (i = 0; i < n; i++) {
7346                         n1 = bonds_tmp[i * 2];
7347                         n2 = bonds_tmp[i * 2 + 1];
7348                         ap1 = ATOM_AT_INDEX(mp->atoms, n1);
7349                         ap2 = ATOM_AT_INDEX(mp->atoms, n2);
7350                         cp1 = AtomConnects(ap1);
7351                         cp2 = AtomConnects(ap2);
7352                         /*  Angles X-n1-n2  */
7353                         for (j = 0; j < ap1->nconnects; j++) {
7354                                 n3 = cp1[j];
7355                                 if (n3 == n2)
7356                                         continue;
7357                                 temp[0] = n3;
7358                                 temp[1] = n1;
7359                                 temp[2] = n2;
7360                                 for (k = 0; k < nangles; k++) {
7361                                         ip = angles + k * 3;
7362                                         if (ip[1] == n1 && ((ip[0] == n3 && ip[2] == n2) || (ip[0] == n2 && ip[2] == n3)))
7363                                                 break;
7364                                 }
7365                                 if (k == nangles) {
7366                                         if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7367                                                 goto panic;
7368                                 }
7369                                 /*  Dihedrals X-n1-n2-X  */
7370                                 for (k = 0; k < ap2->nconnects; k++) {
7371                                         n4 = cp2[k];
7372                                         if (n4 == n1 || n4 == n3)
7373                                                 continue;
7374                                         temp[3] = n4;
7375                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7376                                                 goto panic;
7377                                 }
7378                                 /*  Impropers X-n2-n1-X  */
7379                         /*      temp[1] = n2;
7380                                 temp[2] = n1;
7381                                 for (k = 0; k < ap1->nconnects; k++) {
7382                                         n4 = ap1->connects[k];
7383                                         if (n4 == n2 || n4 <= n3)
7384                                                 continue;
7385                                         temp[3] = n4;
7386                                         if (AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, temp) == NULL)
7387                                                 goto panic;
7388                                 } */
7389                         }
7390                         /*  Angles X-n2-n1  */
7391                         for (j = 0; j < ap2->nconnects; j++) {
7392                                 n3 = cp2[j];
7393                                 if (n3 == n1)
7394                                         continue;
7395                                 temp[0] = n1;
7396                                 temp[1] = n2;
7397                                 temp[2] = n3;
7398                                 for (k = 0; k < nangles; k++) {
7399                                         ip = angles + k * 3;
7400                                         if (ip[1] == n2 && ((ip[0] == n3 && ip[2] == n1) || (ip[0] == n1 && ip[2] == n3)))
7401                                                 break;
7402                                 }
7403                                 if (k == nangles) {
7404                                         if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7405                                                 goto panic;
7406                                 }
7407                         }
7408                 }
7409                 temp[0] = kInvalidIndex;
7410                 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7411                         goto panic;
7412                 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7413                         goto panic;
7414                 if (AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, temp) == NULL)
7415                         goto panic;
7416                 MoleculeAddAngles(mp, angles, NULL);
7417                 MoleculeAddDihedrals(mp, dihedrals, NULL);
7418                 MoleculeAddImpropers(mp, impropers, NULL);
7419                 if (angles != NULL)
7420                         free(angles);
7421                 if (dihedrals != NULL)
7422                         free(dihedrals);
7423                 if (impropers != NULL)
7424                         free(impropers);
7425         }
7426         
7427         MoleculeIncrementModifyCount(mp);
7428         mp->needsMDRebuild = 1;
7429         __MoleculeUnlock(mp);
7430
7431         free(bonds_tmp);
7432         return n;       
7433
7434   panic:
7435         __MoleculeUnlock(mp);
7436         Panic("Low memory while adding bonds");
7437         return -1;  /*  Not reached  */
7438 }
7439
7440 int
7441 MoleculeDeleteBonds(Molecule *mp, Int nbonds, const Int *bonds)
7442 {
7443         Int i, j, n1, n2, *cp;
7444         Atom *ap;
7445
7446         if (mp == NULL || nbonds <= 0)
7447                 return 0;
7448         if (mp->noModifyTopology)
7449                 return -4;  /*  Prohibited operation  */
7450
7451         __MoleculeLock(mp);
7452
7453         /*  Update connects[]  */
7454         for (i = 0; i < nbonds; i++) {
7455                 n1 = bonds[i * 2];
7456                 n2 = bonds[i * 2 + 1];
7457                 ap = ATOM_AT_INDEX(mp->atoms, n1);
7458                 cp = AtomConnects(ap);
7459                 for (j = 0; j < ap->nconnects; j++) {
7460                         if (cp[j] == n2) {
7461                         /*      memmove(&ap->connects[j], &ap->connects[j + 1], sizeof(Int) * (ap->nconnects - j - 1));
7462                                 ap->nconnects--; */
7463                                 AtomDeleteConnectEntry(ap, j);
7464                                 break;
7465                         }
7466                 }
7467                 ap = ATOM_AT_INDEX(mp->atoms, n2);
7468                 cp = AtomConnects(ap);
7469                 for (j = 0; j < ap->nconnects; j++) {
7470                         if (cp[j] == n1) {
7471                         /*      memmove(&ap->connects[j], &ap->connects[j + 1], sizeof(Int) * (ap->nconnects - j - 1));
7472                                 ap->nconnects--; */
7473                                 AtomDeleteConnectEntry(ap, j);
7474                                 break;
7475                         }
7476                 }
7477         }
7478
7479         /*  Remove bonds, angles, dihedrals, impropers  */      
7480         {
7481                 IntGroup *bg, *ag, *dg, *ig;
7482                 Int *ip;
7483
7484                 bg = IntGroupNew();
7485                 ag = IntGroupNew();
7486                 dg = IntGroupNew();
7487                 ig = IntGroupNew();
7488                 if (bg == NULL || ag == NULL || dg == NULL || ig == NULL)
7489                         goto panic;
7490                 for (i = 0; i < nbonds; i++) {
7491                         n1 = bonds[i * 2];
7492                         n2 = bonds[i * 2 + 1];
7493                         for (j = 0; j < mp->nbonds; j++) {
7494                                 ip = mp->bonds + j * 2;
7495                                 if ((ip[0] == n1 && ip[1] == n2)
7496                                  || (ip[1] == n1 && ip[0] == n2)) {
7497                                         if (IntGroupAdd(bg, j, 1) != 0)
7498                                                 goto panic;
7499                                 }
7500                         }
7501                         for (j = 0; j < mp->nangles; j++) {
7502                                 ip = mp->angles + j * 3;
7503                                 if ((ip[0] == n1 && ip[1] == n2)
7504                                  || (ip[1] == n1 && ip[0] == n2)
7505                                  || (ip[1] == n1 && ip[2] == n2)
7506                                  || (ip[2] == n1 && ip[1] == n2)) {
7507                                         if (IntGroupAdd(ag, j, 1) != 0)
7508                                                 goto panic;
7509                                 }
7510                         }
7511                         for (j = 0; j < mp->ndihedrals; j++) {
7512                                 ip = mp->dihedrals + j * 4;
7513                                 if ((ip[1] == n1 && ip[2] == n2)
7514                                  || (ip[2] == n1 && ip[1] == n2)) {
7515                                         if (IntGroupAdd(dg, j, 1) != 0)
7516                                                 goto panic;
7517                                 }
7518                         }
7519                         for (j = 0; j < mp->nimpropers; j++) {
7520                                 ip = mp->impropers + j * 4;
7521                                 if ((ip[0] == n1 && ip[2] == n2)
7522                                  || (ip[1] == n1 && ip[2] == n2)
7523                                  || (ip[3] == n1 && ip[2] == n2)
7524                                  || (ip[0] == n2 && ip[2] == n1)
7525                                  || (ip[1] == n2 && ip[2] == n1)
7526                                  || (ip[3] == n2 && ip[2] == n1)) {
7527                                         if (IntGroupAdd(ig, j, 1) != 0)
7528                                                 goto panic;
7529                                 }
7530                         }
7531                 }
7532                 if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, NULL, sizeof(Int) * 2, bg) != 0)
7533                         goto panic;
7534                 mp->nbonds -= IntGroupGetCount(bg);
7535                 
7536                 if (IntGroupGetCount(ag) > 0)
7537                         MoleculeDeleteAngles(mp, NULL, ag);
7538                 if (IntGroupGetCount(dg) > 0)
7539                         MoleculeDeleteDihedrals(mp, NULL, dg);
7540                 if (IntGroupGetCount(ig) > 0)
7541                         MoleculeDeleteImpropers(mp, NULL, ig);
7542                 IntGroupRelease(bg);
7543                 IntGroupRelease(ag);
7544                 IntGroupRelease(dg);
7545                 IntGroupRelease(ig);
7546         }
7547
7548         MoleculeIncrementModifyCount(mp);
7549         mp->needsMDRebuild = 1;
7550         __MoleculeUnlock(mp);
7551
7552         return nbonds;
7553
7554   panic:
7555         __MoleculeUnlock(mp);
7556         Panic("Low memory while removing bonds");
7557         return -1;  /*  Not reached  */
7558 }
7559
7560 int
7561 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
7562 {
7563         int n1, nc;
7564         if (mp == NULL || angles == NULL)
7565                 return 0;
7566         if (mp->noModifyTopology)
7567                 return -4;  /*  Prohibited operation  */
7568
7569         __MoleculeLock(mp);
7570         if (where != NULL)
7571                 nc = IntGroupGetCount(where);
7572         else {
7573                 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
7574                         ;
7575                 nc = n1;
7576         }
7577         if (nc > 0) {
7578                 n1 = mp->nangles;
7579                 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
7580                         || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
7581                         __MoleculeUnlock(mp);
7582                         Panic("Low memory while adding angles");
7583                 }
7584         }
7585         mp->needsMDRebuild = 1;
7586         __MoleculeUnlock(mp);
7587         return nc;
7588 }
7589
7590 int
7591 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
7592 {
7593         int nc;
7594         if (mp == NULL || where == NULL)
7595                 return 0;
7596         if (mp->noModifyTopology)
7597                 return -4;  /*  Prohibited operation  */
7598         __MoleculeLock(mp);
7599         if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
7600                 __MoleculeUnlock(mp);
7601                 Panic("Low memory while adding angles");
7602         }
7603         mp->nangles -= (nc = IntGroupGetCount(where));
7604         mp->needsMDRebuild = 1;
7605         __MoleculeUnlock(mp);
7606         return nc;
7607 }
7608
7609 int
7610 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
7611 {
7612         int n1, nc;
7613         if (mp == NULL || dihedrals == NULL)
7614                 return 0;
7615         if (mp->noModifyTopology)
7616                 return -4;  /*  Prohibited operation  */
7617         if (where != NULL)
7618                 nc = IntGroupGetCount(where);
7619         else {
7620                 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
7621                         ;
7622                 nc = n1;
7623         }
7624         if (nc <= 0)
7625                 return 0;
7626         n1 = mp->ndihedrals;
7627         __MoleculeLock(mp);
7628         if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
7629         || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
7630                 __MoleculeUnlock(mp);
7631                 Panic("Low memory while adding dihedrals");
7632         }
7633         mp->needsMDRebuild = 1;
7634         __MoleculeUnlock(mp);
7635         return nc;
7636 }
7637
7638 int
7639 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
7640 {       
7641         int nc;
7642         if (mp == NULL || where == NULL)
7643                 return 0;
7644         if (mp->noModifyTopology)
7645                 return -4;  /*  Prohibited operation  */
7646         __MoleculeLock(mp);
7647         if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
7648                 __MoleculeUnlock(mp);
7649                 Panic("Low memory while adding dihedrals");
7650         }
7651         mp->ndihedrals -= (nc = IntGroupGetCount(where));
7652         mp->needsMDRebuild = 1;
7653         __MoleculeUnlock(mp);
7654         return nc;
7655 }
7656
7657 int
7658 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
7659 {
7660         int n1, nc;
7661         if (mp == NULL || impropers == NULL)
7662                 return 0;
7663         if (mp->noModifyTopology)
7664                 return -4;  /*  Prohibited operation  */
7665         if (where != NULL)
7666                 nc = IntGroupGetCount(where);
7667         else {
7668                 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
7669                         ;
7670                 nc = n1;
7671         }
7672         if (nc <= 0)
7673                 return 0;
7674         n1 = mp->nimpropers;
7675         __MoleculeLock(mp);
7676         if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
7677         || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
7678                 __MoleculeUnlock(mp);
7679                 Panic("Low memory while adding impropers");
7680         }
7681         mp->needsMDRebuild = 1;
7682         __MoleculeUnlock(mp);
7683         return nc;
7684 }
7685
7686 int
7687 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
7688 {
7689         int nc;
7690         if (mp == NULL || where == NULL)
7691                 return 0;
7692         if (mp->noModifyTopology)
7693                 return -4;  /*  Prohibited operation  */
7694         __MoleculeLock(mp);
7695         if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
7696                 __MoleculeUnlock(mp);
7697                 Panic("Low memory while adding impropers");
7698         }
7699         mp->nimpropers -= (nc = IntGroupGetCount(where));
7700         __MoleculeUnlock(mp);
7701         return nc;
7702 }
7703
7704 int
7705 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
7706 {
7707         Int i, *ip;
7708         if (mp == NULL || mp->bonds == NULL)
7709                 return -1;
7710         for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
7711                 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
7712                         return i;
7713         }
7714         return -1;
7715 }
7716
7717 int
7718 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
7719 {
7720         Int i, *ip;
7721         if (mp == NULL || mp->angles == NULL)
7722                 return -1;
7723         for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
7724                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
7725                         (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
7726                         return i;
7727         }
7728         return -1;
7729 }
7730
7731 int
7732 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
7733 {
7734         Int i, *ip;
7735         if (mp == NULL || mp->dihedrals == NULL)
7736                 return -1;
7737         for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
7738                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
7739                         (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
7740                         return i;
7741         }
7742         return -1;
7743 }
7744
7745 int
7746 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
7747 {
7748         Int i, *ip;
7749         if (mp == NULL || mp->impropers == NULL)
7750                 return -1;
7751         for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
7752                 if (n3 != ip[2])
7753                         continue;
7754                 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
7755                         (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
7756                         (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
7757                         return i;
7758         }
7759         return -1;
7760 }
7761
7762 /*  Remove the bond at bondIndex and create two dummy atoms instead.
7763     The dummy atoms are placed at the end of atoms[], and the residue
7764         numbers are the same as the root atoms (i.e. the atoms to which
7765         the dummy atoms are connected). The indices are returned in
7766         dummyIndices[0,1].  */
7767 int
7768 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
7769 {
7770         Int roots[3], newBonds[5];
7771         Vector dr;
7772         Atom *rootp[2];
7773         Atom na[2], *nap;
7774         int i, natoms;
7775         if (mp == NULL || mp->noModifyTopology)
7776                 return 0;
7777         if (bondIndex < 0 || bondIndex >= mp->nbonds)
7778                 return -1;
7779         roots[0] = mp->bonds[bondIndex * 2];
7780         roots[1] = mp->bonds[bondIndex * 2 + 1];
7781         roots[2] = kInvalidIndex;
7782         rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
7783         rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
7784         VecSub(dr, rootp[0]->r, rootp[1]->r);
7785         for (i = 0; i < 2; i++) {
7786                 float w;
7787                 nap = &na[i];
7788                 memmove(nap, rootp[i], sizeof(na));
7789                 nap->aname[0] = '*';
7790                 strcpy(nap->element, "Du");
7791                 nap->type = 0;
7792                 nap->charge = nap->weight = 0.0;
7793                 nap->atomicNumber = 0;
7794                 nap->nconnects = 0;
7795                 w = (i == 0 ? 0.4 : -0.4);
7796                 VecScaleInc(nap->r, dr, w);
7797                 VecZero(nap->v);
7798                 VecZero(nap->f);
7799                 nap->intCharge = 0;
7800                 nap->exflags = 0;
7801         }
7802
7803         /*  Expand atoms array and append the dummy atoms at the end  */
7804         __MoleculeLock(mp);
7805         natoms = mp->natoms;
7806         if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
7807                 goto panic;
7808         memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
7809         dummyIndices[0] = natoms;
7810         dummyIndices[1] = natoms + 1;
7811
7812         /*  Remove the old bond and create new bonds  */
7813 /*      ig = IntGroupNewWithPoints(bondIndex, 1, -1);
7814         if (ig == NULL)
7815                 goto panic;
7816         MoleculeDeleteBonds(mp, NULL, ig);
7817         IntGroupRelease(ig); */
7818         MoleculeDeleteBonds(mp, 1, roots);
7819         newBonds[0] = roots[0];
7820         newBonds[1] = dummyIndices[0];
7821         newBonds[2] = roots[1];
7822         newBonds[3] = dummyIndices[1];
7823         newBonds[4] = kInvalidIndex;
7824         
7825         i = (MoleculeAddBonds(mp, 2, newBonds) < 0 ? -1 : 0);
7826         mp->needsMDRebuild = 1;
7827         __MoleculeUnlock(mp);
7828         return i;
7829
7830 panic:
7831         __MoleculeUnlock(mp);
7832         Panic("Low memory during creating dummy atoms");
7833         return 1;
7834 }
7835
7836 /*  Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
7837     a bond between the two root atoms. The value bondIndex is used as a
7838         hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
7839         the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
7840         is ignored and the new bond is stored at the end of bonds[].  */
7841 int
7842 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
7843 {
7844         return 0;
7845 }
7846
7847 /*
7848 Int
7849 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
7850 {
7851         Int n1, *np1;
7852         if (mol == NULL || mol->noModifyTopology)
7853                 return -1;
7854         n1 = mol->nangles;
7855         np1 = mol->angles;
7856         mol->nangles = 0;
7857         mol->angles = NULL;
7858         if (nangles > 0) {
7859                 __MoleculeLock(mol);
7860                 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
7861                 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
7862                 mol->needsMDRebuild = 1;
7863                 __MoleculeUnlock(mol);
7864         }
7865         *outAngles = np1;
7866         return n1;
7867 }
7868                                                 
7869 Int
7870 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
7871 {
7872         Int n1, *np1;
7873         if (mol == NULL || mol->noModifyTopology)
7874                 return -1;
7875         n1 = mol->ndihedrals;
7876         np1 = mol->dihedrals;
7877         mol->ndihedrals = 0;
7878         mol->dihedrals = NULL;
7879         if (ndihedrals > 0) {
7880                 __MoleculeLock(mol);
7881                 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
7882                 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
7883                 mol->needsMDRebuild = 1;
7884                 __MoleculeUnlock(mol);
7885         }
7886         *outDihedrals = np1;
7887         return n1;
7888 }
7889
7890 Int
7891 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
7892 {
7893         Int n1, *np1;
7894         if (mol == NULL || mol->noModifyTopology)
7895                 return -1;
7896         n1 = mol->nimpropers;
7897         np1 = mol->impropers;
7898         mol->nimpropers = 0;
7899         mol->impropers = NULL;
7900         if (nimpropers > 0) {
7901                 __MoleculeLock(mol);
7902                 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
7903                 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
7904                 mol->needsMDRebuild = 1;
7905                 __MoleculeUnlock(mol);
7906         }
7907         *outImpropers = np1;
7908         return n1;
7909 }
7910 */
7911
7912 Int
7913 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
7914 {
7915         Int i, j, k, *ip;
7916         Atom *ap;
7917         Int nangles;
7918         Int *angles;
7919         
7920         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
7921                 return 0;  /*  molecule is empty  */
7922         if (mol->noModifyTopology)
7923                 return -1;
7924         nangles = 0;
7925         angles = NULL;
7926         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7927                 Int *cp = AtomConnects(ap);
7928                 for (j = 0; j < ap->nconnects; j++) {
7929                         Int j0 = cp[j];
7930                         for (k = j + 1; k < ap->nconnects; k++) {
7931                                 Int k0 = cp[k];
7932                                 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
7933                                         ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
7934                                         ip[0] = j0;
7935                                         ip[1] = i;
7936                                         ip[2] = k0;
7937                                 }
7938                         }
7939                 }
7940         }
7941         if (nangles > 0) {
7942                 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
7943                 ip[0] = -1;
7944                 nangles--;
7945         }
7946         if (outAngles != NULL)
7947                 *outAngles = angles;
7948         return nangles;
7949 }
7950
7951 Int
7952 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
7953 {
7954         Int n1, n2, n3, n4, *ip, *cp2, *cp3;
7955         Atom *ap2, *ap3;
7956         Int ndihedrals;
7957         Int *dihedrals;
7958         
7959         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
7960                 return 0;  /*  molecule is empty  */
7961         ndihedrals = 0;
7962         dihedrals = NULL;
7963         for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
7964                 Int i1, i3, i4, *ip;
7965                 cp2 = AtomConnects(ap2);
7966                 for (i3 = 0; i3 < ap2->nconnects; i3++) {
7967                         n3 = cp2[i3];
7968                         if (n2 > n3)
7969                                 continue;
7970                         ap3 = ATOM_AT_INDEX(mol->atoms, n3);
7971                         cp3 = AtomConnects(ap3);
7972                         for (i1 = 0; i1 < ap2->nconnects; i1++) {
7973                                 n1 = cp2[i1];
7974                                 if (n1 == n3)
7975                                         continue;
7976                                 for (i4 = 0; i4 < ap3->nconnects; i4++) {
7977                                         n4 = cp3[i4];
7978                                         if (n2 == n4 || n1 == n4)
7979                                                 continue;
7980                                         if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
7981                                                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
7982                                                 ip[0] = n1;
7983                                                 ip[1] = n2;
7984                                                 ip[2] = n3;
7985                                                 ip[3] = n4;
7986                                         }
7987                                 }
7988                         }
7989                 }
7990         }
7991         if (ndihedrals > 0) {
7992                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
7993                 ip[0] = -1;
7994                 ndihedrals--;
7995         }
7996         if (outDihedrals != NULL)
7997                 *outDihedrals = dihedrals;
7998         return ndihedrals;
7999 }
8000
8001 Int
8002 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
8003 {
8004         Int n1, n2, n3, n4, t1, t2, t3, t4, *ip, *cp;
8005         Parameter *par = mol->par;
8006         Atom *ap, *ap3;
8007         Int nimpropers;
8008         Int *impropers;
8009         
8010         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8011                 return 0;  /*  molecule is empty  */
8012         if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
8013                 return 0;  /*  No improper parameters are defined  */
8014         nimpropers = 0;
8015         impropers = NULL;
8016         ap = mol->atoms;
8017         for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
8018                 Int i1, i2, i4, found, *ip;
8019                 t3 = ap3->type;
8020                 cp = AtomConnects(ap3);
8021                 for (i1 = 0; i1 < ap3->nconnects; i1++) {
8022                         n1 = cp[i1];
8023                         t1 = ATOM_AT_INDEX(ap, n1)->type;
8024                         for (i2 = i1 + 1; i2 < ap3->nconnects; i2++) {
8025                                 n2 = cp[i2];
8026                                 t2 = ATOM_AT_INDEX(ap, n2)->type;
8027                                 for (i4 = i2 + 1; i4 < ap3->nconnects; i4++) {
8028                                         n4 = cp[i4];
8029                                         t4 = ATOM_AT_INDEX(ap, n4)->type;
8030                                         found = 0;
8031                                         if (ParameterLookupImproperPar(par, t1, t2, t3, t4, n1, n2, n3, n4, 0) != NULL)
8032                                                 found = 1;
8033                                         else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, -1, -1, -1, -1, 0) != NULL)
8034                                                 found = 1;
8035                                         if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
8036                                                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8037                                                 ip[0] = n1;
8038                                                 ip[1] = n2;
8039                                                 ip[2] = n3;
8040                                                 ip[3] = n4;
8041                                         }
8042                                 }
8043                         }
8044                 }
8045         }
8046         if (nimpropers > 0) {
8047                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8048                 ip[0] = -1;
8049                 nimpropers--;
8050         }
8051         if (outImpropers != NULL)
8052                 *outImpropers = impropers;
8053         return nimpropers;
8054 }
8055
8056 #pragma mark ====== Residues ======
8057
8058 void
8059 MoleculeCleanUpResidueTable(Molecule *mp)
8060 {
8061         int i, maxres;
8062         Atom *ap;
8063         if (mp == NULL || mp->natoms == 0)
8064                 return;
8065         maxres = 0;
8066         __MoleculeLock(mp);
8067         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8068                 if (ap->resSeq >= maxres)
8069                         maxres = ap->resSeq + 1;
8070                 if (ap->resSeq < mp->nresidues) {
8071                         if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
8072                                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8073                 } else {
8074                         AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
8075                 }
8076         }
8077         if (maxres < mp->nresidues)
8078                 mp->nresidues = maxres;
8079         __MoleculeUnlock(mp);
8080 }
8081
8082 /*  Change the number of residues. If nresidues is greater than the current value,
8083     then the array mp->residues is expanded with null names. If nresidues is smaller
8084         than the current value, mp->nresidues is set to the smallest possible value
8085         that is no smaller than nresidues and larger than any of the resSeq values.  */
8086 int
8087 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
8088 {
8089         int n;
8090         if (mp == NULL)
8091                 return 0;
8092         if (mp->nresidues == nresidues)
8093                 return nresidues;
8094         else if (mp->nresidues < nresidues) {
8095                 __MoleculeLock(mp);
8096                 n = mp->nresidues;
8097                 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
8098                 while (n < nresidues)
8099                         mp->residues[n++][0] = 0;
8100                 __MoleculeUnlock(mp);
8101                 return nresidues;
8102         } else {
8103                 int i;
8104                 Atom *ap;
8105                 n = nresidues;
8106                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8107                         if (ap->resSeq >= n)
8108                                 n = ap->resSeq + 1;
8109                 }
8110                 mp->nresidues = n;
8111                 return n;
8112         }
8113 }
8114
8115 int
8116 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
8117 {
8118         IntGroupIterator iter;
8119         int withArray, resSeq, maxSeq;
8120         int i, j;
8121         Atom *ap;
8122         
8123         /*  If LSB of resSeqs is 1, then a constant value is used for all specified atoms  */
8124         if (((int)resSeqs & 1) == 0) {
8125                 withArray = 1;
8126                 resSeq = 0;
8127         } else {
8128                 withArray = 0;
8129                 resSeq = ((int)resSeqs - 1) / 2;
8130         }
8131         
8132         IntGroupIteratorInit(group, &iter);
8133
8134         /*  Change resSeqs  */
8135         maxSeq = 0;
8136         j = 0;
8137         __MoleculeLock(mp);
8138         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8139                 ap = ATOM_AT_INDEX(mp->atoms, i);
8140                 if (withArray)
8141                         resSeq = resSeqs[j++];
8142                 if (resSeq > maxSeq)
8143                         maxSeq = resSeq;
8144                 ap->resSeq = resSeq;
8145         }
8146         __MoleculeUnlock(mp);
8147
8148         /*  Expand array if necessary  */
8149         if (maxSeq >= mp->nresidues)
8150                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8151
8152         /*  Synchronize resName and residues[]  */
8153         __MoleculeLock(mp);
8154         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8155                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8156                         continue;
8157                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8158         }
8159         IntGroupIteratorRelease(&iter);
8160         __MoleculeUnlock(mp);
8161         
8162         MoleculeIncrementModifyCount(mp);
8163         
8164         return 0;
8165 }
8166
8167 int
8168 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
8169 {
8170         return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(resSeq * 2 + 1));
8171 }
8172
8173 /*  Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
8174     specifies the mp->nresidues after modifying the residue numbers.
8175         If all atoms are modified, then the table of residue names is also shifted. Otherwise,
8176         the table of residue names is not touched. */
8177 int
8178 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
8179 {
8180         int i, maxSeq, nmodatoms;
8181         Atom *ap;
8182         IntGroupIterator iter;
8183         IntGroupIteratorInit(group, &iter);
8184         maxSeq = 0;
8185         if (nresidues < 0)
8186                 nresidues = mp->nresidues;
8187         nmodatoms = 0;
8188         __MoleculeLock(mp);
8189         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8190                 ap = ATOM_AT_INDEX(mp->atoms, i);
8191                 ap->resSeq += offset;
8192                 if (ap->resSeq < 0) {
8193                         /*  Bad argument; undo change and returns this index + 1  */
8194                         int bad_index = i;
8195                         ap->resSeq -= offset;
8196                         while ((i = IntGroupIteratorLast(&iter)) >= 0) {
8197                                 ap = ATOM_AT_INDEX(mp->atoms, i);
8198                                 ap->resSeq -= offset;
8199                         }
8200                         IntGroupIteratorRelease(&iter);
8201                         return bad_index + 1;
8202                 }
8203                 if (ap->resSeq > maxSeq)
8204                         maxSeq = ap->resSeq;
8205                 nmodatoms++;
8206         }
8207         if (maxSeq >= nresidues)
8208                 nresidues = maxSeq + 1;
8209         if (offset < 0 && nmodatoms == mp->natoms) {
8210                 /*  Shift the residue names downward  */
8211                 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
8212         }
8213         __MoleculeUnlock(mp);
8214         MoleculeChangeNumberOfResidues(mp, nresidues);
8215         if (offset > 0 && nmodatoms == mp->natoms) {
8216                 /*  Shift the residue names upward  */
8217                 __MoleculeLock(mp);
8218                 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
8219                 __MoleculeUnlock(mp);
8220         }
8221         IntGroupIteratorRelease(&iter);
8222
8223         MoleculeIncrementModifyCount(mp);
8224         
8225         return 0;
8226 }
8227
8228 /*  Change residue names for the specified residue numbers. Names is an array of
8229     chars containing argc*4 characters, and every 4 characters represent a
8230         residue name; characters '\x01'-'\x1f' are converted to '\0', which allow 
8231         names to be handled as a C string.  */
8232 int
8233 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
8234 {
8235         int i, maxSeq;
8236         Atom *ap;
8237         maxSeq = 0;
8238         for (i = 0; i < argc; i++) {
8239                 if (maxSeq < resSeqs[i])
8240                         maxSeq = resSeqs[i];
8241         }
8242         if (maxSeq >= mp->nresidues)
8243                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8244         __MoleculeLock(mp);
8245         for (i = 0; i < argc; i++) {
8246                 char *p = mp->residues[resSeqs[i]];
8247                 int j;
8248                 strncpy(p, names + i * 4, 4);
8249                 for (j = 0; j < 4; j++) {
8250                         if (p[j] >= 0 && p[j] < 0x20)
8251                                 p[j] = 0;
8252                 }
8253         }
8254         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8255                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8256         }
8257         __MoleculeUnlock(mp);
8258
8259         MoleculeIncrementModifyCount(mp);
8260         
8261         return 0;
8262 }
8263
8264 /*  Returns the maximum residue number actually used  */
8265 int
8266 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
8267 {
8268         int i, maxSeq;
8269         Atom *ap;
8270         maxSeq = -1;
8271         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8272                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8273                         continue;
8274                 if (ap->resSeq > maxSeq)
8275                         maxSeq = ap->resSeq;
8276         }
8277         return maxSeq;
8278 }
8279
8280 /*  Returns the minimum residue number actually used  */
8281 int
8282 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
8283 {
8284         int i, minSeq;
8285         Atom *ap;
8286         minSeq = ATOMS_MAX_NUMBER;
8287         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8288                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8289                         continue;
8290                 if (ap->resSeq < minSeq)
8291                         minSeq = ap->resSeq;
8292         }
8293         return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
8294 }
8295
8296 #pragma mark ====== Sort by Residues ======
8297
8298 static int
8299 sAtomSortComparator(const void *a, const void *b)
8300 {
8301         const Atom *ap, *bp;
8302         ap = *((const Atom **)a);
8303         bp = *((const Atom **)b);
8304         if (ap->resSeq == bp->resSeq) {
8305                 /*  Retain the original order (i.e. atom with larger pointer address is larger)  */
8306                 if (ap < bp)
8307                         return -1;
8308                 else if (ap > bp)
8309                         return 1;
8310                 else return 0;
8311         } else {
8312                 /*  Compare the residue sequence. However, residue sequence 0 is always larger.  */
8313                 if (ap->resSeq == 0)
8314                         return 1;
8315                 else if (bp->resSeq == 0)
8316                         return -1;
8317                 else if (ap->resSeq < bp->resSeq)
8318                         return -1;
8319                 else if (ap->resSeq > bp->resSeq)
8320                         return 1;
8321                 else return 0;
8322         }
8323 }
8324
8325 static void
8326 sMoleculeReorder(Molecule *mp)
8327 {
8328         int i, res, prevRes;
8329         Atom **apArray;
8330         Int *old2new;
8331         Atom *newAtoms;
8332         if (mp == NULL || mp->natoms <= 1)
8333                 return;
8334
8335         /*  Sort the atoms, bonds, etc. */
8336         apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
8337         old2new = (Int *)calloc(sizeof(Int), mp->natoms);
8338         newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
8339         if (apArray == NULL || old2new == NULL || newAtoms == NULL)
8340                 Panic("Low memory during reordering atoms");
8341         for (i = 0; i < mp->natoms; i++)
8342                 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
8343
8344         /*  Sort the atoms. Note: apArray is an array of "Pointer to Atom"  */
8345         qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
8346         
8347         /*  Make a table of 'which atom becomes which'  */
8348         for (i = 0; i < mp->natoms; i++) {
8349                 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
8350                 old2new[j] = i;
8351         }
8352         
8353         /*  Renumber the bonds, etc.  */
8354         for (i = 0; i < mp->nbonds * 2; i++) {
8355                 mp->bonds[i] = old2new[mp->bonds[i]];
8356         }
8357         for (i = 0; i < mp->nangles * 3; i++) {
8358                 mp->angles[i] = old2new[mp->angles[i]];
8359         }
8360         for (i = 0; i < mp->ndihedrals * 4; i++) {
8361                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
8362         }
8363         for (i = 0; i < mp->nimpropers * 4; i++) {
8364                 mp->impropers[i] = old2new[mp->impropers[i]];
8365         }
8366         for (i = 0; i < mp->natoms; i++) {
8367                 Int *ip, j;
8368                 ip = AtomConnects(apArray[i]);
8369                 for (j = 0; j < apArray[i]->nconnects; j++, ip++)
8370                         *ip = old2new[*ip];
8371         }
8372         
8373         /*  Renumber the residues so that the residue numbers are contiguous  */
8374         res = prevRes = 0;
8375         for (i = 0; i < mp->natoms; i++) {
8376                 if (apArray[i]->resSeq == 0)
8377                         break;
8378                 if (apArray[i]->resSeq != prevRes) {
8379                         res++;
8380                         prevRes = apArray[i]->resSeq;
8381                         if (prevRes != res) {
8382                                 strncpy(mp->residues[res], mp->residues[prevRes], 4);
8383                         }
8384                 }
8385                 apArray[i]->resSeq = res;
8386         }
8387         mp->nresidues = res + 1;
8388
8389         /*  Sort the atoms and copy back to atoms[] */
8390         for (i = 0; i < mp->natoms; i++) {
8391                 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
8392         }
8393         memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
8394         
8395         /*  Free the locally allocated storage  */
8396         free(newAtoms);
8397         free(old2new);
8398         free(apArray);
8399
8400 }
8401
8402 /*  Renumber atoms  */
8403 int
8404 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
8405 {
8406         Int *old2new, i, j, retval;
8407         Atom *saveAtoms;
8408         if (mp == NULL)
8409                 return 0;
8410         if (mp->noModifyTopology)
8411                 return -1;
8412         if (old2new_out != NULL)
8413                 old2new = old2new_out;
8414         else
8415                 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
8416         saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
8417         if (old2new == NULL || saveAtoms == NULL)
8418                 Panic("Low memory during reordering atoms");
8419         memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
8420         __MoleculeLock(mp);
8421         for (i = 0; i < mp->natoms; i++)
8422                 old2new[i] = -1;
8423         for (i = 0; i < isize && i < mp->natoms; i++) {
8424                 j = new2old[i];
8425                 if (j < 0 || j >= mp->natoms) {
8426                         retval = 1; /* Out of range */
8427                         goto end;
8428                 }
8429                 if (old2new[j] != -1) {
8430                         retval = 2;  /*  Duplicate entry  */
8431                         goto end;
8432                 }
8433                 old2new[j] = i;
8434         }
8435         if (i < mp->natoms) {
8436                 for (j = 0; j < mp->natoms; j++) {
8437                         if (old2new[j] != -1)
8438                                 continue;
8439                         old2new[j] = i++;
8440                 }
8441         }
8442         if (i != mp->natoms) {
8443                 retval = 3;  /*  Internal inconsistency  */
8444                 goto end;
8445         }
8446
8447         /*  Renumber the bonds, etc.  */
8448         for (i = 0; i < mp->nbonds * 2; i++) {
8449                 mp->bonds[i] = old2new[mp->bonds[i]];
8450         }
8451         for (i = 0; i < mp->nangles * 3; i++) {
8452                 mp->angles[i] = old2new[mp->angles[i]];
8453         }
8454         for (i = 0; i < mp->ndihedrals * 4; i++) {
8455                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
8456         }
8457         for (i = 0; i < mp->nimpropers * 4; i++) {
8458                 mp->impropers[i] = old2new[mp->impropers[i]];
8459         }
8460         for (i = 0; i < mp->natoms; i++) {
8461                 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
8462                 Int *ip = AtomConnects(ap);
8463                 for (j = 0; j < ap->nconnects; j++, ip++)
8464                         *ip = old2new[*ip];
8465         }
8466         if (mp->par != NULL) {
8467                 /*  Renumber the parameters  */
8468                 int n;
8469                 for (j = kFirstParType; j <= kLastParType; j++) {
8470                         n = ParameterGetCountForType(mp->par, j);
8471                         for (i = 0; i < n; i++) {
8472                                 UnionPar *up = ParameterGetUnionParFromTypeAndIndex(mp->par, j, i);
8473                                 if (up != NULL)
8474                                         ParameterRenumberAtoms(j, up, mp->natoms, old2new);
8475                         }
8476                 }
8477         }
8478         
8479         /*  Renumber the atoms  */
8480         for (i = 0; i < mp->natoms; i++)
8481                 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
8482         retval = 0;
8483
8484         MoleculeIncrementModifyCount(mp);
8485         mp->needsMDRebuild = 1;
8486
8487   end:
8488         __MoleculeUnlock(mp);
8489         free(saveAtoms);
8490         if (old2new_out == NULL)
8491                 free(old2new);
8492         return retval;
8493 }
8494
8495 #pragma mark ====== Coordinate Transform ======
8496
8497 void
8498 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
8499 {
8500         int i;
8501         Atom *ap;
8502         Symop new_symop;
8503         Transform rtr, symtr;
8504         if (mp == NULL || tr == NULL)
8505                 return;
8506         TransformInvert(rtr, tr);
8507         __MoleculeLock(mp);
8508         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8509                 if (group == NULL || IntGroupLookup(group, i, NULL) != 0) {
8510                         TransformVec(&ap->r, tr, &ap->r);
8511                         if (!SYMOP_ALIVE(ap->symop))
8512                                 continue;
8513                         /*  Transform symop  */
8514                         if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
8515                                 continue;
8516                         TransformMul(symtr, tr, symtr);
8517                         if (group == NULL || IntGroupLookup(group, ap->symbase, NULL) != 0)
8518                                 TransformMul(symtr, symtr, rtr);
8519                 } else {
8520                         if (!SYMOP_ALIVE(ap->symop))
8521                                 continue;
8522                         /*  Transform symop if the base atom is transformed  */
8523                         if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
8524                                 continue;
8525                         if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
8526                                 continue;
8527                         TransformMul(symtr, symtr, rtr);
8528                 }
8529                 if (MoleculeGetSymopForTransform(mp, symtr, &new_symop, 1) != 0)
8530                         continue;
8531                 ap->symop = new_symop;
8532         }
8533         mp->needsMDCopyCoordinates = 1;
8534         __MoleculeUnlock(mp);
8535         sMoleculeNotifyChangeAppearance(mp);
8536 }
8537
8538 /*
8539 void
8540 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
8541 {
8542         int i;
8543         Atom *ap;
8544         if (mp == NULL || tr == NULL)
8545                 return;
8546         __MoleculeLock(mp);
8547         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8548                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8549                         continue;
8550                 TransformVec(&ap->r, tr, &ap->r);
8551         }
8552         mp->needsMDCopyCoordinates = 1;
8553         __MoleculeUnlock(mp);
8554         sMoleculeNotifyChangeAppearance(mp);
8555 }
8556 */
8557
8558 void
8559 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
8560 {
8561         Transform tr;
8562         if (mp == NULL || vp == NULL)
8563                 return;
8564         memset(tr, 0, sizeof(tr));
8565         tr[0] = tr[4] = tr[8] = 1.0;
8566         tr[9] = vp->x;
8567         tr[10] = vp->y;
8568         tr[11] = vp->z;
8569         MoleculeTransform(mp, tr, group);
8570 }
8571
8572 void
8573 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
8574 {
8575         Transform tr;
8576         TransformForRotation(tr, axis, angle, center);
8577         MoleculeTransform(mp, tr, group);
8578 }
8579
8580 int
8581 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
8582 {
8583         int i;
8584         Atom *ap;
8585         Double w;
8586         if (mp == NULL || center == NULL)
8587                 return 1;
8588         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
8589                 return 2;   /*  Empty molecule  */
8590         w = 0.0;
8591         center->x = center->y = center->z = 0.0;
8592         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8593                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8594                         continue;
8595                 VecScaleInc(*center, ap->r, ap->weight);
8596                 w += ap->weight;
8597         }
8598         if (w < 1e-7)
8599                 return 3;  /*  Atomic weights are not defined?  */
8600         w = 1.0 / w;
8601         VecScaleSelf(*center, w);
8602         return 0;
8603 }
8604
8605 int
8606 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
8607 {
8608         Vector vmin, vmax;
8609         int i;
8610         Atom *ap;
8611         if (mp == NULL)
8612                 return 1;
8613         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
8614                 return 2;   /*  Empty molecule  */
8615         vmin.x = vmin.y = vmin.z = 1e50;
8616         vmax.x = vmax.y = vmax.z = -1e50;
8617         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8618                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8619                         continue;
8620                 if (vmin.x > ap->r.x)
8621                         vmin.x = ap->r.x;
8622                 if (vmin.y > ap->r.y)
8623                         vmin.y = ap->r.y;
8624                 if (vmin.z > ap->r.z)
8625                         vmin.z = ap->r.z;
8626                 if (vmax.x < ap->r.x)
8627                         vmax.x = ap->r.x;
8628                 if (vmax.y < ap->r.y)
8629                         vmax.y = ap->r.y;
8630                 if (vmax.z < ap->r.z)
8631                         vmax.z = ap->r.z;
8632         }
8633         if (min != NULL)
8634                 *min = vmin;
8635         if (max != NULL)
8636                 *max = vmax;
8637         return 0;       
8638 }
8639
8640 #pragma mark ====== Measurements ======
8641
8642 Double
8643 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
8644 {
8645         Vector r1, r2;
8646 /*      if (mp->is_xtal_coord) {
8647                 TransformVec(&r1, mp->cell->tr, vp1);
8648                 TransformVec(&r2, mp->cell->tr, vp2);
8649         } else */ {
8650                 r1 = *vp1;
8651                 r2 = *vp2;
8652         }
8653         VecDec(r1, r2);
8654         return VecLength(r1);
8655 }
8656
8657 Double
8658 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
8659 {
8660         Vector r1, r2, r3;
8661         double w;
8662 /*      if (mp->is_xtal_coord) {
8663                 TransformVec(&r1, mp->cell->tr, vp1);
8664                 TransformVec(&r2, mp->cell->tr, vp2);
8665                 TransformVec(&r3, mp->cell->tr, vp3);
8666         } else */ {
8667                 r1 = *vp1;
8668                 r2 = *vp2;
8669                 r3 = *vp3;
8670         }
8671         VecDec(r1, r2);
8672         VecDec(r3, r2);
8673         w = VecLength(r1) * VecLength(r3);
8674         if (w < 1e-20)
8675                 return NAN;
8676         return acos(VecDot(r1, r3) / w) * kRad2Deg;
8677 }
8678
8679 Double
8680 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
8681 {
8682         Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
8683         double w1, w2, w3;
8684 /*      if (mp->is_xtal_coord) {
8685                 TransformVec(&r1, mp->cell->tr, vp1);
8686                 TransformVec(&r2, mp->cell->tr, vp2);
8687                 TransformVec(&r3, mp->cell->tr, vp3);
8688                 TransformVec(&r4, mp->cell->tr, vp4);
8689         } else */ {
8690                 r1 = *vp1;
8691                 r2 = *vp2;
8692                 r3 = *vp3;
8693                 r4 = *vp4;
8694         }
8695         VecSub(r21, r1, r2);
8696         VecSub(r32, r2, r3);
8697         VecSub(r43, r3, r4);
8698         VecCross(v1, r21, r32);
8699         VecCross(v2, r32, r43);
8700         VecCross(v3, r32, v1);
8701         w1 = VecLength(v1);
8702         w2 = VecLength(v2);
8703         w3 = VecLength(v3);
8704         if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
8705                 return NAN;
8706         } else {
8707                 w1 = 1.0 / w1;
8708                 w2 = 1.0 / w2;
8709                 w3 = 1.0 / w3;
8710                 VecScaleSelf(v1, w1);
8711                 VecScaleSelf(v2, w2);
8712                 VecScaleSelf(v3, w3);
8713                 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
8714         }
8715 }
8716
8717 #pragma mark ====== XtalCell Parameters ======
8718
8719 void
8720 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
8721 {
8722         if (mp->cell != NULL) {
8723                 TransformVec(dst, mp->cell->tr, src);
8724         } else *dst = *src;
8725 }
8726
8727 void
8728 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
8729 {
8730         if (mp->cell != NULL) {
8731                 TransformVec(dst, mp->cell->rtr, src);
8732         } else *dst = *src;
8733 }
8734
8735 int
8736 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
8737 {
8738         static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
8739         int n1, n2, n3;
8740         Vector *vp1, *vp2, *vp3;
8741         Vector v1, v2;
8742
8743         if (cp == NULL)
8744                 return 0;
8745         for (n1 = 0; n1 < 3; n1++) {
8746                 if (cp->flags[n1] != 0)
8747                         break;
8748         }
8749         if (n1 == 3) {
8750                 /*  All directions are non-periodic  */
8751                 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
8752                 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
8753         } else {
8754                 n2 = (n1 + 1) % 3;
8755                 n3 = (n1 + 2) % 3;
8756                 vp1 = &(cp->axes[n1]);
8757                 vp2 = &(cp->axes[n2]);
8758                 vp3 = &(cp->axes[n3]);
8759                 cp->tr[n1*3] = vp1->x;
8760                 cp->tr[n1*3+1] = vp1->y;
8761                 cp->tr[n1*3+2] = vp1->z;
8762                 cp->tr[9] = cp->origin.x;
8763                 cp->tr[10] = cp->origin.y;
8764                 cp->tr[11] = cp->origin.z;
8765                 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
8766                         /*  1-dimensional or 2-dimensional system  */
8767                         /*  Create "dummy" axes, so that transforms between internal and cartesian coordinates are
8768                          possible with a single matrix  */
8769                         if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
8770                                 /*  1-dimensional  */
8771                                 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
8772                                 VecCross(v1, *vp1, xvec);
8773                                 VecCross(v2, *vp1, yvec);
8774                                 if (VecLength2(v1) < VecLength2(v2))
8775                                         v1 = v2;
8776                                 VecCross(v2, *vp1, v1);
8777                                 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
8778                                         return -1;   /*  Non-regular transform  */
8779                         } else if (cp->flags[n2] == 0) {
8780                                 v2 = *vp3;
8781                                 VecCross(v1, v2, *vp1);
8782                                 if (NormalizeVec(&v1, &v1))
8783                                         return -1;  /*  Non-regular transform  */
8784                         } else {
8785                                 v1 = *vp2;
8786                                 VecCross(v2, *vp1, v1);
8787                                 if (NormalizeVec(&v2, &v2))
8788                                         return -1;  /*  Non-regular transform  */
8789                         }
8790                         cp->tr[n2*3] = v1.x;
8791                         cp->tr[n2*3+1] = v1.y;
8792                         cp->tr[n2*3+2] = v1.z;
8793                         cp->tr[n3*3] = v2.x;
8794                         cp->tr[n3*3+1] = v2.y;
8795                         cp->tr[n3*3+2] = v2.z;
8796                 } else {
8797                         VecCross(v1, *vp1, *vp2);
8798                         if (fabs(VecDot(v1, *vp3)) < 1e-7)
8799                                 return -1;  /*  Non-regular transform  */
8800                         cp->tr[n2*3] = vp2->x;
8801                         cp->tr[n2*3+1] = vp2->y;
8802                         cp->tr[n2*3+2] = vp2->z;
8803                         cp->tr[n3*3] = vp3->x;
8804                         cp->tr[n3*3+1] = vp3->y;
8805                         cp->tr[n3*3+2] = vp3->z;
8806                 }
8807         }
8808         if (TransformInvert(cp->rtr, cp->tr))
8809                 return -1;  /*  Non-regular transform  */
8810
8811         /*  Calculate the reciprocal cell parameters  */
8812         cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[1] * cp->rtr[1] + cp->rtr[2] * cp->rtr[2]);
8813         cp->rcell[1] = sqrt(cp->rtr[3] * cp->rtr[3] + cp->rtr[4] * cp->rtr[4] + cp->rtr[5] * cp->rtr[5]);
8814         cp->rcell[2] = sqrt(cp->rtr[6] * cp->rtr[6] + cp->rtr[7] * cp->rtr[7] + cp->rtr[8] * cp->rtr[8]);
8815         cp->rcell[3] = acos((cp->rtr[3] * cp->rtr[6] + cp->rtr[4] * cp->rtr[7] + cp->rtr[5] * cp->rtr[8]) / (cp->rcell[1] * cp->rcell[2])) * kRad2Deg;
8816         cp->rcell[4] = acos((cp->rtr[6] * cp->rtr[0] + cp->rtr[7] * cp->rtr[1] + cp->rtr[8] * cp->rtr[2]) / (cp->rcell[2] * cp->rcell[0])) * kRad2Deg;
8817         cp->rcell[5] = acos((cp->rtr[0] * cp->rtr[3] + cp->rtr[1] * cp->rtr[4] + cp->rtr[2] * cp->rtr[5]) / (cp->rcell[0] * cp->rcell[1])) * kRad2Deg;
8818         
8819         if (calc_abc) {
8820                 /*  Calculate a, b, c, alpha, beta, gamma  */
8821                 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[1] * cp->tr[1] + cp->tr[2] * cp->tr[2]);
8822                 cp->cell[1] = sqrt(cp->tr[3] * cp->tr[3] + cp->tr[4] * cp->tr[4] + cp->tr[5] * cp->tr[5]);
8823                 cp->cell[2] = sqrt(cp->tr[6] * cp->tr[6] + cp->tr[7] * cp->tr[7] + cp->tr[8] * cp->tr[8]);
8824                 cp->cell[3] = acos((cp->tr[3] * cp->tr[6] + cp->tr[4] * cp->tr[7] + cp->tr[5] * cp->tr[8]) / (cp->cell[1] * cp->cell[2])) * kRad2Deg;
8825                 cp->cell[4] = acos((cp->tr[6] * cp->tr[0] + cp->tr[7] * cp->tr[1] + cp->tr[8] * cp->tr[2]) / (cp->cell[2] * cp->cell[0])) * kRad2Deg;
8826                 cp->cell[5] = acos((cp->tr[0] * cp->tr[3] + cp->tr[1] * cp->tr[4] + cp->tr[2] * cp->tr[5]) / (cp->cell[0] * cp->cell[1])) * kRad2Deg;
8827         }
8828         
8829         return 0;
8830 }
8831
8832 void
8833 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
8834 {
8835         XtalCell *cp;
8836         int i;
8837         Atom *ap;
8838         Transform cmat;
8839         if (mp == NULL)
8840                 return;
8841         __MoleculeLock(mp);
8842         memset(&cmat, 0, sizeof(Transform));
8843         if (a == 0.0) {
8844                 if (mp->cell != NULL) {
8845                         memmove(&cmat, &(mp->cell->tr), sizeof(Transform));
8846                         free(mp->cell);
8847                 } else {
8848                         cmat[0] = cmat[4] = cmat[8] = 1.0;
8849                 }
8850                 mp->cell = NULL;
8851         /*      mp->is_xtal_coord = 0; */
8852         } else {
8853                 cp = mp->cell;
8854                 if (cp == NULL) {
8855                         cp = (XtalCell *)calloc(sizeof(XtalCell), 1);
8856                         if (cp == NULL)
8857                                 Panic("Low memory during setting cell parameters");
8858                         mp->cell = cp;
8859                         cmat[0] = cmat[4] = cmat[8] = 1.0;
8860                 } else {
8861                 /*      if (mp->is_xtal_coord)
8862                                 memmove(&cmat, &(cp->tr), sizeof(Transform)); */
8863                 }
8864         /*      mp->is_xtal_coord = 1; */
8865                 /*  alpha, beta, gamma are in degree  */
8866                 cp->cell[0] = a;
8867                 cp->cell[1] = b;
8868                 cp->cell[2] = c;
8869                 cp->cell[3] = alpha;
8870                 cp->cell[4] = beta;
8871                 cp->cell[5] = gamma;
8872                 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
8873                         /*  c unique (hexagonal etc.)  */
8874                         Double cosa, cosb, sinb, cosg;
8875                         cosa = cos(alpha * kDeg2Rad);
8876                         cosb = cos(beta * kDeg2Rad);
8877                         sinb = sin(beta * kDeg2Rad);
8878                         cosg = cos(gamma * kDeg2Rad);
8879                         cp->axes[0].x = a * sinb;
8880                         cp->axes[0].y = 0;
8881                         cp->axes[0].z = a * cosb;
8882                         cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
8883                         cp->axes[1].z = b * cosa;
8884                         cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
8885                         cp->axes[2].x = 0;
8886                         cp->axes[2].y = 0;
8887                         cp->axes[2].z = c;
8888                 } else {
8889                         /*  b unique  */
8890                         Double cosg, sing, cosa, cosb;
8891                         cosa = cos(alpha * kDeg2Rad);
8892                         cosb = cos(beta * kDeg2Rad);
8893                         cosg = cos(gamma * kDeg2Rad);
8894                         sing = sin(gamma * kDeg2Rad);
8895                         cp->axes[0].x = a * sing;
8896                         cp->axes[0].y = a * cosg;
8897                         cp->axes[0].z = 0;
8898                         cp->axes[1].x = 0;
8899                         cp->axes[1].y = b;
8900                         cp->axes[1].z = 0;
8901                         cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
8902                         cp->axes[2].y = c * cosa;
8903                         cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
8904                 }
8905                 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
8906                 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
8907                 MoleculeCalculateCellFromAxes(cp, 0);
8908                 TransformMul(cmat, cp->rtr, cmat);
8909         }
8910         
8911         /*  Update the coordinates (if requested)  */
8912         if (convertCoordinates) {
8913                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8914                         TransformVec(&(ap->r), cmat, &(ap->r));
8915                 }
8916         }
8917         
8918         /*  Update the anisotropic parameters  */
8919         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8920                 Aniso *anp = ap->aniso;
8921                 if (anp != NULL) {
8922                         MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
8923                 }
8924         }
8925         __MoleculeUnlock(mp);
8926         sMoleculeNotifyChangeAppearance(mp);
8927 }
8928
8929 void
8930 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x13, Double x23, const Double *sigmaptr)
8931 {
8932         Double d, dx;
8933         int u = 0;
8934         const Double log2 = 0.693147180559945;
8935         const Double pi22 = 19.7392088021787;  /* 2*pi**2 */
8936         Transform m1, m2;
8937         Aniso *anp;
8938         XtalCell *cp;
8939         Vector axis[3];
8940         Double val[3];
8941         if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
8942                 return;
8943         anp = mp->atoms[n1].aniso;
8944         __MoleculeLock(mp);
8945         if (anp == NULL) {
8946                 anp = (Aniso *)calloc(sizeof(Aniso), 1);
8947                 if (anp == NULL) {
8948                         __MoleculeUnlock(mp);
8949                         Panic("Low memory during setting anisotropic atom parameters");
8950                 }
8951                 mp->atoms[n1].aniso = anp;
8952         }
8953         switch (type) {
8954                 case 1: d = 1; dx = 0.5; break;
8955                 case 2: d = log2; dx = log2; break;
8956                 case 3: d = log2; dx = log2 * 0.5; break;
8957                 case 4: u = 1; d = 0.25; dx = 0.25; break;
8958                 case 5: u = 1; d = 0.25; dx = 0.125; break;
8959                 case 8: u = 1; d = pi22; dx = pi22; break;
8960                 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
8961                 case 10: d = pi22; dx = pi22; break;
8962                 default: d = dx = 1; break;
8963         }
8964         anp->bij[0] = x11 * d;
8965         anp->bij[1] = x22 * d;
8966         anp->bij[2] = x33 * d;
8967         anp->bij[3] = x12 * dx;
8968         anp->bij[4] = x13 * dx;
8969         anp->bij[5] = x23 * dx;
8970         if (sigmaptr != NULL) {
8971                 anp->has_bsig = 1;
8972                 anp->bsig[0] = sigmaptr[0] * d;
8973                 anp->bsig[1] = sigmaptr[1] * d;
8974                 anp->bsig[2] = sigmaptr[2] * d;
8975                 anp->bsig[3] = sigmaptr[3] * dx;
8976                 anp->bsig[4] = sigmaptr[4] * dx;
8977                 anp->bsig[5] = sigmaptr[5] * dx;
8978         } else {
8979                 anp->has_bsig = 0;
8980                 anp->bsig[0] = anp->bsig[1] = anp->bsig[2] = anp->bsig[3] = anp->bsig[4] = anp->bsig[5] = 0.0;
8981         }
8982         cp = mp->cell;
8983         if (cp != NULL && u == 1) {
8984                 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
8985                 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
8986                 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
8987                 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
8988                 anp->bij[4] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[3] * kDeg2Rad); */
8989                 anp->bij[5] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[4] * kDeg2Rad); */
8990                 if (sigmaptr != NULL) {
8991                         anp->bsig[0] *= cp->rcell[0] * cp->rcell[0];
8992                         anp->bsig[1] *= cp->rcell[1] * cp->rcell[1];
8993                         anp->bsig[2] *= cp->rcell[2] * cp->rcell[2];
8994                         anp->bsig[3] *= cp->rcell[0] * cp->rcell[1];
8995                         anp->bsig[4] *= cp->rcell[2] * cp->rcell[0];
8996                         anp->bsig[5] *= cp->rcell[1] * cp->rcell[2];
8997                 }
8998         }
8999         
9000         /*  Calculate the principal axes (in Cartesian coordinates)  */
9001         /*  The principal axes are the eigenvectors of matrix At(B^-1)A, where
9002                 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
9003                 in which x and z are the crystal-space and cartesian coordinates. */
9004         m1[0] = anp->bij[0] / pi22;
9005         m1[4] = anp->bij[1] / pi22;
9006         m1[8] = anp->bij[2] / pi22;
9007         m1[1] = m1[3] = anp->bij[3] / pi22;
9008         m1[2] = m1[6] = anp->bij[4] / pi22;
9009         m1[5] = m1[7] = anp->bij[5] / pi22;
9010         MatrixInvert(m1, m1);
9011         if (cp != NULL) {
9012                 memmove(m2, cp->rtr, sizeof(Mat33));
9013                 MatrixMul(m1, m1, m2);
9014                 MatrixTranspose(m2, m2);
9015                 MatrixMul(m1, m2, m1);
9016         }
9017         MatrixSymDiagonalize(m1, val, axis);
9018         for (u = 0; u < 3; u++) {
9019                 if (val[u] < 0) {
9020                         fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
9021                         val[u] = 0.001;
9022                 } else {
9023                         val[u] = 1 / sqrt(val[u]);
9024                 }
9025                 anp->pmat[u*3] = axis[u].x * val[u];
9026                 anp->pmat[u*3+1] = axis[u].y * val[u];
9027                 anp->pmat[u*3+2] = axis[u].z * val[u];
9028         }
9029         __MoleculeUnlock(mp);
9030 }
9031
9032 int
9033 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic)
9034 {
9035         static Vector zeroVec = {0, 0, 0};
9036 /*      static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}; */
9037         XtalCell b;
9038         int n;
9039         if (mp == NULL)
9040                 return 0;
9041         if (ax == NULL) {
9042                 if (mp->cell != NULL)
9043                         free(mp->cell);
9044                 mp->cell = NULL;
9045         /*      mp->is_xtal_coord = 0; */
9046                 return 0;
9047         }
9048         memset(&b, 0, sizeof(b));
9049         b.axes[0] = (ax != NULL ? *ax : zeroVec);
9050         b.axes[1] = (ay != NULL ? *ay : zeroVec);
9051         b.axes[2] = (az != NULL ? *az : zeroVec);
9052         b.origin = *ao;
9053         memmove(b.flags, periodic, 3);
9054         if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
9055                 return -1;
9056         __MoleculeLock(mp);
9057         if (mp->cell != NULL) {
9058                 if (mp->cell->has_sigma) {
9059                         /*  Keep the sigma  */
9060                         b.has_sigma = 1;
9061                         memmove(b.cellsigma, mp->cell->cellsigma, sizeof(mp->cell->cellsigma));
9062                 }
9063                 free(mp->cell);
9064         }
9065         mp->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
9066         if (mp->cell != NULL) {
9067                 memmove(mp->cell, &b, sizeof(XtalCell));
9068                 n = 0;
9069         } else n = -2;  /*  Out of memory  */
9070         __MoleculeUnlock(mp);
9071         return n;
9072 }
9073
9074 #pragma mark ====== Fragment manipulation ======
9075
9076 static void
9077 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
9078 {
9079         Atom *ap;
9080         Int i, *cp;
9081         if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
9082                 return;
9083         IntGroupAdd(result, idx, 1);
9084         ap = ATOM_AT_INDEX(mp->atoms, idx);
9085         cp = AtomConnects(ap);
9086         for (i = 0; i < ap->nconnects; i++) {
9087                 int idx2 = cp[i];
9088                 if (IntGroupLookup(result, idx2, NULL))
9089                         continue;
9090                 sMoleculeFragmentSub(mp, idx2, result, exatoms);
9091         }
9092 }
9093
9094 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
9095     not containing the atoms in exatoms  */
9096 IntGroup *
9097 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
9098 {
9099         IntGroup *result;
9100         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9101                 return NULL;
9102         result = IntGroupNew();
9103         sMoleculeFragmentSub(mp, n1, result, exatoms);
9104         return result;
9105 }
9106
9107 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
9108     not containing the atoms n2, n3, ... (terminated by -1)  */
9109 IntGroup *
9110 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
9111 {
9112         int i;
9113         IntGroup *exatoms, *result;
9114         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9115                 return NULL;
9116         exatoms = IntGroupNew();
9117         for (i = 0; i < argc; i++)
9118                 IntGroupAdd(exatoms, argv[i], 1);
9119         result = IntGroupNew();
9120         sMoleculeFragmentSub(mp, n1, result, exatoms);
9121         IntGroupRelease(exatoms);
9122         return result;
9123 }
9124
9125 /*  The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
9126     not containing the atoms in exatoms  */
9127 IntGroup *
9128 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
9129 {
9130         IntGroupIterator iter;
9131         IntGroup *result;
9132         int i;
9133         if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
9134                 return NULL;
9135         IntGroupIteratorInit(inatoms, &iter);
9136         result = IntGroupNew();
9137         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9138                 sMoleculeFragmentSub(mp, i, result, exatoms);
9139         }
9140         IntGroupIteratorRelease(&iter);
9141         return result;
9142 }
9143
9144 /*  Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
9145     group is bound to the rest of the molecule via only one bond.
9146         If the result is true, then the atoms belonging to the (only) bond are returned
9147         in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
9148         and n2 can be NULL, if those informations are not needed.  */
9149 int
9150 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
9151 {
9152         Int i, i1, i2, j, k, bond_count, nval1, nval2, *cp;
9153         Atom *ap;
9154         if (mp == NULL || mp->natoms == 0 || group == NULL)
9155                 return 0;  /*  Invalid arguments  */
9156         bond_count = 0;
9157         for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
9158                 i2 = IntGroupGetEndPoint(group, i);
9159                 for (j = i1; j < i2; j++) {
9160                         if (j < 0 || j >= mp->natoms)
9161                                 return 0;  /*  Invalid atom group  */
9162                         ap = ATOM_AT_INDEX(mp->atoms, j);
9163                         cp = AtomConnects(ap);
9164                         for (k = 0; k < ap->nconnects; k++) {
9165                                 if (IntGroupLookup(group, cp[k], NULL) == 0) {
9166                                         bond_count++;
9167                                         nval1 = j;
9168                                         nval2 = cp[k];
9169                                         if (bond_count > 1)
9170                                                 return 0;  /*  Too many bonds  */
9171                                 }
9172                         }
9173                 }
9174         }
9175         if (bond_count == 1) {
9176                 if (n1 != NULL)
9177                         *n1 = nval1;
9178                 if (n2 != NULL)
9179                         *n2 = nval2;
9180                 return 1;
9181         } else {
9182                 return 0;
9183         }       
9184 }
9185
9186 /*  Returns non-zero if the given group is 'rotatable' in the molecule. The group
9187     is said to be 'rotatable' when either of the following conditions are met; (1)
9188         the group is detachable, or (2) the group consists of two bonded atoms that define
9189         a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
9190         (either a new IntGroup or 'group' with incremented reference count; thus the caller
9191         is responsible for releasing the returned value).  */
9192 int
9193 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
9194 {
9195         int i1, i2;
9196         if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
9197                 if (rotGroup != NULL) {
9198                         IntGroupRetain(group);
9199                         *rotGroup = group;
9200                 }
9201                 return 1;
9202         }
9203         if (group != NULL && IntGroupGetCount(group) == 2) {
9204                 i1 = IntGroupGetNthPoint(group, 0);
9205                 i2 = IntGroupGetNthPoint(group, 1);
9206                 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
9207                         IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
9208                         if (frag == NULL)
9209                                 return 0;
9210                         i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
9211                         if (i1 == 0) {
9212                                 IntGroupRelease(frag);
9213                                 if (rotGroup != NULL)
9214                                         *rotGroup = NULL;
9215                                 return 0;
9216                         }
9217                         if (rotGroup != NULL)
9218                                 *rotGroup = frag;
9219                         else if (frag != NULL)
9220                                 IntGroupRelease(frag);
9221                         return i1;
9222                 }
9223         }
9224         return 0;
9225 }
9226
9227 #pragma mark ====== Multiple frame ======
9228
9229 int
9230 MoleculeGetNumberOfFrames(Molecule *mp)
9231 {
9232         if (mp == NULL)
9233                 return 0;
9234         if (mp->nframes <= 0) {
9235                 /*  Recalculate  */
9236                 int i, n;
9237                 Atom *ap;
9238                 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9239                         if (ap->nframes > n)
9240                                 n = ap->nframes;
9241                 }
9242                 if (n == 0)
9243                         n = 1;
9244                 mp->nframes = n;
9245         }
9246         return mp->nframes;
9247 }
9248
9249 int
9250 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
9251 {
9252         int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
9253         Vector *tempv, *vp;
9254         Atom *ap;
9255         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
9256                 return -1;
9257
9258         n_old = MoleculeGetNumberOfFrames(mp);
9259         n_new = n_old + count;
9260         last_inserted = IntGroupGetNthPoint(group, count - 1);
9261         if (n_new <= last_inserted) {
9262                 exframes = last_inserted - n_new + 1;  /*  number of extra frames that will be silently inserted  */
9263                 n_new += exframes;
9264         } else exframes = 0;
9265
9266         tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4);  /*  "*4" for handling cells  */
9267         if (tempv == NULL)
9268                 return -1;
9269
9270         __MoleculeLock(mp);
9271
9272         /*  Copy back the current coordinates  */
9273         /*  No change in the current coordinates, but the frame buffer is updated  */
9274         MoleculeSelectFrame(mp, mp->cframe, 1); 
9275         
9276         /*  Expand ap->frames for all atoms  */
9277         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9278                 if (ap->frames == NULL)
9279                         vp = (Vector *)calloc(sizeof(Vector), n_new);
9280                 else
9281                         vp = (Vector *)realloc(ap->frames, sizeof(Vector) * n_new);
9282                 if (vp == NULL) {
9283                         __MoleculeUnlock(mp);
9284                         return -1;
9285                 }
9286                 for (j = ap->nframes; j < n_new; j++)
9287                         vp[j] = ap->r;
9288                 ap->frames = vp;
9289         }
9290         if (mp->cell != NULL && (mp->frame_cells != NULL || inFrameCell != NULL)) {
9291                 vp = mp->frame_cells;
9292                 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, n_new - 1, NULL);
9293                 if (vp == NULL) {
9294                         /*  Set the first cell parameters  */
9295                         mp->frame_cells[0] = mp->cell->axes[0];
9296                         mp->frame_cells[1] = mp->cell->axes[1];
9297                         mp->frame_cells[2] = mp->cell->axes[2];
9298                         mp->frame_cells[3] = mp->cell->origin;
9299                 }
9300 /*              vp = mp->frame_cells;
9301                 if (mp->frame_cells == NULL) {
9302                         vp = (Vector *)calloc(sizeof(Vector), n_new * 4);
9303                         vp[0] = mp->cell->axes[0];
9304                         vp[1] = mp->cell->axes[1];
9305                         vp[2] = mp->cell->axes[2];
9306                         vp[3] = mp->cell->origin;
9307                 } else
9308                         vp = (Vector *)realloc(mp->frame_cells, sizeof(Vector) * 4 * n_new);
9309                 if (vp == NULL) {
9310                         __MoleculeUnlock(mp);
9311                         return -1;
9312                 }
9313                 mp->frame_cells = vp; */
9314         }
9315         
9316         /*  group = [n0..n1-1, n2..n3-1, ...]  */
9317         /*  s = t = 0,  */
9318         /*  tempv[0..n0-1] <- ap[0..n0-1], s += n0,
9319             tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
9320                 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
9321                 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
9322                 ...
9323                 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
9324                 At last, s will become n_old and t will become count.  */
9325         for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9326                 int s, t, ns, ne, mult;
9327                 Vector cr;
9328                 ne = s = t = 0;
9329                 if (i == mp->natoms) {
9330                         if (mp->cell == NULL || mp->frame_cells == NULL)
9331                                 break;
9332                         vp = mp->frame_cells;
9333                         mult = 4;
9334                 } else {
9335                         cr = ap->r;
9336                         vp = ap->frames;
9337                         mult = 1;
9338                 }
9339                 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
9340                         if (ns > ne) {
9341                                 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
9342                                 s += ns - ne;
9343                         }
9344                         ne = IntGroupGetEndPoint(group, j);
9345                         while (ns < ne) {
9346                                 if (i == mp->natoms) {
9347                                         if (inFrameCell != NULL) {
9348                                                 tempv[ns * 4] = inFrameCell[t * 4];
9349                                                 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
9350                                                 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
9351                                                 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
9352                                         } else {
9353                                                 tempv[ns * 4] = mp->cell->axes[0];
9354                                                 tempv[ns * 4 + 1] = mp->cell->axes[1];
9355                                                 tempv[ns * 4 + 2] = mp->cell->axes[2];
9356                                                 tempv[ns * 4 + 3] = mp->cell->origin;
9357                                         }
9358                                 } else {
9359                                         if (inFrame != NULL)
9360                                                 tempv[ns] = inFrame[natoms * t + i];
9361                                         else
9362                                                 tempv[ns] = cr;
9363                                 }
9364                                 t++;
9365                                 ns++;
9366                         }
9367                 }
9368                 if (n_new > ne) {
9369                         memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
9370                         s += n_new - ne;
9371                 }
9372                 if (i < mp->natoms)
9373                         ap->nframes = n_new;
9374                 memmove(vp, tempv, sizeof(Vector) * mult * n_new);
9375         }
9376         free(tempv);
9377         mp->nframes = n_new;
9378         MoleculeSelectFrame(mp, last_inserted, 0);
9379         MoleculeIncrementModifyCount(mp);
9380         __MoleculeUnlock(mp);
9381         return count;
9382 }
9383
9384 int
9385 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
9386 {
9387         int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
9388         Vector *tempv, *vp;
9389         Atom *ap;
9390         IntGroup *group, *group2;
9391
9392         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
9393                 return -1;
9394
9395         /*  outFrame[] should have enough size for Vector * natoms * group.count  */
9396         memset(outFrame, 0, sizeof(Vector) * natoms * count);
9397         if (mp->cell != NULL && mp->frame_cells != NULL)
9398                 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
9399
9400         n_old = MoleculeGetNumberOfFrames(mp);
9401         if (n_old == 1)
9402                 return -2;  /*  Cannot delete last frame  */
9403
9404         group = IntGroupNew();
9405         group2 = IntGroupNewWithPoints(0, n_old, -1);
9406         IntGroupIntersect(inGroup, group2, group);
9407         IntGroupRelease(group2);
9408         count = IntGroupGetCount(group);
9409         n_new = n_old - count;
9410         if (n_new < 1) {
9411                 IntGroupRelease(group);
9412                 return -2;  /*  Trying to delete too many frames  */
9413         }
9414         tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4);  /*  "*4" for handling cells  */
9415         if (tempv == NULL) {
9416                 IntGroupRelease(group);
9417                 return -1;
9418         }
9419
9420         __MoleculeLock(mp);
9421
9422         /*  Copy back the current coordinates  */
9423         /*  No change in the current coordinates, but the frame buffer is updated  */
9424         MoleculeSelectFrame(mp, mp->cframe, 1); 
9425
9426         /*  Determine which frame should be selected after removal is completed  */
9427         {
9428                 int n1;
9429                 if (IntGroupLookup(group, mp->cframe, &i)) {
9430                         /*  cframe will be removed  */
9431                         n1 = IntGroupGetStartPoint(group, i) - 1;
9432                         if (n1 < 0)
9433                                 n1 = IntGroupGetEndPoint(group, i);
9434                 } else n1 = mp->cframe;
9435                 /*  Change to that frame  */
9436                 MoleculeSelectFrame(mp, n1, 0);
9437                 group2 = IntGroupNewFromIntGroup(group);
9438                 IntGroupReverse(group2, 0, n_old);
9439                 new_cframe = IntGroupLookupPoint(group2, n1);
9440                 if (new_cframe < 0)
9441                         return -3;  /*  This cannot happen  */
9442                 IntGroupRelease(group2);
9443         }
9444
9445         /*  group = [n0..n1-1, n2..n3-1, ...]  */
9446         /*  s = t = 0, */
9447         /*  tempv[0..n0-1] -> ap[0..n0-1], s += n0,
9448             tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
9449                 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
9450                 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
9451                 ...
9452                 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
9453                 At last, s will become n_new and t will become count.  */
9454         nframes = 0;
9455         for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9456                 int s, t, j, ns, ne;
9457                 int mult;
9458                 /*  if i == mp->natoms, mp->frame_cells is handled  */
9459                 if (i == mp->natoms) {
9460                         if (mp->cell == NULL || mp->frame_cells == NULL)
9461                                 break;
9462                         mult = 4;
9463                         vp = mp->frame_cells;
9464                         old_count = n_old;
9465                 } else {
9466                         mult = 1;
9467                         vp = ap->frames;
9468                         if (vp == NULL) {
9469                                 ap->frames = vp = (Vector *)calloc(sizeof(Vector), n_old);
9470                                 if (vp == NULL) {
9471                                         __MoleculeUnlock(mp);
9472                                         return -1;
9473                                 }
9474                         }
9475                         old_count = ap->nframes;
9476                 }
9477
9478                 /*  Copy vp to tempv  */
9479                 memset(tempv, 0, sizeof(Vector) * mult * n_old);
9480                 memmove(tempv, vp, sizeof(Vector) * mult * (old_count > n_old ? n_old : old_count));
9481                 ne = ns = s = t = 0;
9482                 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
9483                         if (ns > n_old)
9484                                 ns = n_old;
9485                         if (ns > ne) {
9486                                 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (ns - ne));
9487                                 s += ns - ne;
9488                         }
9489                         ne = IntGroupGetEndPoint(group, j);
9490                         if (ne > n_old)
9491                                 ne = n_old;
9492                         while (ns < ne) {
9493                                 if (i < mp->natoms)
9494                                         outFrame[natoms * t + i] = tempv[ns];
9495                                 else if (outFrameCell != NULL) {
9496                                         outFrameCell[t * 4] = tempv[ns * 4];
9497                                         outFrameCell[t * 4 + 1] = tempv[ns * 4 + 1];
9498                                         outFrameCell[t * 4 + 2] = tempv[ns * 4 + 2];
9499                                         outFrameCell[t * 4 + 3] = tempv[ns * 4 + 3];
9500                                 }
9501                                 t++;
9502                                 ns++;
9503                         }
9504                 }
9505                 if (n_old > ne) {
9506                         memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (n_old - ne));
9507                         s += n_old - ne;
9508                 }
9509                 if (i < mp->natoms)
9510                         ap->nframes = s;
9511                 if (nframes < s)
9512                         nframes = s;
9513                 if (s <= 1) {
9514                         if (i < mp->natoms) {
9515                                 free(ap->frames);
9516                                 ap->frames = NULL;
9517                                 ap->nframes = 0;
9518                         } else {
9519                                 free(mp->frame_cells);
9520                                 mp->frame_cells = NULL;
9521                         }
9522                 } else {
9523                         if (i < mp->natoms)
9524                                 ap->frames = (Vector *)realloc(ap->frames, sizeof(Vector) * s);
9525                         else {
9526                                 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, s - 1, NULL);
9527                         }
9528                 }
9529         }
9530         free(tempv);
9531         mp->nframes = nframes;
9532         
9533         /*  Select the "last" frame; do not "copy back" the coordinates to the frame table  */
9534 /*      i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
9535         MoleculeSelectFrame(mp, new_cframe, 0);
9536
9537         IntGroupRelease(group);
9538
9539         MoleculeIncrementModifyCount(mp);
9540         __MoleculeUnlock(mp);
9541         return count;
9542 }
9543
9544 int
9545 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
9546 {
9547         int i, cframe, ok;
9548         Atom *ap;
9549         if (mp == NULL || mp->natoms == 0)
9550                 return -1;
9551         cframe = mp->cframe;
9552         ok = 0;
9553         __MoleculeLock(mp);
9554         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9555                 if (copyback && cframe >= 0 && cframe < ap->nframes) {
9556                         /*  Write the current coordinate back to the frame array  */
9557                         ap->frames[cframe] = ap->r;
9558                 }
9559                 if (frame >= 0 && frame < ap->nframes) {
9560                         /*  Read the coordinate from the frame array  */
9561                         ap->r = ap->frames[frame];
9562                         ok = 1;
9563                 }
9564         }
9565
9566         if (mp->cell != NULL && mp->frame_cells != NULL) {
9567                 /*  Write the current cell back to the frame_cells array  */
9568                 if (copyback && cframe >= 0) {
9569                         Vector *vp = (Vector *)AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, cframe, NULL);
9570                         vp[0] = mp->cell->axes[0];
9571                         vp[1] = mp->cell->axes[1];
9572                         vp[2] = mp->cell->axes[2];
9573                         vp[3] = mp->cell->origin;
9574                 }
9575                 /*  Set the cell from the frame array  */
9576                 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);
9577         }
9578         mp->needsMDCopyCoordinates = 1;
9579         __MoleculeUnlock(mp);
9580         if (ok) {
9581                 mp->cframe = frame;
9582                 sMoleculeNotifyChangeAppearance(mp);
9583                 return frame;
9584         } else return -1;
9585 }
9586
9587 #pragma mark ====== MO calculation ======
9588
9589 /*  Calculate an MO value for a single point.  */
9590 /*  Index is the MO number (1-based)  */
9591 /*  tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom.  */
9592 static Double
9593 sCalcMOPoint(const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
9594 {
9595         ShellInfo *sp;
9596         PrimInfo *pp;
9597         Double val, tval, *cnp, *tmpp, *mobasep, *mop;
9598         Int i, j;
9599         /*  Cache dr and |dr|^2  */
9600         for (i = 0; i < bset->natoms; i++) {
9601                 Vector r = bset->pos[i];
9602                 tmp[i * 4] = r.x = vp->x - r.x;
9603                 tmp[i * 4 + 1] = r.y = vp->y - r.y;
9604                 tmp[i * 4 + 2] = r.z = vp->z - r.z;
9605                 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
9606         }
9607         /*  Iterate over all shells  */
9608         val = 0.0;
9609         mobasep = bset->mo + (index - 1) * bset->ncomps;
9610         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
9611                 pp = bset->priminfos + sp->p_idx;
9612                 cnp = bset->cns + sp->cn_idx;
9613                 tmpp = tmp + sp->a_idx * 4;
9614                 mop = mobasep + sp->m_idx;
9615                 switch (sp->sym) {
9616                         case kGTOType_S: {
9617                                 tval = 0;
9618                                 for (j = 0; j < sp->nprim; j++) {
9619                                         tval += *cnp++ * exp(-pp->A * tmpp[3]);
9620                                         pp++;
9621                                 }
9622                                 val += mop[0] * tval;
9623                                 break;
9624                         }
9625                         case kGTOType_P: {
9626                                 Double x, y, z;
9627                                 x = y = z = 0;
9628                                 for (j = 0; j < sp->nprim; j++) {
9629                                         tval = exp(-pp->A * tmpp[3]);
9630                                         x += *cnp++ * tval;
9631                                         y += *cnp++ * tval;
9632                                         z += *cnp++ * tval;
9633                                         pp++;
9634                                 }
9635                                 x *= mop[0] * tmpp[0];
9636                                 y *= mop[1] * tmpp[1];
9637                                 z *= mop[2] * tmpp[2];
9638                                 val += x + y + z;
9639                                 break;
9640                         }
9641                         case kGTOType_SP: {
9642                                 Double t, x, y, z;
9643                                 t = x = y = z = 0;
9644                                 for (j = 0; j < sp->nprim; j++) {
9645                                         tval = exp(-pp->A * tmpp[3]);
9646                                         t += *cnp++ * tval;
9647                                         x += *cnp++ * tval;
9648                                         y += *cnp++ * tval;
9649                                         z += *cnp++ * tval;
9650                                         pp++;
9651                                 }
9652                                 t *= mop[0];
9653                                 x *= mop[1] * tmpp[0];
9654                                 y *= mop[2] * tmpp[1];
9655                                 z *= mop[3] * tmpp[2];
9656                                 val += t + x + y + z;
9657                                 break;
9658                         }
9659                         case kGTOType_D: {
9660                                 Double xx, yy, zz, xy, xz, yz;
9661                                 xx = yy = zz = xy = xz = yz = 0;
9662                                 for (j = 0; j < sp->nprim; j++) {
9663                                         tval = exp(-pp->A * tmpp[3]);
9664                                         xx += *cnp++ * tval;
9665                                         yy += *cnp++ * tval;
9666                                         zz += *cnp++ * tval;
9667                                         xy += *cnp++ * tval;
9668                                         xz += *cnp++ * tval;
9669                                         yz += *cnp++ * tval;
9670                                         pp++;
9671                                 }
9672                                 xx *= mop[0] * tmpp[0] * tmpp[0];
9673                                 yy *= mop[1] * tmpp[1] * tmpp[1];
9674                                 zz *= mop[2] * tmpp[2] * tmpp[2];
9675                                 xy *= mop[3] * tmpp[0] * tmpp[1];
9676                                 xz *= mop[4] * tmpp[0] * tmpp[2];
9677                                 yz *= mop[5] * tmpp[1] * tmpp[2];
9678                                 val += xx + yy + zz + xy + xz + yz;
9679                                 break;
9680                         }
9681                         case kGTOType_D5: {
9682                                 Double d0, d1p, d1n, d2p, d2n;
9683                                 d0 = d1p = d1n = d2p = d2n = 0;
9684                                 for (j = 0; j < sp->nprim; j++) {
9685                                         tval = exp(-pp->A * tmpp[3]);
9686                                         d0 += *cnp++ * tval;
9687                                         d1p += *cnp++ * tval;
9688                                         d1n += *cnp++ * tval;
9689                                         d2p += *cnp++ * tval;
9690                                         d2n += *cnp++ * tval;
9691                                         pp++;
9692                                 }
9693                                 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
9694                                 d1p *= mop[1] * tmpp[0] * tmpp[2];
9695                                 d1n *= mop[2] * tmpp[1] * tmpp[2];
9696                                 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
9697                                 d2n *= mop[4] * tmpp[0] * tmpp[1];
9698                                 val += d0 + d1p + d1n + d2p + d2n;
9699                                 break;
9700                         }
9701                 }
9702         }
9703         return val;
9704 }
9705
9706 /*  Calculate one MO. The input vectors should be in bohr unit (angstrom * 1.889725989 = kAngstrom2Bohr).  */
9707 /*  mono is the MO number (1-based)  */
9708 int
9709 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)
9710 {
9711         int ix, iy, iz, n, nn;
9712         Cube *cp;
9713         Double *tmp;
9714         if (mp == NULL || mp->bset == NULL)
9715                 return -1;
9716         if (mp->bset->cns == NULL) {
9717                 if (sSetupGaussianCoefficients(mp->bset) != 0)
9718                         return -1;
9719         }
9720         cp = (Cube *)calloc(sizeof(Cube), 1);
9721         if (cp == NULL) {
9722                 return -1;
9723         }
9724         cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
9725         if (cp->dp == NULL) {
9726                 free(cp);
9727                 return -1;
9728         }
9729         cp->idn = mono;
9730         cp->origin = *op;
9731         cp->dx = *dxp;
9732         cp->dy = *dyp;
9733         cp->dz = *dzp;
9734         cp->nx = nx;
9735         cp->ny = ny;
9736         cp->nz = nz;
9737         
9738         /*  TODO: use multithread  */
9739         tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms * 4);
9740         if (tmp == NULL) {
9741                 free(cp->dp);
9742                 free(cp);
9743                 return -1;
9744         }
9745         n = nn = 0;
9746         for (ix = 0; ix < nx; ix++) {
9747                 Vector p;
9748                 for (iy = 0; iy < ny; iy++) {
9749                         for (iz = 0; iz < nz; iz++) {
9750                                 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
9751                                 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
9752                                 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
9753                                 cp->dp[n++] = sCalcMOPoint(mp->bset, mono, &p, tmp);
9754                         }
9755                         if (callback != NULL && n - nn > 100) {
9756                                 nn = n;
9757                                 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
9758                                         free(cp->dp);
9759                                         free(cp);
9760                                         free(tmp);
9761                                         return -2;  /*  User interrupt  */
9762                                 }
9763                         }
9764                 }
9765         }
9766         free(tmp);
9767
9768         AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
9769         return mp->bset->ncubes - 1;
9770 }
9771
9772 int
9773 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
9774 {
9775         int i;
9776         Vector rmin, rmax, *vp;
9777         Double dr, dx, dy, dz;
9778         if (mp == NULL || mp->bset == NULL || mp->bset->natoms == 0)
9779                 return -1;
9780         if (npoints <= 0)
9781                 npoints = 1000000;
9782         rmin.x = rmin.y = rmin.z = 1e10;
9783         rmax.x = rmax.y = rmax.z = -1e10;
9784         for (i = 0, vp = mp->bset->pos; i < mp->bset->natoms; i++, vp++) {
9785                 dr = RadiusForAtomicNumber(ATOM_AT_INDEX(mp->atoms, i)->atomicNumber);
9786                 if (dr == 0.0)
9787                         dr = 1.0;
9788                 dr = dr * kAngstrom2Bohr * 3.0 + 2.0;
9789                 if (rmin.x > vp->x - dr)
9790                         rmin.x = vp->x - dr;
9791                 if (rmin.y > vp->y - dr)
9792                         rmin.y = vp->y - dr;
9793                 if (rmin.z > vp->z - dr)
9794                         rmin.z = vp->z - dr;
9795                 if (rmax.x < vp->x + dr)
9796                         rmax.x = vp->x + dr;
9797                 if (rmax.y < vp->y + dr)
9798                         rmax.y = vp->y + dr;
9799                 if (rmax.z < vp->z + dr)
9800                         rmax.z = vp->z + dr;
9801         }
9802         dx = rmax.x - rmin.x;
9803         dy = rmax.y - rmin.y;
9804         dz = rmax.z - rmin.z;
9805         dr = pow(dx * dy * dz / npoints, 1.0/3.0);
9806         *nx = floor(dx / dr + 0.5);
9807         *ny = floor(dy / dr + 0.5);
9808         *nz = floor(dz / dr + 0.5);
9809         if (*nx == 0)
9810                 *nx = 1;
9811         if (*ny == 0)
9812                 *ny = 1;
9813         if (*nz == 0)
9814                 *nz = 1;
9815         *op = rmin;
9816         xp->x = yp->y = zp->z = dr;
9817         xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
9818         return 0;
9819 }
9820
9821 const Cube *
9822 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
9823 {
9824         if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
9825                 return NULL;
9826         return mp->bset->cubes[index];
9827 }
9828
9829 int
9830 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
9831 {
9832         int i;
9833         if (mp == NULL || mp->bset == NULL)
9834                 return -1;
9835         for (i = 0; i < mp->bset->ncubes; i++) {
9836                 if (mp->bset->cubes[i]->idn == mono)
9837                         return i;
9838         }
9839         return -1;
9840 }
9841
9842 int
9843 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
9844 {
9845         int n;
9846         if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
9847                 return -1;
9848         CubeRelease(mp->bset->cubes[index]);
9849         if (index < n - 1)
9850                 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
9851         if (--(mp->bset->ncubes) == 0) {
9852                 free(mp->bset->cubes);
9853                 mp->bset->cubes = NULL;
9854         }
9855         return mp->bset->ncubes;
9856 }
9857
9858 int
9859 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
9860 {
9861         const Cube *cp;
9862         int i, j, k, n;
9863         FILE *fp;
9864         if (mp == NULL || mp->bset == NULL)
9865                 return -1;  /*  Molecule or the basis set information is empty  */
9866         cp = MoleculeGetCubeAtIndex(mp, index);
9867         if (cp == NULL)
9868                 return -2;  /*  MO not yet calculated  */
9869         fp = fopen(fname, "wb");
9870         if (fp == NULL)
9871                 return -3;  /*  Cannot create file  */
9872
9873         /*  Comment lines  */
9874         fprintf(fp, "%s MO=%d\n", comment, cp->idn);
9875         fprintf(fp, " MO coefficients\n");
9876         
9877         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms), cp->origin.x, cp->origin.y, cp->origin.z);
9878         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx, cp->dx.x, cp->dx.y, cp->dx.z);
9879         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny, cp->dy.x, cp->dy.y, cp->dy.z);
9880         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz, cp->dz.x, cp->dz.y, cp->dz.z);
9881         
9882         /*  Atomic information  */
9883         for (i = 0; i < mp->bset->natoms; i++) {
9884                 Vector *vp = mp->bset->pos + i;
9885                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
9886                 /*  The second number should actually be the effective charge  */
9887                 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber, vp->x, vp->y, vp->z);
9888         }
9889         fprintf(fp, "%5d%5d\n", 1, 1);
9890         
9891         /*  3D data  */
9892         for (i = n = 0; i < cp->nx; i++) {
9893                 for (j = 0; j < cp->ny; j++) {
9894                         for (k = 0; k < cp->nz; k++) {
9895                                 fprintf(fp, " %12.5e", cp->dp[n++]);
9896                                 if (k == cp->nz - 1 || k % 6 == 5)
9897                                         fprintf(fp, "\n");
9898                         }
9899                 }
9900         }
9901         fclose(fp);
9902         return 0;
9903 }