OSDN Git Service

Some unused fields of Molecule were removed.
[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 /*  These are the pasteboard data type. Since the internal representation of the
34         pasteboard data includes binary data that may be dependent on the software version,
35     the revision number is appended to these strings on startup (See MyApp::OnInit())  */
36 char *gMoleculePasteboardType = "Molecule";
37 char *gParameterPasteboardType = "Parameter";
38
39 #pragma mark ====== Utility function ======
40
41 int
42 strlen_limit(const char *s, int limit)
43 {
44         int len;
45         for (len = 0; *s != 0 && (limit < 0 || len < limit); s++, len++);
46         return len;
47 }
48
49 #pragma mark ======  Atom handling  ======
50
51 static Atom *
52 s_AtomDuplicate(Atom *dst, const Atom *src, Int copy_frame)
53 {
54         if (dst == NULL) {
55                 dst = (Atom *)malloc(gSizeOfAtomRecord);
56                 if (dst == NULL)
57                         return NULL;
58         }
59         memmove(dst, src, gSizeOfAtomRecord);
60         if (src->aniso != NULL) {
61                 dst->aniso = (Aniso *)malloc(sizeof(Aniso));
62                 if (dst->aniso != NULL)
63                         memmove(dst->aniso, src->aniso, sizeof(Aniso));
64         }
65         if (src->frames != NULL && copy_frame) {
66                 dst->frames = (Vector *)malloc(sizeof(Vector) * src->nframes);
67                 if (dst->frames != NULL) {
68                         memmove(dst->frames, src->frames, sizeof(Vector) * src->nframes);
69                         dst->nframes = src->nframes;
70                 } else {
71                         dst->nframes = 0;
72                 }
73         }
74         if (src->connect.count > ATOM_CONNECT_LIMIT) {
75                 dst->connect.u.ptr = NULL;
76                 dst->connect.count = 0;
77                 NewArray(&(dst->connect.u.ptr), &(dst->connect.count), sizeof(Int), src->connect.count);
78                 memmove(dst->connect.u.ptr, src->connect.u.ptr, sizeof(Int) * src->connect.count);
79         }
80         return dst;
81 }
82
83 Atom *
84 AtomDuplicate(Atom *dst, const Atom *src)
85 {
86         return s_AtomDuplicate(dst, src, 1);
87 }
88
89 Atom *
90 AtomDuplicateNoFrame(Atom *dst, const Atom *src)
91 {
92         return s_AtomDuplicate(dst, src, 0);
93 }
94
95 void
96 AtomClean(Atom *ap)
97 {
98         if (ap->aniso != NULL) {
99                 free(ap->aniso);
100                 ap->aniso = NULL;
101         }
102         if (ap->frames != NULL) {
103                 free(ap->frames);
104                 ap->frames = NULL;
105                 ap->nframes = 0;
106         }
107         if (ap->connect.count > ATOM_CONNECT_LIMIT) {
108                 ap->connect.count = 0;
109                 free(ap->connect.u.ptr);
110                 ap->connect.u.ptr = NULL;
111         }
112 }
113
114 void
115 CubeRelease(Cube *cp)
116 {
117         if (cp != NULL) {
118                 if (cp->dp != NULL)
119                         free(cp->dp);
120                 free(cp);
121         }
122 }
123
124 void
125 BasisSetRelease(BasisSet *bset)
126 {
127         int i;
128         if (bset == NULL)
129                 return;
130         if (bset->shells != NULL)
131                 free(bset->shells);
132         if (bset->priminfos != NULL)
133                 free(bset->priminfos);
134         if (bset->mo != NULL)
135                 free(bset->mo);
136         if (bset->cns != NULL)
137                 free(bset->cns);
138         if (bset->moenergies != NULL)
139                 free(bset->moenergies);
140         if (bset->scfdensities != NULL)
141                 free(bset->scfdensities);
142         if (bset->pos != NULL)
143                 free(bset->pos);
144         if (bset->nuccharges != NULL)
145                 free(bset->nuccharges);
146         if (bset->cubes != NULL) {
147                 for (i = 0; i < bset->ncubes; i++) {
148                         CubeRelease(bset->cubes[i]);
149                 }
150                 free(bset->cubes);
151         }
152         free(bset);
153 }
154
155 Int *
156 AtomConnectData(AtomConnect *ac)
157 {
158         if (ac == NULL)
159                 return NULL;
160         return ATOM_CONNECT_PTR(ac);
161 }
162
163 void
164 AtomConnectResize(AtomConnect *ac, Int nconnects)
165 {
166         Int *p;
167         if (ac == NULL)
168                 return;
169         if (nconnects <= ATOM_CONNECT_LIMIT) {
170                 if (ac->count > ATOM_CONNECT_LIMIT) {
171                         p = ac->u.ptr;
172                         memmove(ac->u.data, p, sizeof(Int) * nconnects);
173                         free(p);
174                 }
175         } else {
176                 if (ac->count <= ATOM_CONNECT_LIMIT) {
177                         p = NULL;
178                         ac->count = 0;
179                         NewArray(&p, &(ac->count), sizeof(Int), nconnects);
180                         memmove(p, ac->u.data, sizeof(Int) * ac->count);
181                         ac->u.ptr = p;
182                 } else if (ac->count < nconnects) {
183                         /*  Reallocate  */
184                         AssignArray(&(ac->u.ptr), &(ac->count), sizeof(Int), nconnects - 1, NULL);
185                 }
186         }
187         ac->count = nconnects;
188 }
189
190 void
191 AtomConnectInsertEntry(AtomConnect *ac, Int idx, Int connect)
192 {
193         Int n, *p;
194         if (ac == NULL)
195                 return;
196         if (idx < 0 || idx >= ac->count)
197                 idx = ac->count;
198         AtomConnectResize(ac, ac->count + 1);
199         n = ac->count - idx - 1;  /*  Number of entries to be moved towards the bottom  */
200         p = ATOM_CONNECT_PTR(ac);
201         if (n > 0) {
202                 memmove(p + idx + 1, p + idx, sizeof(Int) * n);
203         }
204         p[idx] = connect;
205 }
206
207 void
208 AtomConnectDeleteEntry(AtomConnect *ac, Int idx)
209 {
210         Int n, *p;
211         if (ac == NULL)
212                 return;
213         if (idx < 0 || idx >= ac->count)
214                 return;
215         n = ac->count - idx - 1;  /*  Number of entries to be moved towards the top  */
216         p = ATOM_CONNECT_PTR(ac);
217         if (n > 0) {
218                 memmove(p + idx, p + idx + 1, sizeof(Int) * n);
219         }
220         AtomConnectResize(ac, ac->count - 1);
221 }
222
223 #pragma mark ====== Accessor types ======
224
225 MolEnumerable *
226 MolEnumerableNew(Molecule *mol, int kind)
227 {
228         MolEnumerable *mseq = (MolEnumerable *)calloc(sizeof(MolEnumerable), 1);
229         if (mseq != NULL) {
230                 mseq->mol = MoleculeRetain(mol);
231                 mseq->kind = kind;
232         }
233         return mseq;
234 }
235
236 void
237 MolEnumerableRelease(MolEnumerable *mseq)
238 {
239         if (mseq != NULL) {
240                 MoleculeRelease(mseq->mol);
241                 free(mseq);
242         }
243 }
244
245 AtomRef *
246 AtomRefNew(Molecule *mol, int idx)
247 {
248         AtomRef *aref = (AtomRef *)calloc(sizeof(AtomRef), 1);
249         if (aref != NULL) {
250                 aref->mol = MoleculeRetain(mol);
251                 aref->idx = idx;
252         }
253         return aref;
254 }
255
256 void
257 AtomRefRelease(AtomRef *aref)
258 {
259         if (aref != NULL) {
260                 MoleculeRelease(aref->mol);
261                 free(aref);
262         }
263 }
264
265 #pragma mark ====== Creation of molecules ======
266
267 Molecule *
268 MoleculeNew(void)
269 {
270         char name[40];
271         Molecule *mp = (Molecule *)calloc(sizeof(Molecule), 1);
272         if (mp == NULL)
273                 Panic("Cannot allocate new molecule record");
274         snprintf(name, sizeof name, "Untitled %d", sMoleculeUntitledCount++);
275         ObjectInit((Object *)mp, (Object **)&sMoleculeRoot, name);
276         return mp;
277 }
278
279 Molecule *
280 MoleculeNewWithName(const char *name)
281 {
282         Molecule *mp = MoleculeNew();
283         MoleculeSetName(mp, name);
284         return mp;
285 }
286
287 Molecule *
288 MoleculeInitWithAtoms(Molecule *mp, const Atom *atoms, int natoms)
289 {
290         int i;
291         if (mp == NULL)
292                 mp = MoleculeNew();
293         if (natoms == 0)
294                 return mp;
295         if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
296                 Panic("Cannot allocate memory for atoms");
297         for (i = 0; i < natoms; i++)
298                 AtomDuplicate(mp->atoms + i, atoms + i);
299         mp->nframes = -1;  /*  Should be recalculated later  */
300         return mp;
301 }
302
303 Molecule *
304 MoleculeInitWithMolecule(Molecule *mp2, const Molecule *mp)
305 {
306         MoleculeInitWithAtoms(mp2, mp->atoms, mp->natoms);
307         if (mp->nbonds > 0) {
308                 if (NewArray(&mp2->bonds, &mp2->nbonds, sizeof(Int)*2, mp->nbonds) == NULL)
309                         goto error;
310                 memmove(mp2->bonds, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
311         }
312         if (mp->nangles > 0) {
313                 if (NewArray(&mp2->angles, &mp2->nangles, sizeof(Int)*3, mp->nangles) == NULL)
314                         goto error;
315                 memmove(mp2->angles, mp->angles, sizeof(Int) * 3 * mp->nangles);
316         }
317         if (mp->ndihedrals > 0) {
318                 if (NewArray(&mp2->dihedrals, &mp2->ndihedrals, sizeof(Int)*4, mp->ndihedrals) == NULL)
319                         goto error;
320                 memmove(mp2->dihedrals, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
321         }
322         if (mp->nimpropers > 0) {
323                 if (NewArray(&mp2->impropers, &mp2->nimpropers, sizeof(Int)*4, mp->nimpropers) == NULL)
324                         goto error;
325                 memmove(mp2->impropers, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
326         }
327         if (mp->nresidues > 0) {
328                 if (NewArray(&mp2->residues, &mp2->nresidues, sizeof(mp->residues[0]), mp->nresidues) == NULL)
329                         goto error;
330                 memmove(mp2->residues, mp->residues, sizeof(mp->residues[0]) * mp->nresidues);
331         }
332         if (mp->cell != NULL) {
333                 mp2->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
334                 memmove(mp2->cell, mp->cell, sizeof(XtalCell));
335         }
336         if (mp->nsyms > 0) {
337                 NewArray(&(mp2->syms), &(mp2->nsyms), sizeof(Transform), mp->nsyms);
338                 memmove(mp2->syms, mp->syms, sizeof(Transform) * mp2->nsyms);
339         }
340         mp2->useFlexibleCell = mp->useFlexibleCell;
341         if (mp->nframe_cells > 0) {
342                 if (NewArray(&mp2->frame_cells, &mp2->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells) == NULL)
343                         goto error;
344                 memmove(mp2->frame_cells, mp->frame_cells, sizeof(Vector) * 4 * mp->nframe_cells);
345         }
346         
347         /* FIXME: should bset (basis set info) and elpot be duplicated or not?  */
348
349         if (mp->par != NULL)
350                 mp2->par = ParameterDuplicate(mp->par);
351         if (mp->arena != NULL) {
352                 md_arena_new(mp2);
353                 md_arena_init_from_arena(mp2->arena, mp->arena);
354         }
355         
356         return mp2;
357   error:
358         Panic("Cannot allocate memory for duplicate molecule");
359         return NULL;  /*  Not reached  */
360 }
361
362 /*  Assign a unique name to this parameter record  */
363 void
364 MoleculeSetName(Molecule *mp, const char *name)
365 {
366         ObjectSetName((Object *)mp, name, (Object *)sMoleculeRoot);
367 }
368
369 const char *
370 MoleculeGetName(Molecule *mp)
371 {
372         return ObjectGetName((Object *)mp);
373 }
374
375 Molecule *
376 MoleculeWithName(const char *name)
377 {
378         return (Molecule *)ObjectWithName(name, (Object *)sMoleculeRoot);
379 }
380
381 void
382 MoleculeSetPath(Molecule *mol, const char *fname)
383 {
384         char *buf, *cwd;
385         if (mol == NULL || fname == NULL)
386                 return;
387         if (fname[0] == '/' || (isalpha(fname[0]) && fname[1] == ':')) {
388                 /*  Full path  */
389                 buf = strdup(fname);
390         } else {
391                 cwd = getcwd(NULL, 0);
392                 asprintf(&buf, "%s/%s", cwd, fname);
393                 free(cwd);
394         }
395         if (mol->path != NULL) {
396                 if (strcmp(mol->path, buf) == 0) {
397                         /*  No change  */
398                         free(buf);
399                         return;
400                 }
401                 free((void *)(mol->path));
402         }
403         mol->path = buf;
404         if (mol->arena != NULL) {
405                 md_close_output_files(mol->arena);
406         }
407 }
408
409 const char *
410 MoleculeGetPath(Molecule *mol)
411 {
412         if (mol == NULL)
413                 return NULL;
414         return mol->path;
415 }
416
417 Molecule *
418 MoleculeRetain(Molecule *mp)
419 {
420         ObjectIncrRefCount((Object *)mp);
421         MoleculeRetainExternalObj(mp);
422         return mp;
423 }
424
425 void
426 MoleculeClear(Molecule *mp)
427 {
428         if (mp == NULL)
429                 return;
430         if (mp->arena != NULL) {
431                 md_arena_set_molecule(mp->arena, NULL);
432                 mp->arena = NULL;
433         }
434         if (mp->par != NULL) {
435                 ParameterRelease(mp->par);
436                 mp->par = NULL;
437         }
438         if (mp->bset != NULL) {
439                 BasisSetRelease(mp->bset);
440                 mp->bset = NULL;
441         }
442         if (mp->atoms != NULL) {
443                 int i;
444                 for (i = 0; i < mp->natoms; i++)
445                         AtomClean(mp->atoms + i);
446                 free(mp->atoms);
447                 mp->atoms = NULL;
448                 mp->natoms = 0;
449         }
450         if (mp->bonds != NULL) {
451                 free(mp->bonds);
452                 mp->bonds = NULL;
453                 mp->nbonds = 0;
454         }
455         if (mp->angles != NULL) {
456                 free(mp->angles);
457                 mp->angles = NULL;
458                 mp->nangles = 0;
459         }
460         if (mp->dihedrals != NULL) {
461                 free(mp->dihedrals);
462                 mp->dihedrals = NULL;
463                 mp->ndihedrals = 0;
464         }
465         if (mp->impropers != NULL) {
466                 free(mp->impropers);
467                 mp->impropers = NULL;
468                 mp->nimpropers = 0;
469         }
470         if (mp->residues != NULL) {
471                 free(mp->residues);
472                 mp->residues = NULL;
473                 mp->nresidues = 0;
474         }
475         if (mp->cell != NULL) {
476                 free(mp->cell);
477                 mp->cell = NULL;
478         }
479         if (mp->syms != NULL) {
480                 free(mp->syms);
481                 mp->syms = NULL;
482                 mp->nsyms = 0;
483         }
484         if (mp->selection != NULL) {
485                 IntGroupRelease(mp->selection);
486                 mp->selection = NULL;
487         }
488         if (mp->frame_cells != NULL) {
489                 free(mp->frame_cells);
490                 mp->frame_cells = NULL;
491                 mp->nframe_cells = 0;
492         }
493         if (mp->bset != NULL) {
494                 BasisSetRelease(mp->bset);
495                 mp->bset = NULL;
496         }
497         if (mp->par != NULL) {
498                 ParameterRelease(mp->par);
499                 mp->par = NULL;
500         }
501         if (mp->elpots != NULL) {
502                 free(mp->elpots);
503                 mp->elpots = NULL;
504                 mp->nelpots = 0;
505         }
506         if (mp->path != NULL) {
507                 free((void *)mp->path);
508                 mp->path = NULL;
509         }
510 }
511
512 void
513 MoleculeRelease(Molecule *mp)
514 {
515         if (mp == NULL)
516                 return;
517         MoleculeReleaseExternalObj(mp);
518         if (ObjectDecrRefCount((Object *)mp) == 0) {
519                 MoleculeClear(mp);
520                 ObjectDealloc((Object *)mp, (Object **)&sMoleculeRoot);
521         }
522 }
523
524 void
525 MoleculeExchange(Molecule *mp1, Molecule *mp2)
526 {
527         Molecule mp_temp;
528         struct MainView *mview1, *mview2;
529         struct MDArena *arena1, *arena2;
530         /*  mview and arena must be kept as they are  */
531         mview1 = mp1->mview;
532         mview2 = mp2->mview;
533         arena1 = mp1->arena;
534         arena2 = mp2->arena;
535         /*  'natoms' is the first member to be copied  */
536         int ofs = offsetof(Molecule, natoms);
537         memmove((char *)(&mp_temp) + ofs, (char *)mp1 + ofs, sizeof(Molecule) - ofs);
538         memmove((char *)mp1 + ofs, (char *)mp2 + ofs, sizeof(Molecule) - ofs);
539         memmove((char *)mp2 + ofs, (char *)(&mp_temp) + ofs, sizeof(Molecule) - ofs);
540         mp1->arena = arena1;
541         mp2->arena = arena2;
542         mp1->mview = mview1;
543         mp2->mview = mview2;
544 /*      if (mp1->arena != NULL && mp1->arena->mol == mp2)
545                 mp1->arena->mol = mp1;
546         if (mp1->arena != NULL && mp1->arena->xmol == mp2)
547                 mp1->arena->xmol = mp1;
548         if (mp2->arena != NULL && mp2->arena->mol == mp1)
549                 mp2->arena->mol = mp2;
550         if (mp2->arena != NULL && mp2->arena->xmol == mp1)
551                 mp2->arena->xmol = mp2; */
552 }
553
554 #pragma mark ====== Mutex ======
555
556 void
557 MoleculeLock(Molecule *mol)
558 {
559         if (mol == NULL || mol->mutex == NULL)
560                 return;
561         MoleculeCallback_lockMutex(mol->mutex);
562 }
563
564 void
565 MoleculeUnlock(Molecule *mol)
566 {
567         if (mol == NULL || mol->mutex == NULL)
568                 return;
569         MoleculeCallback_unlockMutex(mol->mutex);
570 }
571
572 #pragma mark ====== Modify count ======
573
574 void
575 MoleculeIncrementModifyCount(Molecule *mp)
576 {
577         if (mp != NULL) {
578                 if (++(mp->modifyCount) == 1)
579                         MoleculeCallback_notifyModification(mp, 0);
580         /*      fprintf(stderr, "MoleculeIncrementModifyCount: %d\n", mp->modifyCount); */
581         }
582 }
583
584 void
585 MoleculeClearModifyCount(Molecule *mp)
586 {
587         if (mp != NULL) {
588                 mp->modifyCount = 0;
589         /*      fprintf(stderr, "MoleculeClearModifyCount: %d\n", mp->modifyCount); */
590         }
591 }
592
593 #pragma mark ====== File handling functions ======
594
595 static const char *
596 guessMoleculeType(const char *fname)
597 {
598         char buf[1024], *p;
599         FILE *fp;
600         const char *retval = NULL;
601         fp = fopen(fname, "rb");
602         if (fp != NULL) {
603                 memset(buf, 0, sizeof buf);
604                 if (fread(buf, 1, sizeof buf - 1, fp) > 0) {
605                         if (strncmp(buf, "PSF", 3) == 0)
606                                 retval = "psf";
607                         else if (((p = strstr(buf, "ATOM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r'))
608                         || ((p = strstr(buf, "HETATM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r')))
609                                 retval = "pdb";
610                         else
611                                 retval = "???";  /*  unknown  */
612                 }
613                 fclose(fp);
614         }
615         return retval;
616 }
617
618 static int
619 guessElement(Atom *ap)
620 {
621         int atomicNumber = -1;
622         if (ap->atomicNumber > 0)
623                 atomicNumber = ap->atomicNumber;
624         else {
625                 atomicNumber = GuessAtomicNumber(ap->element, ap->weight);
626                 if (atomicNumber <= 0 && ap->aname[0] != 0)
627                         atomicNumber = GuessAtomicNumber(ap->aname, ap->weight);
628         }
629         if (atomicNumber >= 0) {
630                 ap->atomicNumber = atomicNumber;
631                 if (ap->weight <= 0)
632                         ap->weight = WeightForAtomicNumber(atomicNumber);
633                 if (ap->element[0] == 0)
634                         ElementToString(atomicNumber, ap->element);
635         }
636         return atomicNumber;
637 }
638
639 static int
640 sReadLineWithInterrupt(char *buf, int size, FILE *stream, int *lineNumber)
641 {
642         static int lastLineNumber = 0;
643         if (lineNumber != NULL) {
644                 if (*lineNumber == 0)
645                         lastLineNumber = 0;
646                 else if (*lineNumber >= lastLineNumber + 1000) {
647                         if (MyAppCallback_checkInterrupt() != 0)
648                                 return -1;  /*  User interrupt  */
649                         lastLineNumber = *lineNumber;
650                 }
651         }
652         return ReadLine(buf, size, stream, lineNumber);
653 }
654
655 int
656 MoleculeLoadFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
657 {
658         int retval;
659         if (ftype == NULL || *ftype == 0) {
660                 const char *cp;
661                 cp = strrchr(fname, '.');
662                 if (cp != NULL)
663                         ftype = cp + 1;
664                 else {
665                         cp = guessMoleculeType(fname);
666                         if (strcmp(cp, "???") != 0)
667                                 ftype = cp;
668                 }
669         }
670         if (strcasecmp(ftype, "psf") == 0) {
671                 retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
672         } else if (strcasecmp(ftype, "pdb") == 0) {
673                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
674         } else if (strcasecmp(ftype, "tep") == 0) {
675                 retval = MoleculeLoadTepFile(mp, fname, errbuf, errbufsize);
676         } else if (strcasecmp(ftype, "res") == 0 || strcasecmp(ftype, "ins") == 0) {
677                 retval = MoleculeLoadShelxFile(mp, fname, errbuf, errbufsize);
678         } else if (strcasecmp(ftype, "fchk") == 0 || strcasecmp(ftype, "fch") == 0) {
679                 retval = MoleculeLoadGaussianFchkFile(mp, fname, errbuf, errbufsize);
680         } else {
681                 snprintf(errbuf, errbufsize, "Unknown format %s", ftype);
682                 return 1;
683         }
684 /*      if (retval != 0) {
685                 retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
686         } */
687         if (retval == 0)
688                 MoleculeSetPath(mp, fname);
689         return retval;
690 }
691
692 int
693 MoleculeLoadMbsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
694 {
695         FILE *fp;
696         char buf[1024];
697         int i, j, k, n, err, fn, nframes;
698         int lineNumber;
699         int ibuf[12];
700         Int iibuf[4];
701         double dbuf[12];
702         int mview_ibuf[16];
703         float mview_fbuf[8];
704         char cbuf[12][8];
705         const char **pp;
706         char *bufp, *valp, *comp;
707         Int *ip;
708         Double *dp;
709         Vector v;
710         Atom *ap;
711         const int kUndefined = -10000000;
712         err = 0;
713         if (errbuf == NULL) {
714                 errbuf = buf;
715                 errbufsize = 1024;
716         }
717         errbuf[0] = 0;
718         if (mp->natoms != 0 || mp->par != NULL || mp->arena != NULL) {
719                 snprintf(errbuf, errbufsize, "The molecule must be empty");
720                 return 1;
721         }
722         fp = fopen(fname, "rb");
723         if (fp == NULL) {
724                 snprintf(errbuf, errbufsize, "Cannot open file");
725                 return 1;
726         }
727         for (i = 0; i < 8; i++)
728                 mview_fbuf[i] = kUndefined;
729         for (i = 0; i < 16; i++)
730                 mview_ibuf[i] = kUndefined;
731         /*      flockfile(fp); */
732         lineNumber = 0;
733         fn = 0;
734         nframes = 0;
735         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
736                 if (strncmp(buf, "!:", 2) != 0)
737                         continue;   /*  Skip until section header is found  */
738                 bufp = buf;
739                 strsep(&bufp, " \t\n");
740                 if (strcmp(buf, "!:atoms") == 0) {
741                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
742                                 if (buf[0] == '!')
743                                         continue;
744                                 if (buf[0] == '\n')
745                                         break;
746                                 /* idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge */
747                                 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) {
748                                         snprintf(errbuf, errbufsize, "line %d: coordinates cannot be read for atom %d", lineNumber, mp->natoms + 1);
749                                         goto exit;
750                                 }
751                                 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
752                                 strncpy(ap->segName, cbuf[0], 4);
753                                 ap->resSeq = ibuf[1];
754                                 strncpy(ap->resName, cbuf[1], 4);
755                                 strncpy(ap->aname, cbuf[2], 4);
756                                 ap->type = AtomTypeEncodeToUInt(cbuf[3]);
757                                 ap->charge = dbuf[0];
758                                 ap->weight = dbuf[1];
759                                 strncpy(ap->element, cbuf[4], 2);
760                                 ap->atomicNumber = ibuf[2];
761                                 ap->occupancy = dbuf[2];
762                                 ap->tempFactor = dbuf[3];
763                                 ap->intCharge = ibuf[3];
764                         }
765                         continue;
766                 } else if (strcmp(buf, "!:atoms_symop") == 0) {
767                         i = 0;
768                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
769                                 if (buf[0] == '!')
770                                         continue;
771                                 if (buf[0] == '\n')
772                                         break;
773                                 /* idx symop symbase */
774                                 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
775                                         snprintf(errbuf, errbufsize, "line %d: symmetry operations cannot be read for atom %d", lineNumber, i + 1);
776                                         goto exit;
777                                 }
778                                 if (i >= mp->natoms) {
779                                         snprintf(errbuf, errbufsize, "line %d: too many atomic symmetry info\n", lineNumber);
780                                         goto exit;
781                                 }
782                                 ap = ATOM_AT_INDEX(mp->atoms, i);
783                                 ap->symop.sym = ibuf[1] / 1000000;
784                                 ap->symop.dx = (ibuf[1] % 1000000) / 10000;
785                                 ap->symop.dy = (ibuf[1] % 10000) / 100;
786                                 ap->symop.dz = ibuf[1] % 100;
787                                 ap->symbase = ibuf[2];
788                                 i++;
789                         }
790                         continue;
791                 } else if (strcmp(buf, "!:atoms_fix") == 0) {
792                         i = 0;
793                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
794                                 if (buf[0] == '!')
795                                         continue;
796                                 if (buf[0] == '\n')
797                                         break;
798                                 /* idx fix_force fix_pos */
799                                 if (sscanf(buf, "%d %lf %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]) < 5) {
800                                         snprintf(errbuf, errbufsize, "line %d: fix atom info cannot be read for atom %d", lineNumber, i + 1);
801                                         goto exit;
802                                 }
803                                 if (i >= mp->natoms) {
804                                         snprintf(errbuf, errbufsize, "line %d: too many fix atom info\n", lineNumber);
805                                         goto exit;
806                                 }
807                                 ap = ATOM_AT_INDEX(mp->atoms, i);
808                                 ap->fix_force = dbuf[0];
809                                 ap->fix_pos.x = dbuf[1];
810                                 ap->fix_pos.y = dbuf[2];
811                                 ap->fix_pos.z = dbuf[3];
812                                 i++;
813                         }
814                         continue;
815                 } else if (strcmp(buf, "!:mm_exclude") == 0) {
816                         i = 0;
817                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
818                                 if (buf[0] == '!')
819                                         continue;
820                                 if (buf[0] == '\n')
821                                         break;
822                                 /* idx mm_exclude periodic_exclude */
823                                 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
824                                         snprintf(errbuf, errbufsize, "line %d: mm_exclude flags cannot be read for atom %d", lineNumber, i + 1);
825                                         goto exit;
826                                 }
827                                 if (i >= mp->natoms) {
828                                         snprintf(errbuf, errbufsize, "line %d: too many mm_exclude flags\n", lineNumber);
829                                         goto exit;
830                                 }
831                                 ap = ATOM_AT_INDEX(mp->atoms, i);
832                                 ap->mm_exclude = (ibuf[1] != 0);
833                                 ap->periodic_exclude = (ibuf[2] != 0);
834                                 i++;
835                         }
836                         continue;
837                 } else if (strcmp(buf, "!:positions") == 0) {
838                         i = 0;
839                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
840                                 if (buf[0] == '!')
841                                         continue;
842                                 if (buf[0] == '\n')
843                                         break;
844                                 /* idx x y z */
845                                 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) {
846                                         snprintf(errbuf, errbufsize, "line %d: atom position cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
847                                         goto exit;
848                                 }
849                                 if (j > 4 && nframes != 0) {
850                                         snprintf(errbuf, errbufsize, "line %d: atom position sigma can only be given for frame 0", lineNumber);
851                                         goto exit;
852                                 }
853                                 if (j > 4 && j != 7) {
854                                         snprintf(errbuf, errbufsize, "line %d: atom position sigma cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
855                                         goto exit;
856                                 }
857                                 if (i >= mp->natoms) {
858                                         snprintf(errbuf, errbufsize, "line %d: too many atom position records\n", lineNumber);
859                                         goto exit;
860                                 }
861                                 v.x = dbuf[0];
862                                 v.y = dbuf[1];
863                                 v.z = dbuf[2];
864                                 ap = ATOM_AT_INDEX(mp->atoms, i);
865                                 if (nframes > 0) {
866                                         AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes, &v);
867                                         if (nframes == 1)
868                                                 ap->frames[0] = ap->r;
869                                 }
870                                 ap->r = v;
871                                 if (j == 7) {
872                                         ap->sigma.x = dbuf[3];
873                                         ap->sigma.y = dbuf[4];
874                                         ap->sigma.z = dbuf[5];
875                                 }
876                                 i++;
877                         }
878                         nframes++;
879                         if (nframes >= 2) {
880                                 mp->nframes = nframes;
881                                 mp->cframe = nframes - 1;
882                         } else {
883                                 mp->nframes = mp->cframe = 0;
884                         }
885                         continue;
886                 } else if (strcmp(buf, "!:bonds") == 0) {
887                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
888                                 if (buf[0] == '!')
889                                         continue;
890                                 if (buf[0] == '\n')
891                                         break;
892                                 /* from1 to1 from2 to2 from3 to3 from4 to4 */ 
893                                 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]);
894                                 if (i < 2 || i % 2 != 0) {
895                                         snprintf(errbuf, errbufsize, "line %d: bad bond format", lineNumber);
896                                         goto exit;
897                                 }
898                                 for (j = 0; j < i; j += 2) {
899                                         iibuf[0] = ibuf[j];
900                                         iibuf[1] = ibuf[j + 1];
901                                         if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[0] == iibuf[1]) {
902                                                 snprintf(errbuf, errbufsize, "line %d: bad bond format", lineNumber);
903                                                 goto exit;
904                                         }
905                                         AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, iibuf);
906                                         for (k = 0; k < 2; k++) {
907                                                 const Int *cp;
908                                                 ap = ATOM_AT_INDEX(mp->atoms, iibuf[k]);
909                                                 cp = AtomConnectData(&ap->connect);
910                                                 for (n = 0; n < ap->connect.count; n++, cp++) {
911                                                         if (*cp == iibuf[1 - k])
912                                                                 break;
913                                                 }
914                                                 if (n >= ap->connect.count) {
915                                                         AtomConnectInsertEntry(&ap->connect, ap->connect.count, iibuf[1 - k]);
916                                                 }
917                                         }
918                                 }
919                         }
920                         continue;
921                 } else if (strcmp(buf, "!:angles") == 0) {
922                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
923                                 if (buf[0] == '!')
924                                         continue;
925                                 if (buf[0] == '\n')
926                                         break;
927                                 /* a1 b1 c1 a2 b2 c2 a3 b3 c3 */ 
928                                 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]);
929                                 if (i == 0 || i % 3 != 0) {
930                                         snprintf(errbuf, errbufsize, "line %d: bad angle format", lineNumber);
931                                         goto exit;
932                                 }
933                                 for (j = 0; j < i; j += 3) {
934                                         iibuf[0] = ibuf[j];
935                                         iibuf[1] = ibuf[j + 1];
936                                         iibuf[2] = ibuf[j + 2];
937                                         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]) {
938                                                 snprintf(errbuf, errbufsize, "line %d: bad angle format", lineNumber);
939                                                 goto exit;
940                                         }
941                                         AssignArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, mp->nangles, iibuf);
942                                 }
943                         }
944                         continue;
945                 } else if (strcmp(buf, "!:dihedrals") == 0) {
946                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
947                                 if (buf[0] == '!')
948                                         continue;
949                                 if (buf[0] == '\n')
950                                         break;
951                                 /* a1 b1 c1 d1 a2 b2 c2 d2 */ 
952                                 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]);
953                                 if (i == 0 || i % 4 != 0) {
954                                         snprintf(errbuf, errbufsize, "line %d: bad dihedral format", lineNumber);
955                                         goto exit;
956                                 }
957                                 for (j = 0; j < i; j += 4) {
958                                         iibuf[0] = ibuf[j];
959                                         iibuf[1] = ibuf[j + 1];
960                                         iibuf[2] = ibuf[j + 2];
961                                         iibuf[3] = ibuf[j + 3];
962                                         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]) {
963                                                 snprintf(errbuf, errbufsize, "line %d: bad dihedral format", lineNumber);
964                                                 goto exit;
965                                         }
966                                         AssignArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, mp->ndihedrals, iibuf);
967                                 }
968                         }
969                         continue;
970                 } else if (strcmp(buf, "!:impropers") == 0) {
971                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
972                                 if (buf[0] == '!')
973                                         continue;
974                                 if (buf[0] == '\n')
975                                         break;
976                                 /* a1 b1 c1 d1 a2 b2 c2 d2 */ 
977                                 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]);
978                                 if (i == 0 || i % 4 != 0) {
979                                         snprintf(errbuf, errbufsize, "line %d: bad improper format", lineNumber);
980                                         goto exit;
981                                 }
982                                 for (j = 0; j < i; j += 4) {
983                                         iibuf[0] = ibuf[j];
984                                         iibuf[1] = ibuf[j + 1];
985                                         iibuf[2] = ibuf[j + 2];
986                                         iibuf[3] = ibuf[j + 3];
987                                         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]) {
988                                                 snprintf(errbuf, errbufsize, "line %d: bad improper format", lineNumber);
989                                                 goto exit;
990                                         }
991                                         AssignArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, mp->nimpropers, iibuf);
992                                 }
993                         }
994                         continue;
995                 } else if (strcmp(buf, "!:xtalcell") == 0 && mp->cell == NULL) {
996                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
997                                 if (buf[0] == '!')
998                                         continue;
999                                 if (buf[0] == '\n')
1000                                         break;
1001                                 /* a b c alpha beta gamma [sigmaflag] */ 
1002                                 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) {
1003                                         snprintf(errbuf, errbufsize, "line %d: bad xtalcell format", lineNumber);
1004                                         goto exit;
1005                                 }
1006                                 MoleculeSetCell(mp, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], 0);
1007                                 if (j == 7 && ibuf[0] != 0) {
1008                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1009                                                 snprintf(errbuf, errbufsize, "line %d: sigma for xtalcell are missing", lineNumber);
1010                                                 goto exit;
1011                                         }
1012                                         if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1013                                                 snprintf(errbuf, errbufsize, "line %d: bad xtalcell sigma format", lineNumber);
1014                                                 goto exit;
1015                                         }
1016                                         if (mp->cell != NULL) {
1017                                                 mp->cell->has_sigma = 1;
1018                                                 for (i = 0; i < 6; i++) {
1019                                                         mp->cell->cellsigma[i] = dbuf[i];
1020                                                 }
1021                                         } else {
1022                                                 snprintf(errbuf, errbufsize, "line %d: cell sigma are given while cell is not given", lineNumber);
1023                                         }
1024                                 }
1025                         }
1026                         continue;
1027                 } else if (strcmp(buf, "!:symmetry_operations") == 0) {
1028                         i = 0;
1029                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1030                                 Transform tr;
1031                                 if (buf[0] == '!')
1032                                         continue;
1033                                 if (buf[0] == '\n')
1034                                         break;
1035                                 /* a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3 */
1036                                 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1037                                         snprintf(errbuf, errbufsize, "line %d: bad symmetry_operation format", lineNumber);
1038                                         goto exit;
1039                                 }
1040                                 if (i < 3) {
1041                                         tr[i] = dbuf[0];
1042                                         tr[i + 3] = dbuf[1];
1043                                         tr[i + 6] = dbuf[2];
1044                                 } else {
1045                                         tr[9] = dbuf[0];
1046                                         tr[10] = dbuf[1];
1047                                         tr[11] = dbuf[2];
1048                                 }
1049                                 i++;
1050                                 if (i == 4) {
1051                                         AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1052                                         i = 0;
1053                                 }
1054                         }
1055                         continue;
1056                 } else if (strcmp(buf, "!:anisotropic_thermal_parameters") == 0) {
1057                         i = 0;
1058                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1059                                 if (buf[0] == '!')
1060                                         continue;
1061                                 if (buf[0] == '\n')
1062                                         break;
1063                                 /* b11 b22 b33 b12 b13 b23 [has_sigma] */
1064                                 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) {
1065                                         snprintf(errbuf, errbufsize, "line %d: anisotropic thermal parameters cannot be read for atom %d", lineNumber, i + 1);
1066                                         goto exit;
1067                                 }
1068                                 if (i >= mp->natoms) {
1069                                         snprintf(errbuf, errbufsize, "line %d: too many anisotropic thermal parameters\n", lineNumber);
1070                                         goto exit;
1071                                 }
1072                                 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) {
1073                                         /*  Skip it  */
1074                                 } else {
1075                                         MoleculeSetAniso(mp, i, 0, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], NULL);
1076                                 }
1077                                 if (j == 7 && ibuf[0] != 0) {
1078                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1079                                                 snprintf(errbuf, errbufsize, "line %d: anisotropic thermal parameters sigma missing", lineNumber);
1080                                                 goto exit;
1081                                         }
1082                                         if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1083                                                 snprintf(errbuf, errbufsize, "line %d: anisotropic thermal parameters sigma cannot be read for atom %d", lineNumber, i + 1);
1084                                                 goto exit;
1085                                         }
1086                                         ap = ATOM_AT_INDEX(mp->atoms, i);
1087                                         if (ap->aniso == NULL) {
1088                                                 snprintf(errbuf, errbufsize, "line %d: anisotropic thermal parameters sigma are given while the parameters are not given", lineNumber);
1089                                                 goto exit;
1090                                         }
1091                                         ap->aniso->has_bsig = 1;
1092                                         for (j = 0; j < 6; j++)
1093                                                 ap->aniso->bsig[j] = dbuf[j];
1094                                 }
1095                                 i++;
1096                         }
1097                         continue;
1098                 } else if (strcmp(buf, "!:periodic_box") == 0) {
1099                         Vector vs[5];
1100                         Byte has_sigma = 0;
1101                         i = 0;
1102                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1103                                 if (buf[0] == '!')
1104                                         continue;
1105                                 if (buf[0] == '\n')
1106                                         break;
1107                                 /* 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] */
1108                                 if (i < 4) {
1109                                         if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1110                                                 snprintf(errbuf, errbufsize, "line %d: bad periodic_box format", lineNumber);
1111                                                 goto exit;
1112                                         }
1113                                         vs[i].x = dbuf[0];
1114                                         vs[i].y = dbuf[1];
1115                                         vs[i].z = dbuf[2];
1116                                         i++;
1117                                         continue;
1118                                 }
1119                                 if ((j = sscanf(buf, "%d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3])) < 3) {
1120                                         snprintf(errbuf, errbufsize, "line %d: bad periodic_box format", lineNumber);
1121                                         goto exit;
1122                                 }
1123                                 if (j == 4 && ibuf[3] != 0)
1124                                         has_sigma = 1;
1125                                 cbuf[0][0] = ibuf[0];
1126                                 cbuf[0][1] = ibuf[1];
1127                                 cbuf[0][2] = ibuf[2];
1128                                 MoleculeSetPeriodicBox(mp, vs, vs + 1, vs + 2, vs + 3, cbuf[0], 0);
1129                                 if (has_sigma) {
1130                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1131                                                 snprintf(errbuf, errbufsize, "line %d: sigma for cell parameters are missing", lineNumber);
1132                                                 goto exit;
1133                                         }
1134                                         if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1135                                                 snprintf(errbuf, errbufsize, "line %d: bad periodic_box sigma format", lineNumber);
1136                                                 goto exit;
1137                                         }
1138                                         if (mp->cell != NULL) {
1139                                                 mp->cell->has_sigma = 1;
1140                                                 for (i = 0; i < 6; i++) {
1141                                                         mp->cell->cellsigma[i] = dbuf[i];
1142                                                 }
1143                                         } else {
1144                                                 snprintf(errbuf, errbufsize, "line %d: cell sigma are given while cell is not given", lineNumber);
1145                                         }
1146                                 }
1147                                 break;
1148                         }
1149                         continue;
1150                 } else if (strcmp(buf, "!:frame_periodic_boxes") == 0) {
1151                         Vector vs[5];
1152                         i = 0;
1153                         mp->useFlexibleCell = 1;  /*  The presence of this block causes asserting this flag  */
1154                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1155                                 if (buf[0] == '!')
1156                                         continue;
1157                                 if (buf[0] == '\n')
1158                                         break;
1159                                 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1160                                         snprintf(errbuf, errbufsize, "line %d: bad frame_periodic_box format", lineNumber);
1161                                         goto exit;
1162                                 }
1163                                 vs[i].x = dbuf[0];
1164                                 vs[i].y = dbuf[1];
1165                                 vs[i].z = dbuf[2];
1166                                 i++;
1167                                 if (i == 4) {
1168                                         AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells, vs);
1169                                         i = 0;
1170                                 }
1171                         }
1172                         if (mp->cframe < mp->nframe_cells) {
1173                                 /*  mp->cframe should already have been set when positions are read  */
1174                                 Vector *vp = &mp->frame_cells[mp->cframe * 4];
1175                                 static char defaultFlags[] = {1, 1, 1};
1176                                 char *flags = (mp->cell != NULL ? mp->cell->flags : defaultFlags);
1177                                 MoleculeSetPeriodicBox(mp, vp, vp + 1, vp + 2, vp + 3, flags, 0);
1178                         }
1179                         continue;
1180                 } else if (strcmp(buf, "!:md_parameters") == 0) {
1181                         MDArena *arena;
1182                         if (mp->arena == NULL)
1183                                 mp->arena = md_arena_new(NULL);
1184                         arena = mp->arena;
1185                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1186                                 if (buf[0] == '!')
1187                                         continue;
1188                                 if (buf[0] == '\n')
1189                                         break;
1190                                 bufp = buf;
1191                                 comp = strsep(&bufp, " \t");
1192                                 if (bufp != NULL) {
1193                                         while (*bufp == ' ' || *bufp == '\t')
1194                                                 bufp++;
1195                                         valp = strsep(&bufp, "\n");
1196                                 } else valp = NULL;
1197                                 if (strcmp(comp, "alchem_flags") == 0) {
1198                                         j = (valp == NULL ? 0 : atoi(valp));
1199                                         if (j > 0) {
1200                                                 valp = (char *)malloc(j);
1201                                                 i = 0;
1202                                                 while ((k = fgetc(fp)) >= 0) {
1203                                                         ungetc(k, fp);
1204                                                         if (k < '0' || k > '9') {
1205                                                                 snprintf(errbuf, errbufsize, "line %d: too few flags in alchem_flags block", lineNumber + 1);
1206                                                                 free(valp);
1207                                                                 goto exit;
1208                                                         }
1209                                                         ReadLine(buf, sizeof buf, fp, &lineNumber);
1210                                                         bufp = buf;
1211                                                         while (*bufp != 0) {
1212                                                                 if (*bufp >= '0' && *bufp <= '2') {
1213                                                                         if (i >= j) {
1214                                                                                 snprintf(errbuf, errbufsize, "line %d: too many flags in alchem_flags block", lineNumber);
1215                                                                                 free(valp);
1216                                                                                 goto exit;
1217                                                                         }
1218                                                                         valp[i++] = *bufp - '0';
1219                                                                 } else if (*bufp != ' ' && *bufp != '\t' && *bufp != '\n') {
1220                                                                         snprintf(errbuf, errbufsize, "line %d: strange character (0x%02x) in alchem_flags block", lineNumber, (int)*bufp);
1221                                                                         free(valp);
1222                                                                         goto exit;
1223                                                                 }
1224                                                                 bufp++;
1225                                                         }
1226                                                         if (i == j)
1227                                                                 break;
1228                                                 }
1229                                                 md_set_alchemical_flags(arena, j, valp);
1230                                                 free(valp);
1231                                         }
1232                                         continue;
1233                                 }
1234                                 /*  In the following, the redundant "!= NULL" is to suppress suprious warning  */
1235                                 if ((strcmp(comp, "log_file") == 0 && (pp = &arena->log_result_name) != NULL)
1236                                         || (strcmp(comp, "coord_file") == 0 && (pp = &arena->coord_result_name) != NULL)
1237                                         || (strcmp(comp, "vel_file") == 0 && (pp = &arena->vel_result_name) != NULL)
1238                                         || (strcmp(comp, "force_file") == 0 && (pp = &arena->force_result_name) != NULL)
1239                                         || (strcmp(comp, "debug_file") == 0 && (pp = &arena->debug_result_name) != NULL)) {
1240                                         if (*valp == 0 || strstr(valp, "(null)") == valp)
1241                                                 *pp = NULL;
1242                                         else {
1243                                                 valp = strdup(valp);
1244                                                 if (valp != NULL) {
1245                                                         char *valp1 = strchr(valp, '\n');
1246                                                         if (valp1 != NULL)
1247                                                                 *valp1 = 0;
1248                                                 }
1249                                                 *pp = valp;
1250                                         }
1251                                 } else if ((strcmp(comp, "debug_output_level") == 0 && (ip = &arena->debug_output_level) != NULL)
1252                                                    || (strcmp(comp, "coord_output_freq") == 0 && (ip = &arena->coord_output_freq) != NULL)
1253                                                    || (strcmp(comp, "energy_output_freq") == 0 && (ip = &arena->energy_output_freq) != NULL)
1254                                                    || (strcmp(comp, "coord_frame") == 0 && (ip = &arena->coord_result_frame) != NULL)
1255                                                    || (strcmp(comp, "andersen_freq") == 0 && (ip = &arena->andersen_thermo_freq) != NULL)
1256                                                    || (strcmp(comp, "random_seed") == 0 && (ip = &arena->random_seed) != NULL)
1257                                                    || (strcmp(comp, "use_xplor_shift") == 0 && (ip = &arena->use_xplor_shift) != NULL)
1258                                                    || (strcmp(comp, "relocate_center") == 0 && (ip = &arena->relocate_center) != NULL)
1259                                                    || (strcmp(comp, "surface_potential_freq") == 0 && (ip = &arena->surface_potential_freq) != NULL)
1260                                                    || (strcmp(comp, "use_graphite") == 0 && (ip = &arena->use_graphite) != NULL)) {
1261                                         *ip = (valp == NULL ? 0 : atoi(valp));
1262                                 } else if ((strcmp(comp, "timestep") == 0 && (dp = &arena->timestep) != NULL)
1263                                                    || (strcmp(comp, "cutoff") == 0 && (dp = &arena->cutoff) != NULL)
1264                                                    || (strcmp(comp, "electro_cutoff") == 0 && (dp = &arena->electro_cutoff) != NULL)
1265                                                    || (strcmp(comp, "pairlist_distance") == 0 && (dp = &arena->pairlist_distance) != NULL)
1266                                                    || (strcmp(comp, "temperature") == 0 && (dp = &arena->temperature) != NULL)
1267                                                    || (strcmp(comp, "andersen_coupling") == 0 && (dp = &arena->andersen_thermo_coupling) != NULL)
1268                                                    || (strcmp(comp, "dielectric") == 0 && (dp = &arena->dielectric) != NULL)
1269                                                    || (strcmp(comp, "gradient_convergence") == 0 && (dp = &arena->gradient_convergence) != NULL)
1270                                                    || (strcmp(comp, "coordinate_convergence") == 0 && (dp = &arena->coordinate_convergence) != NULL)
1271                                                    || (strcmp(comp, "scale14_vdw") == 0 && (dp = &arena->scale14_vdw) != NULL)
1272                                                    || (strcmp(comp, "scale14_elect") == 0 && (dp = &arena->scale14_elect) != NULL)
1273                                                    || (strcmp(comp, "surface_probe_radius") == 0 && (dp = &arena->probe_radius) != NULL)
1274                                                    || (strcmp(comp, "surface_tension") == 0 && (dp = &arena->surface_tension) != NULL)
1275                                                    || (strcmp(comp, "alchemical_lambda") == 0 && (dp = &arena->alchem_lambda) != NULL)
1276                                                    || (strcmp(comp, "alchemical_delta_lambda") == 0 && (dp = &arena->alchem_dlambda) != NULL)) {
1277                                         *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1278                                 }
1279                         }
1280                         continue;
1281                 } else if (strcmp(buf, "!:pressure_control_parameters") == 0) {
1282                         MDPressureArena *pressure;
1283                         if (mp->arena == NULL)
1284                                 mp->arena = md_arena_new(mp);
1285                         if (mp->arena->pressure == NULL)
1286                                 mp->arena->pressure = pressure_new();
1287                         pressure = mp->arena->pressure;
1288                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1289                                 if (buf[0] == '!')
1290                                         continue;
1291                                 if (buf[0] == '\n')
1292                                         break;
1293                                 bufp = buf;
1294                                 comp = strsep(&bufp, " \t");
1295                                 if (bufp != NULL) {
1296                                         while (*bufp == ' ' || *bufp == '\t')
1297                                                 bufp++;
1298                                         valp = strsep(&bufp, "\n");
1299                                 } else valp = NULL;
1300                                 if (strcmp(comp, "pressure") == 0) {
1301                                         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) {
1302                                                 snprintf(errbuf, errbufsize, "line %d: bad format", lineNumber);
1303                                                 goto exit;
1304                                         }
1305                                         for (i = 0; i < 9; i++)
1306                                                 pressure->apply[i] = dbuf[i];
1307                                 } else if (strcmp(comp, "pressure_cell_flexibility") == 0) {
1308                                         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) {
1309                                                 snprintf(errbuf, errbufsize, "line %d: bad format", lineNumber);
1310                                                 goto exit;
1311                                         }
1312                                         for (i = 0; i < 8; i++)
1313                                                 pressure->cell_flexibility[i] = dbuf[i];
1314                                 } else if ((strcmp(comp, "pressure_freq") == 0 && (ip = &pressure->freq) != NULL)) {
1315                                         *ip = (valp == NULL ? 0 : atoi(valp));
1316                                 } else if ((strcmp(comp, "pressure_coupling") == 0 && (dp = &pressure->coupling) != NULL)
1317                                                    || (strcmp(comp, "pressure_fluctuate_cell_origin") == 0 && (dp = &pressure->fluctuate_cell_origin) != NULL)
1318                                                    || (strcmp(comp, "pressure_fluctuate_cell_orientation") == 0 && (dp = &pressure->fluctuate_cell_orientation) != NULL)) {
1319                                         *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1320                                 }
1321                         }
1322                         continue;
1323                 } else if (strcmp(buf, "!:velocity") == 0) {
1324                         i = 0;
1325                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1326                                 if (buf[0] == '!')
1327                                         continue;
1328                                 if (buf[0] == '\n')
1329                                         break;
1330                                 /* idx vx vy vz */
1331                                 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1332                                         snprintf(errbuf, errbufsize, "line %d: atom velocity cannot be read for atom %d", lineNumber, i + 1);
1333                                         goto exit;
1334                                 }
1335                                 if (i >= mp->natoms) {
1336                                         snprintf(errbuf, errbufsize, "line %d: too many atom velocity records\n", lineNumber);
1337                                         goto exit;
1338                                 }
1339                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1340                                 ap->v.x = dbuf[0];
1341                                 ap->v.y = dbuf[1];
1342                                 ap->v.z = dbuf[2];
1343                                 i++;
1344                         }
1345                         continue;
1346                 } else if (strcmp(buf, "!:force") == 0) {
1347                         i = 0;
1348                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1349                                 if (buf[0] == '!')
1350                                         continue;
1351                                 if (buf[0] == '\n')
1352                                         break;
1353                                 /* idx fx fy fz */
1354                                 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1355                                         snprintf(errbuf, errbufsize, "line %d: atom force cannot be read for atom %d", lineNumber, i + 1);
1356                                         goto exit;
1357                                 }
1358                                 if (i >= mp->natoms) {
1359                                         snprintf(errbuf, errbufsize, "line %d: too many atom force records\n", lineNumber);
1360                                         goto exit;
1361                                 }
1362                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1363                                 ap->f.x = dbuf[0];
1364                                 ap->f.y = dbuf[1];
1365                                 ap->f.z = dbuf[2];
1366                                 i++;
1367                         }
1368                         continue;
1369                 } else if (strcmp(buf, "!:parameter") == 0 || strcmp(buf, "!:parameters") == 0) {
1370                         Parameter *par = mp->par;
1371                         if (par == NULL) {
1372                                 mp->par = ParameterNew();
1373                                 par = mp->par;
1374                         }
1375                         bufp = NULL;
1376                         i = 0;
1377                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1378                                 if (buf[0] == '!')
1379                                         continue;
1380                                 if (buf[0] == '\n')
1381                                         break;
1382                                 j = ParameterReadFromString(par, buf, &bufp, fname, lineNumber, 0);
1383                                 if (j < 0) {
1384                                         snprintf(errbuf, errbufsize, "%s", bufp);
1385                                         goto exit;
1386                                 }
1387                                 i += j;
1388                         }
1389                         if (bufp != NULL) {
1390                                 MyAppCallback_setConsoleColor(1);
1391                                 MyAppCallback_showScriptMessage("%s", bufp);
1392                                 MyAppCallback_setConsoleColor(0);
1393                                 free(bufp);
1394                         }
1395                         continue;
1396                 } else if (strcmp(buf, "!:trackball") == 0) {
1397                         i = 0;
1398                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1399                                 if (buf[0] == '!')
1400                                         continue;
1401                                 if (buf[0] == '\n')
1402                                         break;
1403                                 /* scale; trx try trz; theta_deg x y z */
1404                                 if ((i == 0 && sscanf(buf, "%f", &mview_fbuf[0]) < 1)
1405                                         || (i == 1 && sscanf(buf, "%f %f %f",
1406                                                                                  &mview_fbuf[1], &mview_fbuf[2], &mview_fbuf[3]) < 3)
1407                                         || (i == 2 && sscanf(buf, "%f %f %f %f",
1408                                                                                  &mview_fbuf[4], &mview_fbuf[5], &mview_fbuf[6], &mview_fbuf[7]) < 4)) {
1409                                         snprintf(errbuf, errbufsize, "line %d: bad trackball format", lineNumber);
1410                                         goto exit;
1411                                 }
1412                                 i++;
1413                         }
1414                         continue;
1415                 } else if (strcmp(buf, "!:view") == 0) {
1416                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1417                                 if (buf[0] == '!')
1418                                         continue;
1419                                 if (buf[0] == '\n')
1420                                         break;
1421                                 bufp = buf;
1422                                 comp = strsep(&bufp, " \t");
1423                                 if (bufp != NULL) {
1424                                         while (*bufp == ' ' || *bufp == '\t')
1425                                                 bufp++;
1426                                         valp = strsep(&bufp, "\n");
1427                                 } else valp = NULL;
1428                                 /*  In the following, the redundant "!= NULL" is to suppress suprious warning  */
1429                                 if ((strcmp(comp, "show_unit_cell") == 0 && (i = 1))
1430                                         || (strcmp(comp, "show_periodic_box") == 0 && (i = 2))
1431                                         || (strcmp(comp, "show_expanded_atoms") == 0 && (i = 3))
1432                                         || (strcmp(comp, "show_ellipsoids") == 0 && (i = 4))
1433                                         || (strcmp(comp, "show_hydrogens") == 0 && (i = 5))
1434                                         || (strcmp(comp, "show_dummy_atoms") == 0 && (i = 6))
1435                                         || (strcmp(comp, "show_rotation_center") == 0 && (i = 7))
1436                                         || (strcmp(comp, "show_graphite_flag") == 0 && (i = 8))
1437                                         || (strcmp(comp, "show_periodic_image_flag") == 0 && (i = 9))
1438                                         || (strcmp(comp, "show_graphite") == 0 && (i = 10))) {
1439                                         mview_ibuf[i - 1] = atoi(valp);
1440                                 } else if (strcmp(comp, "show_periodic_image") == 0) {
1441                                         sscanf(valp, "%d %d %d %d %d %d",
1442                                                    &mview_ibuf[10], &mview_ibuf[11], &mview_ibuf[12],
1443                                                    &mview_ibuf[13], &mview_ibuf[14], &mview_ibuf[15]);
1444                                 }
1445                         }
1446                         continue;
1447                 }
1448                 /*  Unknown sections are silently ignored  */
1449         }
1450
1451         MoleculeCleanUpResidueTable(mp);
1452         if (mp->arena != NULL)
1453                 md_arena_set_molecule(mp->arena, mp);
1454
1455 exit:
1456         fclose(fp);
1457         if (errbuf[0] != 0) {
1458                 /*  The content of mp may be broken, so make it empty  */
1459                 MoleculeClear(mp);
1460                 return -1;
1461         } else {
1462                 MainView *mview = mp->mview;
1463                 if (mview != NULL) {
1464                         if (mview_ibuf[0] != kUndefined)
1465                                 mview->showUnitCell = mview_ibuf[0];
1466                         if (mview_ibuf[1] != kUndefined)
1467                                 mview->showPeriodicBox = mview_ibuf[1];
1468                         if (mview_ibuf[2] != kUndefined)
1469                                 mview->showExpandedAtoms = mview_ibuf[2];
1470                         if (mview_ibuf[3] != kUndefined)
1471                                 mview->showEllipsoids = mview_ibuf[3];
1472                         if (mview_ibuf[4] != kUndefined)
1473                                 mview->showHydrogens = mview_ibuf[4];
1474                         if (mview_ibuf[5] != kUndefined)
1475                                 mview->showDummyAtoms = mview_ibuf[5];
1476                         if (mview_ibuf[6] != kUndefined)
1477                                 mview->showRotationCenter = mview_ibuf[6];
1478                         if (mview_ibuf[7] != kUndefined)
1479                                 mview->showGraphiteFlag = mview_ibuf[7];
1480                         if (mview_ibuf[8] != kUndefined)
1481                                 mview->showPeriodicImageFlag = mview_ibuf[8];
1482                         if (mview_ibuf[9] != kUndefined)
1483                                 mview->showGraphite = mview_ibuf[9];
1484                         for (i = 0; i < 6; i++) {
1485                                 if (mview_ibuf[10 + i] != kUndefined)
1486                                         mview->showPeriodicImage[i] = mview_ibuf[10 + i];
1487                         }
1488                         if (mview->track != NULL) {
1489                                 if (mview_fbuf[0] != kUndefined)
1490                                         TrackballSetScale(mview->track, mview_fbuf[0]);
1491                                 if (mview_fbuf[1] != kUndefined)
1492                                         TrackballSetTranslate(mview->track, mview_fbuf + 1);
1493                                 if (mview_fbuf[4] != kUndefined)
1494                                         TrackballSetRotate(mview->track, mview_fbuf + 4);
1495                         }
1496                 }
1497         }
1498         return 0;
1499 }
1500
1501 int
1502 MoleculeLoadPsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
1503 {
1504         FILE *fp;
1505         char buf[1024];
1506         char *p;
1507         int section = -1;
1508         int i, j, err, fn;
1509         int lineNumber;
1510         Int ibuf[12];
1511         Vector *frames = NULL;
1512         Atom *ap;
1513         err = 0;
1514         if (errbuf == NULL) {
1515                 errbuf = buf;
1516                 errbufsize = 1024;
1517         }
1518         errbuf[0] = 0;
1519         if (mp == NULL)
1520                 mp = MoleculeNew();
1521         fp = fopen(fname, "rb");
1522         if (fp == NULL) {
1523                 snprintf(errbuf, errbufsize, "Cannot open file");
1524                 return 1;
1525         }
1526 /*      flockfile(fp); */
1527         lineNumber = 0;
1528         fn = 0;
1529         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1530                 if (strncmp(buf, "PSF", 3) == 0) {
1531                         section = 0;
1532                         continue;
1533                 } else {
1534                         for (p = buf; *p != 0 && isspace(*p); p++) {}
1535                         if (*p == 0) {
1536                                 section++;
1537                                 continue;
1538                         }
1539                 }
1540                 if (strstr(buf, "!COORD") != NULL) {
1541                         /*  Extended psf file with coordinates  */
1542                         if (fn > 0) {
1543                                 /*  Allocate a temporary storage for frames  */
1544                                 size_t size = sizeof(Vector) * mp->natoms * fn;
1545                                 if (frames == NULL)
1546                                         frames = (Vector *)malloc(size);
1547                                 else
1548                                         frames = (Vector *)realloc(frames, size);
1549                                 if (frames == NULL)
1550                                         goto panic;
1551                         #if 0
1552                                 if (fn == 1) {
1553                                         /*  Copy the coordinates of the first frame  */
1554                                         for (i = 0; i < mp->natoms; i++) {
1555                                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1556                                                 frames[i] = ap->r;
1557                                         }
1558                                 }
1559                                 /*  Copy the coordinates of the last frame to the newly created frame  */
1560                                 memmove(frames + sizeof(Vector) * mp->natoms * fn, frames + sizeof(Vector) * mp->natoms * (fn - 1), sizeof(Vector) * mp->natoms);
1561                         #endif
1562                         }
1563                         /*  Read coordinates  */
1564                         for (i = 0; i < mp->natoms; i++) {
1565                                 double dval[3];
1566                                 Vector r;
1567                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1568                                         err = 1;
1569                                         snprintf(errbuf, errbufsize, "line %d: premature end of file while reading coordinates (frame %d)", lineNumber, fn);
1570                                         goto exit;
1571                                 }
1572                                 if (sscanf(buf, "%lg %lg %lg", dval, dval + 1, dval + 2) != 3) {
1573                                         err = 1;
1574                                         snprintf(errbuf, errbufsize, "line %d: coordinates cannot be read for atom %d", lineNumber, i + 1);
1575                                         goto exit;
1576                                 }
1577                                 r.x = dval[0];
1578                                 r.y = dval[1];
1579                                 r.z = dval[2];
1580                                 if (fn == 0)
1581                                         ATOM_AT_INDEX(mp->atoms, i)->r = r;
1582                                 else
1583                                         frames[mp->natoms * (fn - 1) + i] = r;
1584                         }
1585                         fn++;
1586                         continue;
1587                 }
1588                 
1589                 if (section == 2) {
1590                         /*  Atoms  */
1591                         Int natoms;
1592                         ReadFormat(buf, "I8", &natoms);
1593                         if (mp->atoms != NULL)
1594                                 free(mp->atoms);
1595                         if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
1596                                 goto panic;
1597                         mp->nresidues = 0;
1598                         for (i = 0; i < natoms; i++) {
1599                                 struct {
1600                                         char segName[5], resName[4], atomName[5], atomType[3], element[3];
1601                                         Int serial;
1602                                 } w;
1603                                 memset(&w, 0, sizeof(w));
1604                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1605                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1606                                         err = 1;
1607                                         snprintf(errbuf, errbufsize, "line %d: premature end of file while reading atoms", lineNumber);
1608                                         goto exit;
1609                                 }
1610                                 ReadFormat(buf, "I8 x1 S4 I5 x1 S3 x2 S4 x1 S4 F16 F10",
1611                                         &w.serial, w.segName, &ap->resSeq, w.resName, w.atomName, 
1612                                         w.atomType, &ap->charge, &ap->weight);
1613                                 strncpy(ap->segName, w.segName, 4);
1614                                 strncpy(ap->resName, w.resName, 3);
1615                                 strncpy(ap->aname, w.atomName, 4);
1616                                 ap->type = AtomTypeEncodeToUInt(w.atomType);
1617                                 /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
1618                                 ap->atomicNumber = GuessAtomicNumber(w.atomName, ap->weight);
1619                                 ElementToString(ap->atomicNumber, w.element);
1620                                 strncpy(ap->element, w.element, 2);
1621                         /*      w.element[0] = 0;
1622                                 for (p = w.atomName; *p != 0; p++) {
1623                                         if (isalpha(*p) && *p != '_') {
1624                                                 w.element[0] = toupper(*p);
1625                                                 if (isalpha(p[1]) && p[1] != '_') {
1626                                                         w.element[1] = toupper(p[1]);
1627                                                         w.element[2] = 0;
1628                                                 } else {
1629                                                         w.element[1] = 0;
1630                                                 }
1631                                                 break;
1632                                         }
1633                                 }
1634                                 strncpy(ap->element, w.element, 2);
1635                                 ap->atomicNumber = ElementToInt(w.element); */
1636                                 if (w.resName[0] == 0)
1637                                         strncpy(ap->resName, "XXX", 3);
1638                                 if (ap->resSeq > mp->nresidues)
1639                                         mp->nresidues = ap->resSeq;
1640                         }
1641                         if (mp->residues != NULL)
1642                                 free(mp->residues);
1643                         if (NewArray(&mp->residues, &mp->nresidues, sizeof(char (*)[4]), mp->nresidues + 1) == 0)
1644                                 goto panic;
1645                         for (i = 0; i < mp->natoms; i++) {
1646                                 j = mp->atoms[i].resSeq;
1647                                 if (mp->residues[j][0] == 0)
1648                                         strncpy(mp->residues[j], mp->atoms[i].resName, 4);
1649                         }
1650                         continue;
1651                 } else if (section == 3) {
1652                         /*  Bonds  */
1653                         Int nbonds;
1654                         Int *bp;
1655                         ReadFormat(buf, "I8", &nbonds);
1656                         if (mp->bonds != NULL)
1657                                 free(mp->bonds);
1658                         if (NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, nbonds) == NULL)
1659                                 goto panic;
1660                         bp = mp->bonds;
1661                         for (i = 0; i < nbonds; i += 4) {
1662                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1663                                         snprintf(errbuf, errbufsize, "line %d: premature end of file while reading bonds", lineNumber);
1664                                         err = 1;
1665                                         goto exit;
1666                                 }
1667                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1668                                         ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1669                                 for (j = 0; j < 4 && i + j < nbonds; j++) {
1670                                         Int b1, b2;
1671                                         Atom *ap;
1672                                         b1 = ibuf[j * 2] - 1;    /* Internal atom number is 0-based */
1673                                         b2 = ibuf[j * 2 + 1] - 1;
1674                                         if (b1 < 0 || b1 >= mp->natoms || b2 < 0 || b2 >= mp->natoms) {
1675                                                 snprintf(errbuf, errbufsize, "line %d: The bond %d-%d includes non-existent atom", lineNumber, b1+1, b2+1);
1676                                                 err = 1;
1677                                                 goto exit;
1678                                         }
1679                                         *bp++ = b1;
1680                                         *bp++ = b2;
1681                                         ap = ATOM_AT_INDEX(mp->atoms, b1);
1682                                         AtomConnectInsertEntry(&ap->connect, ap->connect.count, b2);
1683                                         ap = ATOM_AT_INDEX(mp->atoms, b2);
1684                                         AtomConnectInsertEntry(&ap->connect, ap->connect.count, b1);
1685                                 }
1686                         }
1687                         continue;
1688                 } else if (section == 4) {
1689                         /*  Angles  */
1690                         Int nangles;
1691                         Int *gp;
1692                         ReadFormat(buf, "I8", &nangles);
1693                         if (mp->angles != NULL)
1694                                 free(mp->angles);
1695                         if (NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, nangles) == NULL)
1696                                 goto panic;
1697                         gp = mp->angles;
1698                         for (i = 0; i < nangles; i += 3) {
1699                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1700                                         snprintf(errbuf, errbufsize, "line %d: premature end of file while reading angles", lineNumber);
1701                                         err = 1;
1702                                         goto exit;
1703                                 }
1704                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1705                                         ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7, ibuf + 8);
1706                                 for (j = 0; j < 3 && i + j < nangles; j++) {
1707                                         Int a1, a2, a3;
1708                                         a1 = ibuf[j * 3] - 1;   /* Internal atom number is 0-based */
1709                                         a2 = ibuf[j * 3 + 1] - 1;
1710                                         a3 = ibuf[j * 3 + 2] - 1;
1711                                         if (a1 < 0 || a1 >= mp->natoms || a2 < 0 || a2 >= mp->natoms || a3 < 0 || a3 >= mp->natoms) {
1712                                                 snprintf(errbuf, errbufsize, "line %d: The angle %d-%d-%d includes non-existent atom", lineNumber, a1+1, a2+1, a3+1);
1713                                                 err = 1;
1714                                                 goto exit;
1715                                         }
1716                                         *gp++ = a1;
1717                                         *gp++ = a2;
1718                                         *gp++ = a3;
1719                                 }
1720                         }
1721                         continue;
1722                 } else if (section == 5 || section == 6) {
1723                         /*  Dihedrals and Impropers  */
1724                         Int ndihedrals;
1725                         Int *dp;
1726                         ReadFormat(buf, "I8", &ndihedrals);
1727                         if (section == 5) {
1728                                 if (NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, ndihedrals) == NULL)
1729                                         goto panic;
1730                                 dp = mp->dihedrals;
1731                         } else {
1732                                 if (NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, ndihedrals) == NULL)
1733                                         goto panic;
1734                                 dp = mp->impropers;
1735                         }
1736                         for (i = 0; i < ndihedrals; i += 2) {
1737                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1738                                         fclose(fp);
1739                                         snprintf(errbuf, errbufsize, "line %d: premature end of file while reading %s", lineNumber, (section == 5 ? "dihedral" : "improper"));
1740                                         err = 1;
1741                                         goto exit;
1742                                 }
1743                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3, ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1744                                 for (j = 0; j < 2 && i + j < ndihedrals; j++) {
1745                                         Int d1, d2, d3, d4;
1746                                         d1 = ibuf[j * 4] - 1;   /*  Internal atom number is 0-based  */
1747                                         d2 = ibuf[j * 4 + 1] - 1;
1748                                         d3 = ibuf[j * 4 + 2] - 1;
1749                                         d4 = ibuf[j * 4 + 3] - 1;
1750                                         if (d1 < 0 || d1 >= mp->natoms || d2 < 0 || d2 >= mp->natoms || d3 < 0 || d3 >= mp->natoms || d4 < 0 || d4 >= mp->natoms) {
1751                                                 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);
1752                                                 err = 1;
1753                                                 goto exit;
1754                                         }
1755                                         *dp++ = d1;
1756                                         *dp++ = d2;
1757                                         *dp++ = d3;
1758                                         *dp++ = d4;
1759                                 }
1760                         }
1761                         continue;
1762                 }
1763         }
1764         
1765         /*  Create frames for each atom if necessary  */
1766         if (fn > 1) {
1767                 for (i = 0; i < mp->natoms; i++) {
1768                         ap = ATOM_AT_INDEX(mp->atoms, i);
1769                         ap->frames = (Vector *)malloc(sizeof(Vector) * fn);
1770                         if (ap->frames == NULL)
1771                                 goto panic;
1772                         ap->nframes = fn;
1773                         for (j = 0; j < fn; j++)
1774                                 ap->frames[j] = frames[mp->natoms * j + i];
1775                 }
1776                 free(frames);
1777                 frames = NULL;
1778         }
1779
1780   exit:
1781 /*      funlockfile(fp); */
1782         fclose(fp);
1783         mp->nframes = -1;  /*  Should be recalculated later  */
1784         if (err)
1785                 return 1;
1786         else if (section == -1)
1787                 return -1;
1788         return 0;
1789   panic:
1790         Panic("low memory while reading structure file %s", fname);
1791         return 1; /* not reached */
1792 }
1793
1794 /* ("-x", "y", "-z+0.5") -> (-1,0,0,0,0,1,0,0,0,0,-1,0.5)  */
1795 static int
1796 sMoleculeSymopStringsToTransform(char **symops, Transform tr)
1797 {
1798         int i;
1799         char *symop;
1800         memset(tr, 0, sizeof(Transform));
1801         for (i = 0; i < 3; i++) {
1802                 symop = symops[i];
1803                 if (symop == NULL)
1804                         return 1;
1805                 while (*symop != 0) {
1806                         int sn = 1;
1807                         while (isspace(*symop))
1808                                 symop++;
1809                         if (*symop == 0 || *symop == '\r' || *symop == 'n')
1810                                 break;
1811                         if (*symop == '-') {
1812                                 sn = -1;
1813                                 symop++;
1814                         } else if (*symop == '+') {
1815                                 sn = 1;
1816                                 symop++;
1817                         }
1818                         while (isspace(*symop))
1819                                 symop++;
1820                         if (*symop == '.' || isdigit(*symop)) {
1821                                 /*  Numerical offset  */
1822                                 double d = strtod(symop, &symop);
1823                                 if (*symop == '/') {
1824                                         double dd = strtod(symop + 1, &symop);
1825                                         if (dd > 0)
1826                                                 d /= dd;
1827                                         else
1828                                                 return 1;  /*  Bad format  */
1829                                 }
1830                                 tr[9 + i] = d * sn;
1831                         } else if (*symop == 'x' || *symop == 'X') {
1832                                 tr[i] = sn;
1833                                 symop++;
1834                         } else if (*symop == 'y' || *symop == 'Y') {
1835                                 tr[i + 3] = sn;
1836                                 symop++;
1837                         } else if (*symop == 'z' || *symop == 'Z') {
1838                                 tr[i + 6] = sn;
1839                                 symop++;
1840                         } else return 1;  /*  Bad format  */
1841                 } /* end while (*symop != 0) */
1842         }
1843         return 0;
1844 }
1845
1846 static void
1847 sMoleculeGenerateSymopWithTransform(Molecule *mp, Transform gtr, int num)
1848 {
1849         int i, j;
1850         Transform tr;
1851         if (num <= 0)
1852                 num = mp->nsyms;
1853         for (i = 0; i < num; i++) {
1854                 memmove(tr, mp->syms[i], sizeof(Transform));
1855                 TransformMul(tr, gtr, tr);
1856                 for (j = 9; j < 12; j++) {
1857                         if (tr[j] >= 1.0)
1858                                 tr[j] -= 1.0;
1859                         else if (tr[j] <= 0.0)
1860                                 tr[j] += 1.0;
1861                 }
1862                 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1863         }
1864 }
1865
1866 static char *
1867 sChomp(char *buf)
1868 {
1869         char *p = buf + strlen(buf) - 1;
1870         if (p >= buf && (*p == '\n' || *p == '\r')) {
1871                 *p = 0;
1872                 if (--p >= buf && (*p == '\n' || *p == '\r')) {
1873                         *p = 0;
1874                 }
1875         }
1876         return buf;
1877 }
1878
1879 int
1880 MoleculeLoadTepFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
1881 {
1882         FILE *fp;
1883         char buf[1024];
1884         int section = -1;
1885         int lineNumber;
1886         int cellType;
1887         Int ibuf[12];
1888         Double fbuf[12];
1889         Int *bonds, nbonds;
1890         if (errbuf == NULL) {
1891                 errbuf = buf;
1892                 errbufsize = 1024;
1893         }
1894         errbuf[0] = 0;
1895         if (mp == NULL)
1896                 mp = MoleculeNew();
1897         fp = fopen(fname, "rb");
1898         if (fp == NULL) {
1899                 snprintf(errbuf, errbufsize, "Cannot open file");
1900                 return 1;
1901         }
1902         lineNumber = 0;
1903         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1904                 if (section == -1) {
1905                         /*  Title  */
1906                         section = 0;
1907                         continue;
1908                 }
1909                 if (section == 0) {
1910                         /*  XtalCell  */
1911                         ReadFormat(buf, "I1F8F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5);
1912                         cellType = ibuf[0];
1913                         MoleculeSetCell(mp, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], 0);
1914                         section = 1;
1915                         continue;
1916                 }
1917                 if (section == 1) {
1918                         /*  Symmetry  */
1919                         Transform tr;
1920                         if (cellType == 0) {
1921                                 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);
1922                                 tr[0] = fbuf[1];
1923                                 tr[3] = fbuf[2];
1924                                 tr[6] = fbuf[3];
1925                                 tr[1] = fbuf[5];
1926                                 tr[4] = fbuf[6];
1927                                 tr[7] = fbuf[7];
1928                                 tr[2] = fbuf[9];
1929                                 tr[5] = fbuf[10];
1930                                 tr[8] = fbuf[11];
1931                                 tr[9] = fbuf[0];
1932                                 tr[10] = fbuf[4];
1933                                 tr[11] = fbuf[8];
1934                         } else {
1935                                 char *symops[3], *brks;
1936                                 sChomp(buf);
1937                                 memset(tr, 0, sizeof(Transform));
1938                                 ReadFormat(buf, "I1", ibuf);
1939                                 symops[0] = strtok_r(buf + 1, ", ", &brks);
1940                                 symops[1] = strtok_r(NULL, ", ", &brks);
1941                                 symops[2] = strtok_r(NULL, ", ", &brks);
1942                                 if (sMoleculeSymopStringsToTransform(symops, tr)) {
1943                                         snprintf(errbuf, errbufsize, "line %d: bad symmetry specification", lineNumber);
1944                                         return 1;
1945                                 }
1946                         }
1947                         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
1948                                 goto panic;
1949                         if (ibuf[0] != 0)
1950                                 section = 2;
1951                         continue;
1952                 }
1953                 if (section == 2) {      /*  Atoms  */
1954                         char name[8];
1955                         Atom *ap;
1956                         int atomType;
1957                         int atomIndex = mp->natoms;
1958                         ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
1959                         memset(ap, 0, gSizeOfAtomRecord);
1960                         ReadFormat(buf, "S6x3x9x9F9F9F9F9", name, fbuf, fbuf+1, fbuf+2, fbuf+3);
1961                         strncpy(ap->aname, name, 4);
1962                         ap->r.x = fbuf[0];
1963                         ap->r.y = fbuf[1];
1964                         ap->r.z = fbuf[2];
1965                         MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
1966                 /*      ap->atomicNumber = AtomNameToElement(ap->name);
1967                         ElementToString(ap->atomicNumber, ap->element); */
1968                 /*      sAtomSetElement(ap, -1, ap->name); */
1969                         guessElement(ap);
1970                         atomType = fbuf[3];
1971                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1972                                 snprintf(errbuf, errbufsize, "unexpected end of file");
1973                                 return 1;
1974                         }
1975                         ReadFormat(buf, "I1F8F9F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
1976                         atomType = fbuf[6];
1977                         if ((atomType >= 0 && atomType <= 5) || (atomType >= 8 && atomType <= 10)) { 
1978                                 /*  Anisotropic thermal parameters  */
1979                                 MoleculeSetAniso(mp, atomIndex, atomType, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[5], fbuf[4], NULL);
1980                         }
1981                         if (ibuf[0] != 0)
1982                                 section = 3;
1983                         continue;
1984                 }
1985         }
1986         fclose(fp);
1987         MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
1988         if (nbonds > 0) {
1989                 MoleculeAddBonds(mp, nbonds, bonds);
1990                 free(bonds);
1991         }
1992         mp->nframes = -1;  /*  Should be recalculated later  */
1993         return 0;
1994   panic:
1995         Panic("low memory while reading structure file %s", fname);
1996         return -1; /* not reached */
1997 }
1998
1999 int
2000 MoleculeLoadShelxFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
2001 {
2002         FILE *fp;
2003         char buf[1024];
2004         char *p1, *p2;
2005         int n;
2006         int lineNumber;
2007         int latticeType;
2008         int currentResSeq = 0;
2009         char currentResName[6];
2010         Transform tr;
2011         int ibuf[12];
2012         float fbuf[12];
2013         Double dbuf[12];
2014         Int nsfacs = 0;
2015         Int nbonds, *bonds;
2016         char (*sfacs)[4] = NULL;
2017
2018         if (errbuf == NULL) {
2019                 errbuf = buf;
2020                 errbufsize = 1024;
2021         }
2022         errbuf[0] = 0;
2023         if (mp == NULL)
2024                 mp = MoleculeNew();
2025         currentResName[0] = 0;
2026         fp = fopen(fname, "rb");
2027         if (fp == NULL) {
2028                 snprintf(errbuf, errbufsize, "Cannot open file");
2029                 return 1;
2030         }
2031         lineNumber = 0;
2032         tr[0] = tr[4] = tr[8] = 1;
2033         tr[1] = tr[2] = tr[3] = tr[5] = tr[6] = tr[7] = tr[9] = tr[10] = tr[11] = 0;
2034         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), 0, tr) == 0)
2035                 goto panic;
2036         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2037                 if (strncmp(buf, "CELL", 4) == 0) {
2038                         /*  XtalCell  */
2039                         sscanf(buf + 4, " %f %f %f %f %f %f %f", fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2040                         MoleculeSetCell(mp, fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], fbuf[6], 0);
2041                         continue;
2042                 } else if (strncmp(buf, "SFAC", 4) == 0) {
2043                         sChomp(buf);
2044                         for (p1 = strtok_r(buf + 4, " ", &p2); p1 != NULL; p1 = strtok_r(NULL, " ", &p2)) {
2045                                 char *pp = (char *)AssignArray(&sfacs, &nsfacs, 4, nsfacs, NULL);
2046                                 if (pp == NULL)
2047                                         goto panic;
2048                                 strncpy(pp, p1, 3);
2049                                 pp[3] = 0;
2050                         }
2051                         continue;
2052                 } else if (strncmp(buf, "LATT", 4) == 0) {
2053                         sscanf(buf + 4, " %d", &latticeType);
2054                         continue;
2055                 } else if (strncmp(buf, "SYMM", 4) == 0) {
2056                         char *symops[3], *brks;
2057                         memset(tr, 0, sizeof(Transform));
2058                 //      ReadFormat(buf + 4, "I1", ibuf);
2059                         sChomp(buf);
2060                         symops[0] = strtok_r(buf + 4, ",", &brks);
2061                         symops[1] = strtok_r(NULL, ",", &brks);
2062                         symops[2] = strtok_r(NULL, ",", &brks);
2063                         if (sMoleculeSymopStringsToTransform(symops, tr)) {
2064                                 snprintf(errbuf, errbufsize, "line %d: bad symmetry specification", lineNumber);
2065                                 return 1;
2066                         }
2067                         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2068                                 goto panic;
2069                         continue;
2070                 } else if (strncmp(buf, "RESI", 4) == 0) {
2071                         for (p1 = buf + 4; isspace(*p1); p1++);
2072                         if (isalpha(*p1)) {
2073                                 for (p2 = p1 + 1; isalnum(*p2); p2++);
2074                                 *p2 = 0;
2075                                 strncpy(currentResName, p1, 4);
2076                                 currentResName[4] = 0;
2077                                 p1 = p2 + 1;
2078                         } else currentResName[0] = 0;
2079                         sscanf(buf + 4, " %d", &currentResSeq);
2080                         continue;
2081                 } else {
2082                         /* Atom name: [A-Za-z]{1,2}[0-9]*  */
2083                         for (p1 = buf; p1 < buf + 2 && (isalpha(*p1) && *p1 != '_'); p1++);
2084                         if (p1 > buf) {
2085                                 while (isdigit(*p1))
2086                                         p1++;
2087                         }
2088                         if (p1 > buf && p1 <= buf + 4 && isspace(*p1)) {
2089                                 /*  Atom  */
2090                                 Atom *ap;
2091                                 char cont[4];
2092                                 int atomIndex = mp->natoms;
2093                                 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2094                                 memset(ap, 0, gSizeOfAtomRecord);
2095                                 strncpy(ap->aname, buf, 4);
2096                                 n = sscanf(p1, " %d %f %f %f %f %f %f %2s", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, cont);
2097                                 if (n == 8 && strcmp(cont, "=") == 0) {
2098                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2099                                                 snprintf(errbuf, errbufsize, "line %d: unexpected end of file within the atom cards", lineNumber);
2100                                                 return 1;
2101                                         }
2102                                         sscanf(buf, " %f %f %f %f", fbuf+6, fbuf+7, fbuf+8, fbuf+9);
2103                                         n = 10;   /*  Aniso  */
2104                                 } else n = 5; /*  Iso  */
2105                                 ap->r.x = fbuf[0];
2106                                 ap->r.y = fbuf[1];
2107                                 ap->r.z = fbuf[2];
2108                                 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2109                                 ap->occupancy = fbuf[3];
2110                                 if (ap->aname[0] != 'Q' && ibuf[0] >= 1 && ibuf[0] <= nsfacs) {
2111                                         strncpy(ap->element, sfacs[ibuf[0] - 1], 2);
2112                                         ap->element[2] = 0;
2113                                 /*      sAtomSetElement(ap, -1, sfacs[ibuf[0] - 1]); */
2114                                 /*      strncpy(ap->element, sfacs[ibuf[0] - 1], 4);
2115                                         ap->atomicNumber = ElementToInt(ap->element); */
2116                         /*      } else {
2117                                         sAtomSetElement(ap, -1, ap->name); */
2118                                 /*      ap->atomicNumber = AtomNameToElement(ap->name);
2119                                         ElementToString(ap->atomicNumber, ap->element); */
2120                                 }
2121                                 guessElement(ap);
2122                                 if (n == 10 || fbuf[4] >= 0.0) {
2123                                         int i, c, j;
2124                                         /*  Read in the standard deviations  */
2125                                         ReadLine(buf, sizeof buf, fp, &lineNumber);
2126                                         for (i = 0; i < 9; i++) {
2127                                                 j = 3 + i * 8;
2128                                                 c = buf[j + 8];
2129                                                 buf[j + 8] = 0;
2130                                                 dbuf[i] = strtod(buf + j, NULL);
2131                                                 buf[j + 8] = c;
2132                                         }
2133                                         ap->sigma.x = dbuf[0];
2134                                         ap->sigma.y = dbuf[1];
2135                                         ap->sigma.z = dbuf[2];
2136                                 }
2137                                 if (n == 5)
2138                                         ap->tempFactor = fbuf[4] * 78.9568352087147; /* 8*pi*pi */
2139                                 else
2140                                         MoleculeSetAniso(mp, atomIndex, 8, fbuf[4], fbuf[5], fbuf[6], fbuf[9], fbuf[7], fbuf[8], dbuf);
2141                                 ap->resSeq = currentResSeq;
2142                                 strncpy(ap->resName, currentResName, 4);
2143                         }
2144                         continue;
2145                 }
2146         }
2147         fclose(fp);
2148
2149         /*  Add symmetry operations according to the lattice type  */
2150         switch (latticeType < 0 ? -latticeType : latticeType) {
2151                 static Transform tr_i = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0.5};
2152                 static Transform tr_c = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0};
2153                 static Transform tr_a = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.5};
2154                 static Transform tr_b = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5};
2155                 static Transform tr_r1 = {0, 1, 0, -1, -1, 0, 0, 0, 1, 0, 0, 0};
2156                 static Transform tr_r2 = {-1, -1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0};
2157                 case 1:  /* P */
2158                         break;
2159                 case 2:  /* I */
2160                         sMoleculeGenerateSymopWithTransform(mp, tr_i, 0);
2161                         break;
2162                 case 3:  /* Rhombohedral obverse on hexagonal axes  */
2163                         n = mp->nsyms;
2164                         sMoleculeGenerateSymopWithTransform(mp, tr_r1, n);
2165                         sMoleculeGenerateSymopWithTransform(mp, tr_r2, n);
2166                         break;
2167                 case 4:  /* F */
2168                         n = mp->nsyms;
2169                         sMoleculeGenerateSymopWithTransform(mp, tr_a, n);
2170                         sMoleculeGenerateSymopWithTransform(mp, tr_b, n);
2171                         sMoleculeGenerateSymopWithTransform(mp, tr_c, n);
2172                         break;
2173                 case 5:  /* A */
2174                         sMoleculeGenerateSymopWithTransform(mp, tr_a, 0);
2175                         break;
2176                 case 6:  /* B */
2177                         sMoleculeGenerateSymopWithTransform(mp, tr_b, 0);
2178                         break;
2179                 case 7:  /* C */
2180                         sMoleculeGenerateSymopWithTransform(mp, tr_c, 0);
2181                         break;
2182         }
2183                 
2184         if (latticeType > 0) {
2185                 static Transform tr_inv = {-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0};
2186                 sMoleculeGenerateSymopWithTransform(mp, tr_inv, 0);
2187         }
2188         
2189         MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
2190         if (nbonds > 0) {
2191                 MoleculeAddBonds(mp, nbonds, bonds);
2192                 free(bonds);
2193         }
2194         mp->nframes = -1;  /*  Should be recalculated later  */
2195         return 0;
2196   panic:
2197         Panic("low memory while reading structure file %s", fname);
2198         return -1; /* not reached */
2199 }
2200
2201 /*  Add one gaussian orbital shell information (not undoable)  */
2202 int
2203 MoleculeAddGaussianOrbitalShell(Molecule *mol, Int sym, Int nprims, Int a_idx)
2204 {
2205         BasisSet *bset;
2206         ShellInfo *shellp;
2207         if (mol == NULL)
2208                 return -1;  /*  Molecule is empty  */
2209         bset = mol->bset;
2210         if (bset == NULL) {
2211                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2212                 if (bset == NULL)
2213                         return -2;  /*  Low memory  */
2214         }
2215         shellp = AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), bset->nshells, NULL);
2216         if (shellp == NULL)
2217                 return -2;  /*  Low memory  */
2218         switch (sym) {
2219                 case 0:  shellp->sym = kGTOType_S;  shellp->ncomp = 1; break;
2220                 case 1:  shellp->sym = kGTOType_P;  shellp->ncomp = 3; break;
2221                 case -1: shellp->sym = kGTOType_SP; shellp->ncomp = 4; break;
2222                 case 2:  shellp->sym = kGTOType_D;  shellp->ncomp = 6; break;
2223                 case -2: shellp->sym = kGTOType_D5; shellp->ncomp = 5; break;
2224                         /*  TODO: Support F/F7 type orbitals  */
2225                         /*      case 3: sp->sym = kGTOtype_F;  sp->ncomp = 10; break;
2226                          case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break; */
2227                 default:
2228                         return -3;  /* Unsupported shell type  */
2229         }
2230         shellp->nprim = nprims;
2231         shellp->a_idx = a_idx;
2232         if (bset->shells < shellp) {
2233                 shellp->m_idx = shellp[-1].m_idx + shellp[-1].ncomp;
2234                 shellp->p_idx = shellp[-1].p_idx + shellp[-1].nprim;
2235         } else {
2236                 shellp->m_idx = 0;
2237                 shellp->p_idx = 0;
2238         }
2239         return 0;
2240 }
2241
2242 /*  Add a set of gaussian primitive coefficients (not undoable)  */
2243 int
2244 MoleculeAddGaussianPrimitiveCoefficients(Molecule *mol, Double exponent, Double contraction, Double contraction_sp)
2245 {
2246         BasisSet *bset;
2247         PrimInfo *primp;
2248         if (mol == NULL)
2249                 return -1;  /*  Molecule is empty  */
2250         bset = mol->bset;
2251         if (bset == NULL) {
2252                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2253                 if (bset == NULL)
2254                         return -2;  /*  Low memory  */
2255         }
2256         primp = AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), bset->npriminfos, NULL);
2257         if (primp == NULL)
2258                 return -2;  /*  Low memory  */
2259         primp->A = exponent;
2260         primp->C = contraction;
2261         primp->Csp = contraction_sp;
2262         return 0;
2263 }
2264
2265 /*  Set MO coefficients for idx-th MO  */
2266 int
2267 MoleculeSetMOCoefficients(Molecule *mol, Int idx, Double energy, Int ncomps, Double *coeffs)
2268 {
2269         BasisSet *bset;
2270         int i, n;
2271         if (mol == NULL)
2272                 return -1;  /*  Molecule is empty  */
2273         bset = mol->bset;
2274         if (bset == NULL) {
2275                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2276                 if (bset == NULL)
2277                         return -2;  /*  Low memory  */
2278         }
2279         if (bset->nmos == 0) {
2280                 if (bset->nshells > 0) {
2281                         /*  Shell info is already set: calculate the number of MOs from there  */
2282                         for (i = n = 0; i < bset->nshells; i++)
2283                                 n += bset->shells[i].ncomp;
2284                         bset->ncomps = n;
2285                 } else if (ncomps > 0) {
2286                         bset->ncomps = ncomps;
2287                 }
2288                 if (bset->rflag == 0)
2289                         bset->nmos = bset->ncomps * 2;
2290                 else
2291                         bset->nmos = bset->ncomps;
2292                 if (bset->nmos <= 0)
2293                         return -3;  /*  Bad or inconsistent number of MOs  */
2294                 bset->mo = (Double *)calloc(sizeof(Double), bset->nmos * bset->ncomps);
2295                 bset->moenergies = (Double *)calloc(sizeof(Double), bset->nmos);
2296                 if (bset->mo == NULL || bset->moenergies == NULL) {
2297                         if (bset->mo != NULL)
2298                                 free(bset->mo);
2299                         if (bset->moenergies != NULL)
2300                                 free(bset->moenergies);
2301                         bset->mo = NULL;
2302                         bset->moenergies = NULL;
2303                         bset->nmos = 0;
2304                         return -2;  /*  Low memory  */
2305                 }
2306         }
2307         if (idx < 0 || idx >= bset->nmos)
2308                 return -4;  /*  Bad MO index  */
2309         if (energy != -1000000)
2310                 bset->moenergies[idx] = energy;
2311         if (ncomps < bset->ncomps)
2312                 return -5;  /*  Insufficient number of data provided  */
2313         memmove(bset->mo + (idx * bset->ncomps), coeffs, sizeof(Double) * bset->ncomps);
2314         if (bset->cns != NULL) {
2315                 /*  Clear the cached values  */
2316                 free(bset->cns);
2317                 bset->cns = NULL;
2318                 bset->ncns = 0;
2319         }
2320         return 0;
2321 }
2322
2323 /*  Allocate BasisSet record. rflag: UHF, 0; RHF, 1; ROHF, 2
2324     ne_alpha: number of alpha electrons, ne_beta: number of beta electrons
2325     The natoms and pos are copied from mol.  */
2326 int
2327 MoleculeAllocateBasisSetRecord(Molecule *mol, Int rflag, Int ne_alpha, Int ne_beta)
2328 {
2329         BasisSet *bset;
2330         int i;
2331         Atom *ap;
2332         if (mol == NULL || mol->natoms == 0)
2333                 return -1;  /*  Molecule is empty  */
2334         bset = mol->bset;
2335         if (bset == NULL) {
2336                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2337                 if (bset == NULL)
2338                         return -2;  /*  Low memory  */
2339         }
2340         if (bset->pos != NULL) {
2341                 free(bset->pos);
2342                 bset->pos = NULL;
2343         }
2344         bset->natoms = mol->natoms;
2345         bset->pos = (Vector *)calloc(sizeof(Vector), bset->natoms);
2346         if (bset->pos == NULL)
2347                 return -2;  /*  Low memory  */
2348         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
2349                 bset->pos[i].x = ap->r.x * kAngstrom2Bohr;
2350                 bset->pos[i].y = ap->r.y * kAngstrom2Bohr;
2351                 bset->pos[i].z = ap->r.z * kAngstrom2Bohr;
2352         }
2353         bset->ne_alpha = ne_alpha;
2354         bset->ne_beta = ne_beta;
2355         bset->rflag = rflag;
2356         return 0;
2357 }
2358
2359 static void
2360 sSeparateTokens(char *inString, char **outPtr, int size)
2361 {
2362         char *p;
2363         int i;
2364         for (i = 0; i < size; i++) {
2365                 p = strtok((i == 0 ? inString : NULL), " \r\n");
2366                 if (p == NULL)
2367                         break;
2368                 outPtr[i] = p;
2369         }
2370         while (i < size) {
2371                 outPtr[i++] = NULL;
2372         }
2373 }
2374
2375 static int
2376 sReadNumberArray(void *basep, Int *countp, Int size, Int num, FILE *fp, int *lnp)
2377 {
2378         char buf[256];
2379         Int i, n;
2380         *((void **)basep) = NULL;
2381         *countp = 0;
2382         if (AssignArray(basep, countp, size, num - 1, NULL) == NULL)
2383                 return 4;  /*  Out of memory  */
2384         n = 0;
2385         while (ReadLine(buf, sizeof buf, fp, lnp) > 0) {
2386                 char *tokens[16], *p;
2387                 sSeparateTokens(buf, tokens, 16);
2388                 for (i = 0; i < 16; i++) {
2389                         if (tokens[i] == NULL)
2390                                 break;
2391                         if (size == sizeof(Int)) {
2392                                 (*((Int **)basep))[n] = strtol(tokens[i], &p, 0);
2393                         } else if (size == sizeof(Double)) {
2394                                 (*((Double **)basep))[n] = strtod(tokens[i], &p);
2395                         } else return -1;  /*  Internal error  */
2396                         if (tokens[i] == p || *p != 0)
2397                                 return 1;  /*  Non-digit character  */
2398                         if (++n == num) {
2399                                 if (i < 15 && tokens[i + 1] != NULL)
2400                                         return 2;  /*  Too many data  */
2401                                 return 0;  /*  All data are successfully read  */
2402                         }
2403                 }
2404         }
2405         return 3;  /*  Unexpected EOF  */                       
2406 }
2407
2408 static int
2409 sSetupGaussianCoefficients(BasisSet *bset)
2410 {
2411         ShellInfo *sp;
2412         PrimInfo *pp;
2413         int i, j, k;
2414         Double *dp, d;
2415         
2416         /*  Cache the contraction coefficients for efficient calculation  */
2417         /*  Sum up the number of components for all primitives  */
2418         for (i = k = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2419                 sp->cn_idx = k;
2420                 k += sp->nprim * sp->ncomp;
2421         }
2422         /*  Allocate memory for the cached values  */
2423         if (AssignArray(&bset->cns, &bset->ncns, sizeof(Double), k - 1, NULL) == NULL)
2424                 return 1;
2425         /*  Iterate over all primitives  */
2426         dp = bset->cns;
2427         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2428                 for (j = 0, pp = bset->priminfos + sp->p_idx; j < sp->nprim; j++, pp++) {
2429                         switch (sp->sym) {
2430                                 case kGTOType_S:
2431                                         // (8 alpha^3/pi^3)^0.25 exp(-alpha r^2)
2432                                         *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2433                                         break;
2434                                 case kGTOType_P:
2435                                         // (128 alpha^5/pi^3)^0.25 [x|y|z]exp(-alpha r^2)
2436                                         d = pp->C * pow(pp->A, 1.25) * 1.425410941;
2437                                         *dp++ = d;
2438                                         *dp++ = d;
2439                                         *dp++ = d;
2440                                         break;
2441                                 case kGTOType_SP:
2442                                         *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2443                                         d = pp->Csp * pow(pp->A, 1.25) * 1.425410941;
2444                                         *dp++ = d;
2445                                         *dp++ = d;
2446                                         *dp++ = d;
2447                                         break;
2448                                 case kGTOType_D:
2449                                         //  xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2)
2450                                         //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2451                                         d = pp->C * pow(pp->A, 1.75);
2452                                         dp[0] = dp[1] = dp[2] = d * 1.645922781;
2453                                         dp[3] = dp[4] = dp[5] = d * 2.850821881;
2454                                         dp += 6;
2455                                         break;
2456                                 case kGTOType_D5:
2457                                         //  3zz-rr:   (128 alpha^7/9pi^3)^0.25 (3zz-rr)exp(-alpha r^2)
2458                                         //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2459                                         //  xx-yy:    (128 alpha^7/pi^3)^0.25 (xx-yy)exp(-alpha r^2)
2460                                         d = pp->C * pow(pp->A, 1.75);
2461                                         dp[0] = d * 0.822961390;
2462                                         dp[1] = dp[2] = dp[4] = d * 2.850821881;
2463                                         dp[3] = d * 1.425410941;
2464                                         dp += 5;
2465                                         break;
2466                         }
2467                 }
2468         }
2469         return 0;
2470 }
2471
2472 int
2473 MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
2474 {
2475         FILE *fp;
2476         char buf[1024];
2477         int lineNumber;
2478         int natoms, nbasis, i, j, k, n, mxbond, retval, ncomps, nprims, nelec;
2479         BasisSet *bset;
2480         ShellInfo *sp;
2481         PrimInfo *pp;
2482         Int nary;
2483         Int *iary;
2484         Double *dary;
2485         Atom *ap;
2486         Vector *vp;
2487         Double w;
2488
2489         if (errbuf == NULL) {
2490                 errbuf = buf;
2491                 errbufsize = 1024;
2492         }
2493         errbuf[0] = 0;
2494         if (mp == NULL)
2495                 mp = MoleculeNew();
2496         bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2497         if (bset == NULL)
2498                 goto panic;
2499         mp->bset = bset;
2500         fp = fopen(fname, "rb");
2501         if (fp == NULL) {
2502                 snprintf(errbuf, errbufsize, "Cannot open file");
2503                 return 1;
2504         }
2505         lineNumber = 0;
2506         natoms = nbasis = -1;
2507         mxbond = 0;
2508         ncomps = 0;
2509         nelec = 0;
2510         nprims = 0;
2511         nary = 0;
2512         iary = NULL;
2513         dary = NULL;
2514         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2515                 char *tokens[16];
2516                 char *p = buf + 41;
2517                 if (lineNumber == 2) {
2518                         /*  job info line  */
2519                         if (buf[10] == 'U')
2520                                 bset->rflag = 0;  /*  UHF  */
2521                         else if (buf[11] == 'O')
2522                                 bset->rflag = 2;  /*  ROHF  */
2523                         else bset->rflag = 1; /*  RHF  */
2524                         continue;
2525                 }
2526                 while (p > buf && *p == ' ')
2527                         p--;
2528                 p[1] = 0;
2529                 sSeparateTokens(buf + 42, tokens, 16);
2530                 if (strcmp(buf, "Number of atoms") == 0) {
2531                         if (tokens[1] == NULL || (natoms = atoi(tokens[1])) <= 0) {
2532                                 snprintf(errbuf, errbufsize, "Line %d: strange number of atoms: %s", lineNumber, tokens[1]);
2533                                 retval = 2;
2534                                 goto cleanup;
2535                         }
2536                         /*  Allocate atom records (all are empty for now)  */
2537                         AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms - 1, NULL);
2538                         /*  Also allocate atom position array for MO calculations  */
2539                         AssignArray(&bset->pos, &bset->natoms, sizeof(Vector), natoms - 1, NULL);
2540                         /*  Also allocate nuclear charge array  */
2541                         bset->nuccharges = (Double *)calloc(sizeof(Double), natoms);
2542                 } else if (strcmp(buf, "Number of electrons") == 0) {
2543                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2544                                 snprintf(errbuf, errbufsize, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
2545                                 retval = 2;
2546                                 goto cleanup;
2547                         }
2548                         nelec = i;
2549                 } else if (strcmp(buf, "Number of alpha electrons") == 0) {
2550                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2551                                 snprintf(errbuf, errbufsize, "Line %d: strange number of alpha electrons: %s", lineNumber, tokens[1]);
2552                                 retval = 2;
2553                                 goto cleanup;
2554                         }
2555                         bset->ne_alpha = i;
2556                 } else if (strcmp(buf, "Number of beta electrons") == 0) {
2557                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2558                                 snprintf(errbuf, errbufsize, "Line %d: strange number of beta electrons: %s", lineNumber, tokens[1]);
2559                                 retval = 2;
2560                                 goto cleanup;
2561                         }
2562                         bset->ne_beta = i;
2563                         if (bset->ne_alpha + bset->ne_beta != nelec) {
2564                                 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);
2565                                 retval = 2;
2566                                 goto cleanup;
2567                         }
2568                 } else if (strcmp(buf, "Number of basis functions") == 0) {
2569                         if (tokens[1] == NULL || (nbasis = atoi(tokens[1])) <= 0) {
2570                                 snprintf(errbuf, errbufsize, "Line %d: strange number of basis functions: %s", lineNumber, tokens[1]);
2571                                 retval = 2;
2572                                 goto cleanup;
2573                         }
2574                 } else if (strcmp(buf, "Atomic numbers") == 0) {
2575                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2576                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2577                                 retval = 2;
2578                                 goto cleanup;
2579                         }
2580                         if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms, fp, &lineNumber) != 0) {
2581                                 snprintf(errbuf, errbufsize, "Line %d: cannot read atomic numbers", lineNumber);
2582                                 retval = 2;
2583                                 goto cleanup;
2584                         }
2585                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2586                                 ap->atomicNumber = iary[i];
2587                                 bset->nuccharges[i] = iary[i];
2588                                 ElementToString(ap->atomicNumber, ap->element);
2589                                 memmove(ap->aname, ap->element, 4);
2590                                 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
2591                                         ap->weight = w;
2592                         }
2593                         free(iary);
2594                         iary = NULL;
2595                 } else if (strcmp(buf, "Nuclear charges") == 0) {
2596                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2597                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2598                                 retval = 2;
2599                                 goto cleanup;
2600                         }
2601                         if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms, fp, &lineNumber) != 0) {
2602                                 snprintf(errbuf, errbufsize, "Line %d: cannot read nuclear charges", lineNumber);
2603                                 retval = 2;
2604                                 goto cleanup;
2605                         }
2606                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2607                                 bset->nuccharges[i] = dary[i];
2608                         }
2609                         free(iary);
2610                         iary = NULL;
2611                 } else if (strcmp(buf, "Current cartesian coordinates") == 0) {
2612                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * 3) {
2613                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of cartesian coordinates: %s", lineNumber, tokens[2]);
2614                                 retval = 2;
2615                                 goto cleanup;
2616                         }
2617                         if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms * 3, fp, &lineNumber) != 0) {
2618                                 snprintf(errbuf, errbufsize, "Line %d: cannot read cartesian coordinates", lineNumber);
2619                                 retval = 2;
2620                                 goto cleanup;
2621                         }
2622                         for (i = 0, ap = mp->atoms, vp = bset->pos; i < natoms; i++, ap = ATOM_NEXT(ap), vp++) {
2623                                 vp->x = dary[i * 3];
2624                                 vp->y = dary[i * 3 + 1];
2625                                 vp->z = dary[i * 3 + 2];
2626                                 ap->r.x = vp->x * kBohr2Angstrom;
2627                                 ap->r.y = vp->y * kBohr2Angstrom;
2628                                 ap->r.z = vp->z * kBohr2Angstrom;
2629                         }
2630                         free(dary);
2631                         dary = NULL;
2632                 } else if (strcmp(buf, "MxBond") == 0) {
2633                         if (tokens[1] == NULL || (mxbond = atoi(tokens[1])) <= 0) {
2634                                 snprintf(errbuf, errbufsize, "Line %d: strange number of bonds per atom: %s", lineNumber, tokens[1]);
2635                                 retval = 2;
2636                                 goto cleanup;
2637                         }
2638                 } else if (strcmp(buf, "IBond") == 0) {
2639                         Int *bonds;
2640                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * mxbond) {
2641                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of bonds: %s", lineNumber, tokens[2]);
2642                                 retval = 2;
2643                                 goto cleanup;
2644                         }
2645                         if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms * mxbond, fp, &lineNumber) != 0) {
2646                                 snprintf(errbuf, errbufsize, "Line %d: cannot read bond information", lineNumber);
2647                                 retval = 2;
2648                                 goto cleanup;
2649                         }
2650                         bonds = (Int *)malloc(sizeof(Int) * (mxbond * 2 + 1));
2651                         for (i = 0; i < natoms; i++) {
2652                                 for (j = k = 0; j < mxbond; j++) {
2653                                         n = iary[i * mxbond + j] - 1;
2654                                         if (n > i) {
2655                                                 /*  Connect atom i and atom n  */
2656                                                 bonds[k++] = i;
2657                                                 bonds[k++] = n;
2658                                         }
2659                                 }
2660                                 if (k > 0) {
2661                                         bonds[k] = kInvalidIndex;
2662                                         MoleculeAddBonds(mp, k / 2, bonds);
2663                                 }
2664                         }
2665                         free(iary);
2666                         free(bonds);
2667                         iary = NULL;
2668                 } else if (strcmp(buf, "Shell types") == 0) {
2669                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0) {
2670                                 snprintf(errbuf, errbufsize, "Line %d: wrong number of shell types: %s", lineNumber, tokens[2]);
2671                                 retval = 2;
2672                                 goto cleanup;
2673                         }
2674                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2675                                 snprintf(errbuf, errbufsize, "Line %d: cannot read shell types", lineNumber);
2676                                 retval = 2;
2677                                 goto cleanup;
2678                         }
2679                         /*  Allocate ShellInfo table and store shell type information  */
2680                         AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), nary - 1, NULL);
2681                         for (i = n = 0, sp = bset->shells; i < nary; i++, sp++) {
2682                                 switch (iary[i]) {
2683                                         case 0:  sp->sym = kGTOType_S;  sp->ncomp = 1; break;
2684                                         case 1:  sp->sym = kGTOType_P;  sp->ncomp = 3; break;
2685                                         case -1: sp->sym = kGTOType_SP; sp->ncomp = 4; break;
2686                                         case 2:  sp->sym = kGTOType_D;  sp->ncomp = 6; break;
2687                                         case -2: sp->sym = kGTOType_D5; sp->ncomp = 5; break;
2688                                                 /*  TODO: Support F/F7 type orbitals  */
2689                                                 /*      case 3: sp->sym = kGTOtype_F;  sp->ncomp = 10; break;
2690                                                  case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break; */
2691                                         default:
2692                                                 snprintf(errbuf, errbufsize, "Line %d: unsupported shell type %d", lineNumber, iary[i]);
2693                                                 retval = 2;
2694                                                 goto cleanup;
2695                                 }
2696                                 sp->m_idx = n;
2697                                 n += sp->ncomp;
2698                         }
2699                         bset->ncomps = ncomps = n;
2700                         free(iary);
2701                         iary = NULL;
2702                 } else if (strcmp(buf, "Number of primitives per shell") == 0) {
2703                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2704                                 snprintf(errbuf, errbufsize, "Line %d: wrong size of the primitive table: %s", lineNumber, tokens[2]);
2705                                 retval = 2;
2706                                 goto cleanup;
2707                         }
2708                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2709                                 snprintf(errbuf, errbufsize, "Line %d: cannot read primitive table", lineNumber);
2710                                 retval = 2;
2711                                 goto cleanup;
2712                         }
2713                         for (i = n = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2714                                 sp->nprim = iary[i];
2715                                 sp->p_idx = n;
2716                                 n += sp->nprim;
2717                         }
2718                         nprims = n;
2719                         free(iary);
2720                         iary = NULL;
2721                 } else if (strcmp(buf, "Shell to atom map") == 0) {
2722                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2723                                 snprintf(errbuf, errbufsize, "Line %d: wrong size of the shell-to-atom map: %s", lineNumber, tokens[2]);
2724                                 retval = 2;
2725                                 goto cleanup;
2726                         }
2727                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2728                                 snprintf(errbuf, errbufsize, "Line %d: cannot read shell-to-atom table", lineNumber);
2729                                 retval = 2;
2730                                 goto cleanup;
2731                         }
2732                         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2733                                 sp->a_idx = iary[i] - 1;
2734                         }
2735                         free(iary);
2736                         iary = NULL;
2737                 } else if (strcmp(buf, "Primitive exponents") == 0) {
2738                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nprims) {
2739                                 snprintf(errbuf, errbufsize, "Line %d: wrong number of primitive exponents: %s", lineNumber, tokens[2]);
2740                                 retval = 2;
2741                                 goto cleanup;
2742                         }
2743                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2744                                 snprintf(errbuf, errbufsize, "Line %d: cannot read primitive exponents", lineNumber);
2745                                 retval = 2;
2746                                 goto cleanup;
2747                         }
2748                         /*  Allocate PrimInfo table  */
2749                         AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), nprims - 1, NULL);
2750                         for (i = 0, pp = bset->priminfos; i < nprims; i++, pp++) {
2751                                 pp->A = dary[i];
2752                         }
2753                         free(dary);
2754                         dary = NULL;
2755                 } else if (strcmp(buf, "Contraction coefficients") == 0) {
2756                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2757                                 snprintf(errbuf, errbufsize, "Line %d: wrong number of contraction coefficients: %s", lineNumber, tokens[2]);
2758                                 retval = 2;
2759                                 goto cleanup;
2760                         }
2761                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2762                                 snprintf(errbuf, errbufsize, "Line %d: cannot read contraction coefficients", lineNumber);
2763                                 retval = 2;
2764                                 goto cleanup;
2765                         }
2766                         for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2767                                 pp->C = dary[i];
2768                         }
2769                         free(dary);
2770                         dary = NULL;
2771                 } else if (strcmp(buf, "P(S=P) Contraction coefficients") == 0) {
2772                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2773                                 snprintf(errbuf, errbufsize, "Line %d: wrong number of P(S=P) contraction coefficients: %s", lineNumber, tokens[2]);
2774                                 retval = 2;
2775                                 goto cleanup;
2776                         }
2777                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2778                                 snprintf(errbuf, errbufsize, "Line %d: cannot read P(S=P) contraction coefficients", lineNumber);
2779                                 retval = 2;
2780                                 goto cleanup;
2781                         }
2782                         for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2783                                 pp->Csp = dary[i];
2784                         }
2785                         free(dary);
2786                         dary = NULL;
2787                 } else if (strcmp(buf, "Alpha Orbital Energies") == 0) {
2788                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2789                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of alpha orbitals: %s", lineNumber, tokens[2]);
2790                                 retval = 2;
2791                                 goto cleanup;
2792                         }
2793                         if (sReadNumberArray(&bset->moenergies, &bset->nmos, sizeof(Double), i, fp, &lineNumber) != 0) {
2794                                 snprintf(errbuf, errbufsize, "Line %d: cannot read alpha orbital energies", lineNumber);
2795                                 retval = 2;
2796                                 goto cleanup;
2797                         }
2798                 } else if (strcmp(buf, "Alpha MO coefficients") == 0) {
2799                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2800                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of alpha MO coefficients: %s", lineNumber, tokens[2]);
2801                                 retval = 2;
2802                                 goto cleanup;
2803                         }
2804                         if (sReadNumberArray(&bset->mo, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2805                                 snprintf(errbuf, errbufsize, "Line %d: cannot read MO coefficients", lineNumber);
2806                                 retval = 2;
2807                                 goto cleanup;
2808                         }
2809                 } else if (strcmp(buf, "Beta Orbital Energies") == 0) {
2810                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2811                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of beta orbitals: %s", lineNumber, tokens[2]);
2812                                 retval = 2;
2813                                 goto cleanup;
2814                         }
2815                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2816                                 snprintf(errbuf, errbufsize, "Line %d: cannot read beta orbital energies", lineNumber);
2817                                 retval = 2;
2818                                 goto cleanup;
2819                         }
2820                         bset->moenergies = (Double *)realloc(bset->moenergies, sizeof(Double) * 2 * ncomps);
2821                         bset->nmos = ncomps * 2;
2822                         bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);
2823                         memmove(bset->moenergies + ncomps, dary, sizeof(Double) * ncomps);
2824                         memset(bset->mo + ncomps * ncomps, 0, sizeof(Double) * ncomps * ncomps);
2825                         free(dary);
2826                         dary = NULL;
2827                 } else if (strcmp(buf, "Beta MO coefficients") == 0) {
2828                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2829                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of beta MO coefficients: %s", lineNumber, tokens[2]);
2830                                 retval = 2;
2831                                 goto cleanup;
2832                         }
2833                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2834                                 snprintf(errbuf, errbufsize, "Line %d: cannot read alpha MO coefficients", lineNumber);
2835                                 retval = 2;
2836                                 goto cleanup;
2837                         }
2838                         bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);  /*  Should be unnecessary, just in case  */
2839                         memmove(bset->mo + ncomps * ncomps, dary, sizeof(Double) * ncomps * ncomps);
2840                         free(dary);
2841                         dary = NULL;
2842                 } else if (strcmp(buf, "Total SCF Density") == 0) {
2843                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * (ncomps + 1) / 2) {
2844                                 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of SCF densities: %s", lineNumber, tokens[2]);
2845                                 retval = 2;
2846                                 goto cleanup;
2847                         }
2848                         if (sReadNumberArray(&bset->scfdensities, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2849                                 snprintf(errbuf, errbufsize, "Line %d: cannot read SCF densities", lineNumber);
2850                                 retval = 2;
2851                                 goto cleanup;
2852                         }
2853                 }
2854         }
2855         if (mp->natoms == 0) {
2856                 snprintf(errbuf, errbufsize, "Atom information is missing");
2857                 retval = 2;
2858                 goto cleanup;
2859         }
2860         if (bset->shells == NULL || bset->priminfos == NULL) {
2861                 snprintf(errbuf, errbufsize, "Gaussian primitive information is missing");
2862                 retval = 2;
2863                 goto cleanup;
2864         }
2865         if (bset->mo == NULL) {
2866                 snprintf(errbuf, errbufsize, "MO coefficients were not found");
2867                 retval = 2;
2868                 goto cleanup;
2869         }
2870         if (sSetupGaussianCoefficients(bset) != 0) {
2871                 snprintf(errbuf, errbufsize, "Internal error during setup MO calculation");
2872                 retval = 2;
2873                 goto cleanup;
2874         }
2875         mp->nframes = -1;
2876         retval = 0;
2877 cleanup:
2878         fclose(fp);
2879         if (iary != NULL)
2880                 free(iary);
2881         if (dary != NULL)
2882                 free(dary);
2883         if (retval != 0) {
2884                 if (mp->bset != NULL) {
2885                         BasisSetRelease(mp->bset);
2886                         mp->bset = NULL;
2887                 }
2888         }
2889         return retval;
2890 panic:
2891         Panic("low memory while reading fchk file %s", fname);
2892         return -1; /* not reached */    
2893 }
2894
2895 int
2896 MoleculeLoadGamessDatFile(Molecule *mol, const char *fname, char *errbuf, int errbufsize)
2897 {
2898         FILE *fp;
2899         int newmol = 0;
2900         char buf[1024];
2901         int lineNumber, i, j, k, len, natoms = 0;
2902         int nframes = 0;
2903         int n1;
2904         int ival[8];
2905         double dval[8];
2906         char sval[16];
2907         Vector *vbuf = NULL;
2908         IntGroup *ig;
2909         int optimizing = 0, status = 0;
2910
2911         if (errbuf == NULL) {
2912                 errbuf = buf;
2913                 errbufsize = 1024;
2914         }
2915         errbuf[0] = 0;
2916         if (mol == NULL) {
2917                 mol = MoleculeNew();
2918         }
2919         if (mol->natoms == 0)
2920                 newmol = 1;
2921
2922         fp = fopen(fname, "rb");
2923         if (fp == NULL) {
2924                 snprintf(errbuf, errbufsize, "Cannot open file");
2925                 return 1;
2926         }
2927         
2928         /*  ESP is cleared (not undoable!)  */
2929         if (mol->elpots != NULL) {
2930                 free(mol->elpots);
2931                 mol->elpots = NULL;
2932                 mol->nelpots = 0;
2933         }
2934         
2935         lineNumber = 0;
2936         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2937         redo:
2938                 n1 = 0;
2939                 if (strncmp(buf, " $DATA", 6) == 0) {
2940                         /*  Initial geometry  */
2941                         if (!newmol) {
2942                                 vbuf = (Vector *)calloc(sizeof(Vector), mol->natoms);
2943                         }
2944                         i = 0;
2945                         ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Title  */
2946                         ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Symmetry  */
2947                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2948                                 if (strncmp(buf, " $END", 5) == 0)
2949                                         break;
2950                                 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
2951                                         snprintf(errbuf, errbufsize, "Line %d: bad format in $DATA section", lineNumber);
2952                                         return 2;
2953                                 }
2954                                 if (newmol) {
2955                                         Atom a;
2956                                         memset(&a, 0, sizeof(a));
2957                                         strncpy(a.aname, sval, 4);
2958                                         a.r.x = dval[1];
2959                                         a.r.y = dval[2];
2960                                         a.r.z = dval[3];
2961                                         a.atomicNumber = (Int)dval[0];
2962                                         strncpy(a.element, ElementToString(a.atomicNumber, sval), 3);
2963                                         a.type = AtomTypeEncodeToUInt(a.element);
2964                                         a.weight = WeightForAtomicNumber(a.atomicNumber);
2965                                         MoleculeCreateAnAtom(mol, &a, mol->natoms);
2966                                 } else {
2967                                         Atom *ap;
2968                                         if (i >= mol->natoms) {
2969                                                 snprintf(errbuf, errbufsize, "Line %d: too many atoms", lineNumber);
2970                                                 return 3;
2971                                         }
2972                                         if ((ap = ATOM_AT_INDEX(mol->atoms, i))->atomicNumber != dval[0]) {
2973                                                 snprintf(errbuf, errbufsize, "Line %d: atomic number does not match", lineNumber);
2974                                                 return 4;
2975                                         }
2976                                         vbuf[i].x = dval[1];
2977                                         vbuf[i].y = dval[2];
2978                                         vbuf[i].z = dval[3];
2979                                 }
2980                                 /*  Skip until a blank line is found  */
2981                                 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2982                                         for (j = 0; buf[j] == ' '; j++);
2983                                         if (buf[j] == '\n')
2984                                                 break;
2985                                 }
2986                                 i++;
2987                         }
2988                         natoms = i;
2989                         if (!newmol) {
2990                                 /*  Set atom positions  */
2991                                 IntGroup *ig;
2992                                 if (natoms < mol->natoms) {
2993                                         snprintf(errbuf, errbufsize, "Line %d: too few atoms", lineNumber);
2994                                         return 5;
2995                                 }
2996                                 ig = IntGroupNewWithPoints(0, natoms, -1);
2997                                 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, natoms, vbuf);
2998                                 IntGroupRelease(ig);
2999                         }
3000                         if (vbuf == NULL)
3001                                 vbuf = (Vector *)calloc(sizeof(Vector), natoms);
3002                         nframes = MoleculeGetNumberOfFrames(mol);
3003                         if (status < 0)
3004                                 break;
3005                         continue;
3006                 } else if (strstr(buf, "DATA FROM NSERCH") != NULL || (strstr(buf, "RESULTS FROM SUCCESSFUL") != NULL && (n1 = 1))) {
3007                         /*  Skip until the separator line is read (three or four lines)  */
3008                         i = 0;
3009                         do {
3010                                 if (i++ >= 4) {
3011                                         snprintf(errbuf, errbufsize, "Line %d: the separator line at the top of the coordinates is not found: bad format?", lineNumber);
3012                                         return 6;
3013                                 }
3014                                 ReadLine(buf, sizeof buf, fp, &lineNumber);
3015                         } while (strstr(buf, "----------------------------") == NULL);
3016                         for (i = 0; i < natoms; i++) {
3017                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3018                                         snprintf(errbuf, errbufsize, "Unexpected end of file in reading NSERCH data");
3019                                         return 6;
3020                                 }
3021                                 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3022                                         snprintf(errbuf, errbufsize, "Line %d: bad format in NSERCH coordinate data", lineNumber);
3023                                         return 7;
3024                                 }
3025                                 vbuf[i].x = dval[1];
3026                                 vbuf[i].y = dval[2];
3027                                 vbuf[i].z = dval[3];
3028                         }
3029                         ig = IntGroupNewWithPoints(nframes, 1, -1);
3030                         MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, natoms, vbuf, 0, NULL);
3031                         IntGroupRelease(ig);
3032                         nframes++;
3033                         if (n1 == 0)
3034                                 optimizing = 1;  /*  Flag to skip reading the VEC group  */
3035                         else
3036                                 optimizing = 0;
3037                         continue;
3038                 } else if (strstr(buf, "E(UHF)") != NULL || (strstr(buf, "E(RHF)") != NULL && (n1 = 1)) || (strstr(buf, "E(ROHF)") != NULL && (n1 = 2))) {
3039                         if (mol->bset == NULL) {
3040                                 i = MoleculeAllocateBasisSetRecord(mol, n1, 0, 0);
3041                                 if (i != 0) {
3042                                         snprintf(errbuf, errbufsize, "Line %d: cannot allocate basis set internal buffer", lineNumber);
3043                                         return 8;
3044                                 }
3045                         }
3046                 } else if (strncmp(buf, " $VEC", 5) == 0) {
3047                         Double *coeffs;
3048                         /*  Read the vec group  */
3049                         if (mol->bset == NULL || mol->bset->ncomps == 0)
3050                                 continue;  /*  Just ignore  */
3051                         if (optimizing)
3052                                 continue;  /*  Ignore VEC group during optimization  */
3053                         coeffs = (Double *)calloc(sizeof(Double), mol->bset->ncomps);
3054                         if (coeffs == NULL) {
3055                                 snprintf(errbuf, errbufsize, "Line %d: low memory during $VEC", lineNumber);
3056                                 return 9;
3057                         }
3058                         i = k = 0;
3059                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3060                                 len = strlen(buf);
3061                                 if (strncmp(buf, " $END", 5) == 0)
3062                                         break;
3063                                 while ((j = 5 + (k % 5) * 15) <= len && buf[j] != 0 && buf[j] != '\n') {
3064                                         strncpy(sval, buf + j, 15);
3065                                         sval[15] = 0;
3066                                         coeffs[k] = strtod(sval, NULL);
3067                                         k++;
3068                                         if ((k % 5) == 0)
3069                                                 break;
3070                                 }
3071                                 if (k < mol->bset->ncomps)
3072                                         continue;
3073                                 j = MoleculeSetMOCoefficients(mol, i, -1000000, k, coeffs);
3074                                 if (j != 0) {
3075                                         snprintf(errbuf, errbufsize, "Line %d: cannot set coefficients for MO %d", lineNumber, i + 1);
3076                                         free(coeffs);
3077                                         return 10;
3078                                 }
3079                                 i++;
3080                                 k = 0;
3081                         }
3082                         if (status < 0)
3083                                 break;
3084                         continue;
3085                 } else if ((strstr(buf, "ELECTRIC POTENTIAL") != NULL || strstr(buf, "ELECTROSTATIC POTENTIAL") != NULL) && strstr(buf, "ELPOTT") != NULL) {
3086                         i = 0;
3087                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3088                                 Elpot *ep;
3089                                 if (strstr(buf, "TOTAL NUMBER OF GRID POINTS") != NULL)
3090                                         continue;
3091                                 if (sscanf(buf, "%d %lf %lf %lf %lf", &ival[0], &dval[0], &dval[1], &dval[2], &dval[3]) < 5)
3092                                         break;
3093                                 ep = AssignArray(&mol->elpots, &mol->nelpots, sizeof(Elpot), i, NULL);
3094                                 ep->pos.x = dval[0];
3095                                 ep->pos.y = dval[1];
3096                                 ep->pos.z = dval[2];
3097                                 ep->esp = dval[3];
3098                                 i++;
3099                         }
3100                         if (status > 0)
3101                                 goto redo;  /*  This section has no end line, so the last line should be processed again  */
3102                         else break;    /*  End of file encountered or interrupted */
3103                 }  /*  TODO: read MOLPLT info if present  */
3104         }
3105         if (status < 0) {
3106                 snprintf(errbuf, errbufsize, "User interrupt at line %d", lineNumber);
3107                 return 11;
3108         }
3109         if (vbuf != NULL)
3110                 free(vbuf);
3111         if (newmol && mol->nbonds == 0) {
3112                 /*  Guess bonds  */
3113                 Int nbonds, *bonds;
3114                 MoleculeGuessBonds(mol, 1.2, &nbonds, &bonds);
3115                 if (nbonds > 0) {
3116                         MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds);
3117                         free(bonds);
3118                 }
3119         }
3120         return 0;
3121 }
3122
3123 int
3124 MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
3125 {
3126         int retval;
3127         if (ftype == NULL || *ftype == 0) {
3128                 const char *cp;
3129                 cp = strrchr(fname, '.');
3130                 if (cp != NULL)
3131                         ftype = cp + 1;
3132                 else {
3133                         cp = guessMoleculeType(fname);
3134                         if (strcmp(cp, "???") != 0)
3135                                 ftype = cp;
3136                 }
3137         }
3138         if (strcasecmp(ftype, "pdb") == 0) {
3139                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
3140         }
3141         if (retval != 0) {
3142                 /*  Try all formats once again  */
3143                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
3144         }
3145         return retval;
3146 }
3147
3148 int
3149 MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3150 {
3151         FILE *fp;
3152         char buf[1024];
3153         char *p;
3154         int lineNumber;
3155         int i, j, new_unit, retval;
3156         Atom *ap;
3157         IntGroup *ig;
3158         Vector *vp = NULL;
3159         Int ibuf[12];
3160         Int entries = 0;
3161         retval = 0;
3162         if (errbuf == NULL) {
3163                 errbuf = buf;
3164                 errbufsize = 1024;
3165         }
3166         errbuf[0] = 0;
3167         fp = fopen(fname, "rb");
3168         if (fp == NULL) {
3169                 snprintf(errbuf, errbufsize, "Cannot open file");
3170                 return -1;
3171         }
3172 /*      flockfile(fp); */
3173         if (mp->natoms == 0)
3174                 new_unit = 1;
3175         else {
3176                 /*  Allocate buffer for undo-capable modification  */
3177                 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
3178                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3179                         /*  Retain current position if the atom info is missing in the input file  */
3180                         vp[i] = ap->r;
3181                 }
3182                 new_unit = 0;
3183         }
3184         lineNumber = 0;
3185         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3186                 if (strncmp(buf, "END", 3) == 0)
3187                         break;
3188                 if (strncmp(buf, "HETATM", 6) == 0 || strncmp(buf, "ATOM", 4) == 0) {
3189                         struct {
3190                                 Int serial, intCharge, resSeq;
3191                                 Vector r;
3192                                 Double occ, temp;
3193                                 char segName[5], resName[4], atomName[5], resSeqStr[5], atomType[3], element[3], occStr[6];
3194                         } w;
3195                         memset(&w, 0, sizeof(w));
3196                         ReadFormat(buf, "x6 I5 x1 S4 x1 S3 x1 x1 S4 x1 x3 F8 F8 F8 S6 F6 x6 S4 S2 I2",
3197                                 &w.serial, w.atomName, w.resName, w.resSeqStr, &w.r.x, &w.r.y, &w.r.z,
3198                                 w.occStr, &w.temp, w.segName, w.element, &w.intCharge);
3199                         if (w.atomName[0] == 0) {
3200                                 continue;  /*  Atom name is empty  */
3201                         }
3202                         /*  A workaround for residue number >= 10000 (XPLOR style)  */
3203                         if (w.resSeqStr[0] >= 'A' && w.resSeqStr[0] <= 'Z') {
3204                                 w.resSeq = (w.resSeqStr[0] - 'A' + 10) * 1000 + atoi(w.resSeqStr + 1);
3205                         } else {
3206                                 w.resSeq = atoi(w.resSeqStr);
3207                         }
3208                         if (w.element[0] == 0) {
3209                                 /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
3210                                 for (p = w.atomName; *p != 0; p++) {
3211                                         if (isalpha(*p) && *p != '_') {
3212                                                 w.element[0] = toupper(*p);
3213                                                 if (isalpha(p[1]) && p[1] != '_') {
3214                                                         w.element[1] = toupper(p[1]);
3215                                                         w.element[2] = 0;
3216                                                 } else {
3217                                                         w.element[1] = 0;
3218                                                 }
3219                                                 break;
3220                                         }
3221                                 }
3222                         }
3223                         if (w.occStr[0] == 0)
3224                                 w.occ = 1.0;
3225                         else
3226                                 w.occ = atof(w.occStr);
3227                         if (w.serial <= 0) {
3228                                 snprintf(errbuf, errbufsize, "line %d: non-positive atom number %d", lineNumber, w.serial);
3229                                 retval = 1;
3230                                 goto abort;
3231                         }
3232                         w.serial--;  /*  The internal atom number is 0-based  */
3233                         if (w.serial >= mp->natoms) {
3234                                 if (new_unit) {
3235                                         /*  Create a new atom entry  */
3236                                         ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, w.serial, NULL);
3237                                 } else {
3238                                         snprintf(errbuf, errbufsize, "line %d: the atom number %d does not exist in the structure file", lineNumber, w.serial+1);
3239                                         retval = 1;
3240                                         goto abort;
3241                                 }
3242                         }
3243                         if (new_unit) {
3244                                 ap = ATOM_AT_INDEX(mp->atoms, w.serial);
3245                                 ap->r = w.r;
3246                                 ap->occupancy = w.occ;
3247                                 ap->tempFactor = w.temp;
3248                                 if (w.segName[0] == 0)
3249                                         strncpy(w.segName, "MAIN", 4);
3250                                 strncpy(ap->segName, w.segName, 4);
3251                                 ap->resSeq = w.resSeq;
3252                                 strncpy(ap->resName, w.resName, 4);
3253                                 strncpy(ap->aname, w.atomName, 4);
3254                                 strncpy(ap->element, w.element, 2);
3255                                 ap->intCharge = w.intCharge;
3256                                 if (ap->resSeq > 0) {
3257                                         if (ap->resSeq < mp->nresidues) {
3258                                                 /*  Update the resName according to residues[]  */
3259                                                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
3260                                         } else {
3261                                                 /*  Register the resName to residues[]  */
3262                                                 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, w.resName);
3263                                         }
3264                                 } else {
3265                                         ap->resSeq = 0;
3266                                         strcpy(ap->resName, "XXX");
3267                                         if (mp->nresidues == 0)
3268                                                 AssignArray(&mp->residues, &mp->nresidues, 4, 0, ap->resName);
3269                                 }
3270                                 i = ElementToInt(ap->element);
3271                                 if (i >= 0)
3272                                         ap->weight = gElementParameters[i].weight;
3273                         } else {
3274                                 /*  Not a new unit: only the atom position is updated  */
3275                                 vp[w.serial] = w.r;
3276                         }
3277                         entries++;
3278                 } else if (strncmp(buf, "CONECT", 6) == 0 && new_unit) {
3279                         i = ReadFormat(buf, "x6 I5I5I5I5I5I5I5I5I5I5I5I5",
3280                                 ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
3281                                 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7,
3282                                 ibuf + 8, ibuf + 9, ibuf + 10, ibuf + 11);
3283                         if (i >= 2) {
3284                                 Int bbuf[25];
3285                                 int bi;
3286                                 for (j = 0; j < i; j++) {
3287                                         if (ibuf[j] < 0 || ibuf[j] > mp->natoms) {
3288                                                 snprintf(errbuf, errbufsize, "line %d: The CONECT record contains non-existent atom %d", lineNumber, ibuf[j]);
3289                                                 retval = 1;
3290                                                 goto abort;
3291                                         } else if (ibuf[j] == 0)
3292                                                 break;
3293                                 }
3294                                 i = j;
3295                                 if (i < 2)
3296                                         continue;
3297                                 for (j = 1, bi = 0; j < i; j++) {
3298                                         if (ibuf[0] < ibuf[j]) {
3299                                                 bbuf[bi * 2] = ibuf[0] - 1;
3300                                                 bbuf[bi * 2 + 1] = ibuf[j] - 1;
3301                                                 bi++;
3302                                         }
3303                                 }
3304                                 if (bi == 0)
3305                                         continue;
3306                                 bbuf[bi * 2] = -1;
3307                                 retval = MoleculeAddBonds(mp, bi, bbuf);
3308                                 if (retval < 0) {
3309                                         snprintf(errbuf, errbufsize, "line %d: bad bond specification", lineNumber);
3310                                         retval = 1;
3311                                         goto abort;
3312                                 }
3313                         }
3314                 }
3315         }
3316 /*      funlockfile(fp); */
3317         fclose(fp);
3318         if (new_unit) {
3319                 /*  Renumber atoms if some atom number is unoccupied  */
3320                 int *old2new, oldidx, newidx;
3321                 old2new = (int *)calloc(sizeof(int), mp->natoms);
3322                 if (old2new == NULL) {
3323                         snprintf(errbuf, errbufsize, "Out of memory");
3324                         retval = 1;
3325                         goto abort;
3326                 }
3327                 for (oldidx = newidx = 0; oldidx < mp->natoms; oldidx++) {
3328                         ap = ATOM_AT_INDEX(mp->atoms, oldidx);
3329                         if (ap->aname[0] != 0) {
3330                                 old2new[oldidx] = newidx;
3331                                 if (oldidx > newidx)
3332                                         memmove(ATOM_AT_INDEX(mp->atoms, newidx), ap, gSizeOfAtomRecord);
3333                                 newidx++;
3334                         }
3335                 }
3336                 mp->natoms = newidx;
3337                 if (oldidx > newidx) {
3338                         /*  Renumber the connects and bonds  */
3339                         Int *cp;
3340                         for (i = 0; i < mp->natoms; i++) {
3341                                 ap = ATOM_AT_INDEX(mp->atoms, i);
3342                                 cp = AtomConnectData(&ap->connect);
3343                                 for (j = 0; j < ap->connect.count; j++) {
3344                                         cp[j] = old2new[cp[j]];
3345                                 }
3346                         }
3347                         for (i = 0; i < mp->nbonds * 2; i++) {
3348                                 mp->bonds[i] = old2new[mp->bonds[i]];
3349                         }
3350                 }
3351                 retval = MoleculeRebuildTablesFromConnects(mp);
3352                 if (retval != 0) {
3353                         /*  This error may not happen  */
3354                         snprintf(errbuf, errbufsize, "Cannot build angle/dihedral/improper tables");
3355                         retval = 1;
3356                         goto abort;
3357                 }
3358                 /*  Undo action: delete all atoms  */
3359                 {
3360                         MolAction *act;
3361                         ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3362                         act = MolActionNew(gMolActionUnmergeMolecule, ig);
3363                         act->frame = mp->cframe;
3364                         MolActionCallback_registerUndo(mp, act);
3365                         MolActionRelease(act);
3366                         IntGroupRelease(ig);
3367                 }
3368         } else {
3369                 /*  Set the new atom positions  */
3370                 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3371                 MolActionCreateAndPerform(mp, gMolActionSetAtomPositions, ig, mp->natoms, vp);
3372                 IntGroupRelease(ig);
3373                 free(vp);
3374                 vp = NULL;
3375         }
3376         mp->nframes = -1;  /*  Should be recalculated later  */
3377         if (entries == 0)
3378                 return 1;  /*  No atoms  */
3379         return 0;
3380         abort:
3381         if (fp != NULL) {
3382         /*      funlockfile(fp); */
3383                 fclose(fp);
3384         }
3385         if (vp != NULL)
3386                 free(vp);
3387         if (entries == 0)
3388                 return 1;  /*  Maybe different format?  */
3389         return retval;
3390 }
3391
3392 int
3393 MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3394 {
3395         DcdRecord dcd;
3396         SFloat32 *xp, *yp, *zp;
3397         Vector *vp, *cp;
3398         IntGroup *ig;
3399         int n;
3400         errbuf[0] = 0;
3401         if (mp == NULL || mp->natoms == 0) {
3402                 snprintf(errbuf, errbufsize, "Molecule is empty");
3403                 return 1;
3404         }
3405         n = DcdOpen(fname, &dcd);
3406         if (n != 0) {
3407                 switch (n) {
3408                         case -2: snprintf(errbuf, errbufsize, "Cannot open file"); break;
3409                         case 1:  snprintf(errbuf, errbufsize, "Premature EOF encountered"); break;
3410                         case 2:  snprintf(errbuf, errbufsize, "Bad block length of the first section"); break;
3411                         case 3:  snprintf(errbuf, errbufsize, "\"CORD\" signature is missing"); break;
3412                         case 4:  snprintf(errbuf, errbufsize, "Bad termination of the first section"); break;
3413                         case 5:  snprintf(errbuf, errbufsize, "The title section is not correct"); break;
3414                         case 6:  snprintf(errbuf, errbufsize, "The atom number section is not correct"); break;
3415                         default: snprintf(errbuf, errbufsize, "Read error in dcd file"); break;
3416                 }
3417         } else {
3418                 if (dcd.natoms == 0)
3419                         snprintf(errbuf, errbufsize, "No atoms were found in the dcd file");
3420                 else if (dcd.nframes == 0)
3421                         snprintf(errbuf, errbufsize, "No frames were found in the dcd file");
3422         }
3423         if (errbuf[0] != 0) {
3424                 if (n == 0)
3425                         DcdClose(&dcd);
3426                 return 1;
3427         }
3428
3429         vp = (Vector *)calloc(sizeof(Vector), mp->natoms * dcd.nframes);
3430         if (dcd.nextra)
3431                 cp = (Vector *)calloc(sizeof(Vector), dcd.nframes * 4);
3432         else cp = NULL;
3433         xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3434         yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3435         zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3436         ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mp), dcd.nframes, -1);
3437         if (vp == NULL || xp == NULL || yp == NULL || zp == NULL || ig == NULL) {
3438                 snprintf(errbuf, errbufsize, "Cannot allocate memory");
3439                 if (vp) free(vp);
3440                 if (cp) free(cp);
3441                 if (xp) free(xp);
3442                 if (yp) free(yp);
3443                 if (zp) free(zp);
3444                 if (ig) IntGroupRelease(ig);
3445                 return 1;
3446         }
3447         for (n = 0; n < dcd.nframes; n++) {
3448                 int i;
3449                 Vector *vpp;
3450                 SFloat32 dcdcell[6];
3451                 if (DcdReadFrame(&dcd, n, xp, yp, zp, dcdcell)) {
3452                         snprintf(errbuf, errbufsize, "Read error in dcd file");
3453                         goto exit;
3454                 }
3455                 for (i = 0, vpp = &vp[n * mp->natoms]; i < dcd.natoms && i < mp->natoms; i++, vpp++) {
3456                         vpp->x = xp[i];
3457                         vpp->y = yp[i];
3458                         vpp->z = zp[i];
3459                 }
3460                 if (cp != NULL) {
3461                         Double sing;
3462                         vpp = &cp[n * 4];
3463                         /*  dcdcell = {a, gamma, b, beta, alpha, c} */
3464                         /*  angles are described either in cosines (Charmm and NAMD > 2.5) or degrees (NAMD 2.5)  */
3465                         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) {
3466                                 dcdcell[4] = cos(dcdcell[4] * kDeg2Rad);  /*  cos(alpha)  */
3467                                 dcdcell[3] = cos(dcdcell[3] * kDeg2Rad);  /*  cos(beta)  */
3468                                 dcdcell[1] = cos(dcdcell[1] * kDeg2Rad);  /*  cos(gamma)  */
3469                         }
3470                         /*  a axis lies along the cartesian x axis  */
3471                         sing = sqrt(1 - dcdcell[1] * dcdcell[1]);
3472                         vpp[0].x = dcdcell[0];
3473                         vpp[0].y = 0;
3474                         vpp[0].z = 0;
3475                         vpp[1].x = dcdcell[2] * dcdcell[1];
3476                         vpp[1].y = dcdcell[2] * sing;
3477                         vpp[1].z = 0;
3478                         vpp[2].x = dcdcell[5] * dcdcell[3];
3479                         vpp[2].y = dcdcell[5] * (dcdcell[4] - dcdcell[3] * dcdcell[1]) / sing;
3480                         vpp[2].z = sqrt(dcdcell[5] * dcdcell[5] - vpp[2].x * vpp[2].x - vpp[2].y * vpp[2].y);
3481                         vpp[3].x = vpp[3].y = vpp[3].z = 0.0;
3482                         if (mp->cell == NULL) {
3483                                 /*  Create periodicity if not present  */
3484                                 MolActionCreateAndPerform(mp, gMolActionSetBox, &vpp[0], &vpp[1], &vpp[2], &vpp[3], 7, 0);
3485                         }
3486                 }
3487         }
3488         if (MolActionCreateAndPerform(mp, gMolActionInsertFrames, ig, mp->natoms * dcd.nframes, vp, (cp == NULL ? 0 : dcd.nframes * 4), cp) != 0)
3489                 snprintf(errbuf, errbufsize, "Cannot insert frames");
3490         mp->startStep = dcd.nstart;
3491         mp->stepsPerFrame = dcd.ninterval;
3492         mp->psPerStep = dcd.delta;
3493 exit:
3494         DcdClose(&dcd);
3495         if (cp != NULL)
3496                 free(cp);
3497         free(vp);
3498         free(xp);
3499         free(yp);
3500         free(zp);
3501         IntGroupRelease(ig);
3502         if (errbuf[0] == 0)
3503                 return 0;
3504         else return 1;
3505 }
3506
3507 int
3508 MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3509 {
3510         FILE *fp;
3511         char buf[1024];
3512         int lineNumber;
3513         int i, retval;
3514         Vector v[3], vv;
3515         double d[3];
3516         int n, flag;
3517         char flags[3];
3518         fp = fopen(fname, "rb");
3519         if (fp == NULL) {
3520                 snprintf(errbuf, errbufsize, "Cannot open file");
3521                 return -1;
3522         }
3523         errbuf[0] = 0;
3524         lineNumber = 0;
3525         retval = 0;
3526         flags[0] = flags[1] = flags[2] = 0;
3527         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3528                 if (strncmp(buf, "Bounding box:", 13) == 0) {
3529                         for (i = 0; i < 3; i++) {
3530                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3531                                         snprintf(errbuf, errbufsize, "line %d: missing %d component of the bounding box", lineNumber, i + 1);
3532                                         retval = 1;
3533                                         goto abort;
3534                                 }
3535                                 n = sscanf(buf, "%lf %lf %lf %d", &d[0], &d[1], &d[2], &flag);
3536                                 if (n < 3) {
3537                                         vv.x = vv.y = vv.z = 0.0;
3538                                         switch (i) {
3539                                                 case 0: vv.x = d[0]; break;
3540                                                 case 1: vv.y = d[0]; break;
3541                                                 case 2: vv.z = d[0]; break;
3542                                         }
3543                                         if (n == 1 || (n == 2 && d[1] != 0.0))
3544                                                 flags[i] = 1;
3545                                 } else {
3546                                         vv.x = d[0];
3547                                         vv.y = d[1];
3548                                         vv.z = d[2];
3549                                         if (n == 4)
3550                                                 flags[i] = (flag != 0);
3551                                         else
3552                                                 flags[i] = (VecLength2(vv) != 0);
3553                                 }
3554                                 v[i] = vv;
3555                         }
3556                         if (mp->cell != NULL)
3557                                 vv = mp->cell->origin;
3558                         else
3559                                 vv.x = vv.y = vv.z = 0.0;
3560                         MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
3561                 } else if (strncmp(buf, "Bounding box origin:", 20) == 0) {
3562                         if (mp->cell != NULL) {
3563                                 v[0] = mp->cell->axes[0];
3564                                 v[1] = mp->cell->axes[1];
3565                                 v[2] = mp->cell->axes[2];
3566                                 memmove(flags, mp->cell->flags, 3);
3567                         } else {
3568                                 v[0].x = 1.0; v[0].y = v[0].z = 0.0;
3569                                 v[1].y = 1.0; v[1].x = v[1].z = 0.0;
3570                                 v[2].z = 1.0; v[2].x = v[2].y = 0.0;
3571                                 flags[0] = flags[1] = flags[2] = 1.0;
3572                         }
3573                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0 || (n = sscanf(buf, "%lf %lf %lf", &d[0], &d[1], &d[2]) < 3)) {
3574                                 snprintf(errbuf, errbufsize, "line %d: wrong format for the bounding box origin", lineNumber);
3575                                 retval = 1;
3576                                 goto abort;
3577                         }
3578                         vv.x = d[0];
3579                         vv.y = d[1];
3580                         vv.z = d[2];
3581                         MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
3582                 }
3583         }
3584         fclose(fp);
3585         return 0;
3586 abort:
3587         if (fp != NULL)
3588                 fclose(fp);
3589         return retval;
3590 }
3591                         
3592 int
3593 MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
3594 {
3595         int retval;
3596         if (ftype == NULL || *ftype == 0) {
3597                 const char *cp;
3598                 cp = strrchr(fname, '.');
3599                 if (cp != NULL)
3600                         ftype = cp + 1;
3601                 else {
3602                         cp = guessMoleculeType(fname);
3603                         if (strcmp(cp, "???") != 0)
3604                                 ftype = cp;
3605                 }
3606         }
3607         if (strcasecmp(ftype, "psf") == 0) {
3608                 retval = MoleculeWriteToPsfFile(mp, fname, errbuf, errbufsize);
3609         } else if (strcasecmp(ftype, "pdb") == 0) {
3610                 retval = MoleculeWriteToPdbFile(mp, fname, errbuf, errbufsize);
3611         } else if (strcasecmp(ftype, "tep") == 0) {
3612                 retval = MoleculeWriteToTepFile(mp, fname, errbuf, errbufsize);
3613         } else {
3614                 snprintf(errbuf, errbufsize, "The file format should be specified");
3615                 retval = 1;
3616         }
3617         if (retval == 0)
3618                 MoleculeSetPath(mp, fname);
3619         return retval;
3620 }
3621
3622 int
3623 MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3624 {
3625         FILE *fp;
3626         int i, j, k, n1, n2, n3, n_aniso, nframes;
3627         Atom *ap;
3628         char bufs[6][8];
3629
3630         fp = fopen(fname, "wb");
3631         if (fp == NULL) {
3632                 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
3633                 return 1;
3634         }
3635         errbuf[0] = 0;
3636
3637         nframes = MoleculeFlushFrames(mp);
3638
3639         fprintf(fp, "!:atoms\n");
3640         fprintf(fp, "! idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge\n");
3641         n1 = n2 = n3 = n_aniso = 0;
3642         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3643                 strncpy(bufs[0], ap->segName, 4);
3644                 bufs[0][4] = 0;
3645                 strncpy(bufs[1], ap->resName, 4);
3646                 bufs[1][4] = 0;
3647                 strncpy(bufs[2], ap->aname, 4);
3648                 bufs[2][4] = 0;
3649                 AtomTypeDecodeToString(ap->type, bufs[3]);
3650                 bufs[3][6] = 0;
3651                 strncpy(bufs[4], ap->element, 4);
3652                 bufs[4][2] = 0;
3653                 for (j = 0; j < 5; j++) {
3654                         if (bufs[j][0] == 0) {
3655                                 bufs[j][0] = '_';
3656                                 bufs[j][1] = 0;
3657                         }
3658                         for (k = 0; k < 6; k++) {
3659                                 if (bufs[j][k] == 0)
3660                                         break;
3661                                 if (bufs[j][k] > 0 && bufs[j][k] < ' ')
3662                                         bufs[j][k] = '_';
3663                         }
3664                 }
3665                 if (SYMOP_ALIVE(ap->symop))
3666                         n1++;
3667                 if (ap->fix_force != 0)
3668                         n2++;
3669                 if (ap->mm_exclude || ap->periodic_exclude)
3670                         n3++;
3671                 if (ap->aniso != NULL)
3672                         n_aniso++;
3673                 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);
3674         }
3675         fprintf(fp, "\n");
3676         
3677         if (n1 > 0) {
3678                 fprintf(fp, "!:atoms_symop\n");
3679                 fprintf(fp, "! idx symop symbase\n");
3680                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3681                         int n;
3682                         n = ap->symop.sym * 1000000 + ap->symop.dx * 10000 + ap->symop.dy * 100 + ap->symop.dz;
3683                         fprintf(fp, "%d %d %d\n", i, n, ap->symbase);
3684                 }
3685                 fprintf(fp, "\n");
3686         }
3687         
3688         if (n2 > 0) {
3689                 fprintf(fp, "!:atoms_fix\n");
3690                 fprintf(fp, "! idx fix_force fix_pos\n");
3691                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3692                         fprintf(fp, "%d %f %f %f %f\n", i, ap->fix_force, ap->fix_pos.x, ap->fix_pos.y, ap->fix_pos.z);
3693                 }
3694                 fprintf(fp, "\n");
3695         }
3696         
3697         if (n3 > 0) {
3698                 fprintf(fp, "!:mm_exclude\n");
3699                 fprintf(fp, "! idx mm_exclude periodic_exclude\n");
3700                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3701                         fprintf(fp, "%d %d %d\n", i, ap->mm_exclude, ap->periodic_exclude);
3702                 }
3703                 fprintf(fp, "\n");
3704         }
3705         
3706         n1 = nframes;
3707         if (n1 > 0)
3708                 n2 = mp->cframe;
3709         else
3710                 n2 = 0;
3711         for (i = 0; (i == n2 || i < n1); i++) {
3712                 fprintf(fp, "!:positions ; frame %d\n", i);
3713                 fprintf(fp, "! idx x y z [sx sy sz]\n");
3714                 for (j = 0, ap = mp->atoms; j < mp->natoms; j++, ap = ATOM_NEXT(ap)) {
3715                         Vector *vp;
3716                         Byte sig_flag = 0;
3717                         if (i != n2 && i < ap->nframes)
3718                                 vp = ap->frames + i;
3719                         else {
3720                                 vp = &(ap->r);
3721                                 if (ap->sigma.x != 0.0 || ap->sigma.y != 0.0 || ap->sigma.z != 0.0)
3722                                         sig_flag = 1;
3723                         }
3724                         fprintf(fp, "%d %.8f %.8f %.8f", j, vp->x, vp->y, vp->z);
3725                         if (sig_flag) {
3726                                 fprintf(fp, " %.8f %.8f %.8f", ap->sigma.x, ap->sigma.y, ap->sigma.z);
3727                         }
3728                         fprintf(fp, "\n");
3729                 }
3730                 fprintf(fp, "\n");
3731         }
3732         
3733         if (mp->nbonds > 0) {
3734                 fprintf(fp, "!:bonds\n");
3735                 fprintf(fp, "! from1 to1 from2 to2 from3 to3 from4 to4\n");
3736                 for (i = 0; i < mp->nbonds; i++) {
3737                         fprintf(fp, "%d %d%c", mp->bonds[i * 2], mp->bonds[i * 2 + 1], (i % 4 == 3 || i == mp->nbonds - 1 ? '\n' : ' '));
3738                 }
3739                 fprintf(fp, "\n");
3740         }
3741         
3742         if (mp->nangles > 0) {
3743                 fprintf(fp, "!:angles\n");
3744                 fprintf(fp, "! a1 b1 c1 a2 b2 c2 a3 b3 c3\n");
3745                 for (i = 0; i < mp->nangles; i++) {
3746                         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' : ' '));
3747                 }
3748                 fprintf(fp, "\n");
3749         }
3750         
3751         if (mp->ndihedrals > 0) {
3752                 fprintf(fp, "!:dihedrals\n");
3753                 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3754                 for (i = 0; i < mp->ndihedrals; i++) {
3755                         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' : ' '));
3756                 }
3757                 fprintf(fp, "\n");
3758         }
3759         
3760         if (mp->nimpropers > 0) {
3761                 fprintf(fp, "!:impropers\n");
3762                 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3763                 for (i = 0; i < mp->nimpropers; i++) {
3764                         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' : ' '));
3765                 }
3766                 fprintf(fp, "\n");
3767         }
3768         
3769         if (mp->cell != NULL) {
3770                 fprintf(fp, "!:xtalcell\n");
3771                 fprintf(fp, "! a b c alpha beta gamma\n");
3772                 fprintf(fp, "! This information is redundant and overridden by the following periodic_box info\n");
3773                 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]);
3774                 fprintf(fp, "\n");
3775
3776                 fprintf(fp, "!:periodic_box\n");
3777                 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");
3778                 for (i = 0; i < 3; i++)
3779                         fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->axes[i].x, mp->cell->axes[i].y, mp->cell->axes[i].z);
3780                 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->origin.x, mp->cell->origin.y, mp->cell->origin.z);
3781                 fprintf(fp, "%d %d %d%s\n", mp->cell->flags[0], mp->cell->flags[1], mp->cell->flags[2], (mp->cell->has_sigma ? " 1" : ""));
3782                 if (mp->cell->has_sigma) {
3783                         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]);
3784                 }
3785                 fprintf(fp, "\n");
3786         }
3787         
3788         if (mp->useFlexibleCell != 0) {
3789                 fprintf(fp, "!:frame_periodic_boxes\n");
3790                 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz\n");
3791                 for (i = 0; i < mp->nframe_cells * 4; i++) {
3792                         fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->frame_cells[i].x, mp->frame_cells[i].y, mp->frame_cells[i].z);
3793                 }
3794                 fprintf(fp, "\n");
3795         }
3796         
3797         if (mp->nsyms > 0) {
3798                 fprintf(fp, "!:symmetry_operations\n");
3799                 fprintf(fp, "! a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3\n");
3800                 for (i = 0; i < mp->nsyms; i++) {
3801                         Transform *tp = mp->syms + i;
3802                         const unsigned char s_index_order[12] = {0, 3, 6, 1, 4, 7, 2, 5, 8, 9, 10, 11};
3803                         for (j = 0; j < 12; j++)
3804                                 fprintf(fp, "%11.6f%c", (*tp)[s_index_order[j]], (j % 3 == 2 ? '\n' : ' '));
3805                 }
3806                 fprintf(fp, "\n");
3807         }
3808         
3809         if (n_aniso > 0) {
3810                 fprintf(fp, "!:anisotropic_thermal_parameters\n");
3811                 fprintf(fp, "! b11 b22 b33 b12 b13 b23 [sigma; sb11 sb22 sb33 sb12 sb13 sb23]\n");
3812                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3813                         if (ap->aniso != NULL) {
3814                                 Double *bp = ap->aniso->bij;
3815                                 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" : ""));
3816                                 if (ap->aniso->has_bsig) {
3817                                         bp = ap->aniso->bsig;
3818                                         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]);
3819                                 }
3820                         } else {
3821                                 fprintf(fp, "0 0 0 0 0 0\n");
3822                         }
3823                 }
3824                 fprintf(fp, "\n");              
3825         }
3826         
3827         if (mp->arena != NULL) {
3828                 MDArena *arena = mp->arena;
3829                 fprintf(fp, "!:md_parameters\n");
3830                 fprintf(fp, "log_file %s\n", arena->log_result_name);
3831                 fprintf(fp, "coord_file %s\n", arena->coord_result_name);
3832                 fprintf(fp, "vel_file %s\n", arena->vel_result_name);
3833                 fprintf(fp, "force_file %s\n", arena->force_result_name);
3834                 fprintf(fp, "debug_file %s\n", arena->debug_result_name);
3835                 fprintf(fp, "debug_output_level %d\n", arena->debug_output_level);
3836                 fprintf(fp, "step %d\n", arena->step);
3837                 fprintf(fp, "coord_output_freq %d\n", arena->coord_output_freq);
3838                 fprintf(fp, "energy_output_freq %d\n", arena->energy_output_freq);
3839                 fprintf(fp, "coord_frame %d\n", arena->coord_result_frame);
3840                 fprintf(fp, "timestep %g\n", arena->timestep);
3841                 fprintf(fp, "cutoff %g\n", arena->cutoff);
3842                 fprintf(fp, "electro_cutoff %g\n", arena->electro_cutoff);
3843                 fprintf(fp, "pairlist_distance %g\n", arena->pairlist_distance);
3844                 fprintf(fp, "temperature %g\n", arena->temperature);
3845                 fprintf(fp, "andersen_freq %d\n", arena->andersen_thermo_freq);
3846                 fprintf(fp, "andersen_coupling %g\n", arena->andersen_thermo_coupling);
3847                 fprintf(fp, "random_seed %d\n", arena->random_seed);
3848                 fprintf(fp, "dielectric %g\n", arena->dielectric);
3849                 fprintf(fp, "gradient_convergence %g\n", arena->gradient_convergence);
3850                 fprintf(fp, "coordinate_convergence %g\n", arena->coordinate_convergence);
3851                 fprintf(fp, "use_xplor_shift %d\n", arena->use_xplor_shift);
3852                 fprintf(fp, "scale14_vdw %g\n", arena->scale14_vdw);
3853                 fprintf(fp, "scale14_elect %g\n", arena->scale14_elect);
3854                 fprintf(fp, "relocate_center %d\n", arena->relocate_center);
3855                 fprintf(fp, "surface_probe_radius %g\n", arena->probe_radius);
3856                 fprintf(fp, "surface_tension %g\n", arena->surface_tension);
3857                 fprintf(fp, "surface_potential_freq %d\n", arena->surface_potential_freq);
3858                 fprintf(fp, "use_graphite %d\n", arena->use_graphite);
3859                 fprintf(fp, "alchemical_lambda %g\n", arena->alchem_lambda);
3860                 fprintf(fp, "alchemical_delta_lambda %g\n", arena->alchem_dlambda);
3861                 if (arena->nalchem_flags > 0) {
3862                         fprintf(fp, "alchem_flags %d", arena->nalchem_flags);
3863                         for (i = 0; i < arena->nalchem_flags; i++) {
3864                                 if (i % 60 == 0)
3865                                         fputc('\n', fp);
3866                                 else if (i % 10 == 0)
3867                                         fputc(' ', fp);
3868                                 fputc('0' + arena->alchem_flags[i], fp);
3869                         }
3870                         fputc('\n', fp);
3871                 }
3872                 if (arena->pressure != NULL) {
3873                         Double *dp;
3874                         fprintf(fp, "pressure_freq %d\n", arena->pressure->freq);
3875                         fprintf(fp, "pressure_coupling %g\n", arena->pressure->coupling);
3876                         dp = arena->pressure->apply;
3877                         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]);
3878                         dp = arena->pressure->cell_flexibility;
3879                         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]);
3880                         fprintf(fp, "pressure_fluctuate_cell_origin %g\n", arena->pressure->fluctuate_cell_origin);
3881                         fprintf(fp, "pressure_fluctuate_cell_orientation %g\n", arena->pressure->fluctuate_cell_orientation);
3882                 }
3883                 fprintf(fp, "\n");
3884
3885                 if (mp->par != NULL) {
3886                         Parameter *par = mp->par;
3887                         fprintf(fp, "!:parameters\n");
3888                         ParameterAppendToFile(par, fp);
3889                         fprintf(fp, "\n");
3890                 }
3891                 
3892                 fprintf(fp, "!:velocity\n");
3893                 fprintf(fp, "! idx vx vy vz\n");
3894                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3895                         fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->v.x, ap->v.y, ap->v.z);
3896                 }
3897                 fprintf(fp, "\n");
3898
3899                 fprintf(fp, "!:force\n");
3900                 fprintf(fp, "! idx fx fy fz\n");
3901                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3902                         fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->f.x, ap->f.y, ap->f.z);
3903                 }
3904                 fprintf(fp, "\n");
3905         }
3906         
3907         if (mp->mview != NULL) {
3908                 float f[4];
3909                 if (mp->mview->track != NULL) {
3910                         fprintf(fp, "!:trackball\n");
3911                         fprintf(fp, "! scale; trx try trz; theta_deg x y z\n");
3912                         f[0] = TrackballGetScale(mp->mview->track);
3913                         fprintf(fp, "%f\n", f[0]);
3914                         TrackballGetTranslate(mp->mview->track, f);
3915                         fprintf(fp, "%f %f %f\n", f[0], f[1], f[2]);
3916                         TrackballGetRotate(mp->mview->track, f);
3917                         fprintf(fp, "%f %f %f %f\n", f[0], f[1], f[2], f[3]);
3918                         fprintf(fp, "\n");
3919                 }
3920                 fprintf(fp, "!:view\n");
3921                 fprintf(fp, "show_unit_cell %d\n", mp->mview->showUnitCell);
3922                 fprintf(fp, "show_periodic_box %d\n", mp->mview->showPeriodicBox);
3923                 fprintf(fp, "show_expanded_atoms %d\n", mp->mview->showExpandedAtoms);
3924                 fprintf(fp, "show_ellipsoids %d\n", mp->mview->showEllipsoids);
3925                 fprintf(fp, "show_hydrogens %d\n", mp->mview->showHydrogens);
3926                 fprintf(fp, "show_dummy_atoms %d\n", mp->mview->showDummyAtoms);
3927                 fprintf(fp, "show_rotation_center %d\n", mp->mview->showRotationCenter);
3928                 fprintf(fp, "show_graphite_flag %d\n", mp->mview->showGraphiteFlag);
3929                 fprintf(fp, "show_graphite %d\n", mp->mview->showGraphite);
3930                 fprintf(fp, "show_periodic_image_flag %d\n", mp->mview->showPeriodicImageFlag);
3931                 fprintf(fp, "show_periodic_image %d %d %d %d %d %d\n",
3932                                 mp->mview->showPeriodicImage[0], mp->mview->showPeriodicImage[1],
3933                                 mp->mview->showPeriodicImage[2], mp->mview->showPeriodicImage[3],
3934                                 mp->mview->showPeriodicImage[4], mp->mview->showPeriodicImage[5]);
3935                 fprintf(fp, "\n");
3936         }
3937
3938         fclose(fp);
3939         return 0;
3940 }
3941
3942 int
3943 MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3944 {
3945         FILE *fp;
3946         int i;
3947         Atom *ap;
3948         fp = fopen(fname, "wb");
3949         if (fp == NULL) {
3950                 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
3951                 return 1;
3952         }
3953         errbuf[0] = 0;
3954         fprintf(fp, "PSF\n\n");
3955         fprintf(fp, "       1 !NTITLE\n");
3956         fprintf(fp, " REMARKS FILENAME=\n");
3957         fprintf(fp, "\n");
3958         
3959         /*  Atoms  */
3960         fprintf(fp, "%8d !NATOM\n", mp->natoms);
3961         for (i = 0; i < mp->natoms; i++) {
3962                 const char *fmt;
3963                 ap = ATOM_AT_INDEX(mp->atoms, i);
3964                 fprintf(fp, "%8d ", i + 1);
3965                 if (ap->resSeq >= 10000) {
3966                         fmt = "%-3.3s %-5d ";
3967                 } else {
3968                         fmt = "%-4.4s %-4d ";
3969                 }
3970                 fprintf(fp, fmt, ap->segName, ap->resSeq);
3971                 fprintf(fp, "%-3.3s  %-4.4s %-4.4s   %12.6f  %8.4f           0\n",
3972                         ap->resName, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->charge, ap->weight);
3973         }
3974         fprintf(fp, "\n");
3975         
3976         /*  Bonds  */
3977         fprintf(fp, "%8d !NBOND: bonds\n", mp->nbonds);
3978         for (i = 0; i < mp->nbonds * 2; i++) {
3979                 fprintf(fp, "%8d", mp->bonds[i] + 1);
3980                 if (i % 8 == 7)
3981                         fprintf(fp, "\n");
3982         }
3983         if (i % 8 != 0)
3984                 fprintf(fp, "\n");
3985         fprintf(fp, "\n");
3986         
3987         /*  Angles  */
3988         fprintf(fp, "%8d !NTHETA: angles\n", mp->nangles);
3989         for (i = 0; i < mp->nangles * 3; i++) {
3990                 fprintf(fp, "%8d", mp->angles[i] + 1);
3991                 if (i % 9 == 8)
3992                         fprintf(fp, "\n");
3993         }
3994         if (i % 9 != 0)
3995                 fprintf(fp, "\n");
3996         fprintf(fp, "\n");
3997         
3998         /*  Dihedrals  */
3999         fprintf(fp, "%8d !NPHI: dihedrals\n", mp->ndihedrals);
4000         for (i = 0; i < mp->ndihedrals * 4; i++) {
4001                 fprintf(fp, "%8d", mp->dihedrals[i] + 1);
4002                 if (i % 8 == 7)
4003                         fprintf(fp, "\n");
4004         }
4005         if (i % 8 != 0)
4006                 fprintf(fp, "\n");
4007         fprintf(fp, "\n");
4008         
4009         /*  Dihedrals  */
4010         fprintf(fp, "%8d !NIMPHI: impropers\n", mp->nimpropers);
4011         for (i = 0; i < mp->nimpropers * 4; i++) {
4012                 fprintf(fp, "%8d", mp->impropers[i] + 1);
4013                 if (i % 8 == 7)
4014                         fprintf(fp, "\n");
4015         }
4016         if (i % 8 != 0)
4017                 fprintf(fp, "\n");
4018         fprintf(fp, "\n");
4019         
4020         fprintf(fp, "%8d !NDON: donors\n\n", 0);
4021         fprintf(fp, "%8d !NACC: acceptors\n\n", 0);
4022         fprintf(fp, "%8d !NNB: non-bonding exclusions\n\n", 0);
4023         for (i = 0; i < mp->natoms; i++) {
4024                 fprintf(fp, "%8d", 0);
4025                 if (i % 8 == 7)
4026                         fprintf(fp, "\n");
4027         }
4028         if (i % 8 != 0)
4029                 fprintf(fp, "\n");
4030         fprintf(fp, "\n");
4031         fprintf(fp, "%8d !NGRP: groups\n", 1);
4032         fprintf(fp, "       0       0       0\n");
4033         fprintf(fp, "\n");
4034         
4035         i = strlen(fname);
4036         if (i > 5 && strcmp(fname + i - 5, ".psfx") == 0) {
4037                 /*  Extended psf (with coordinates and other info)  */
4038                 fprintf(fp, "%8d !COORD: coordinates\n", mp->natoms);
4039                 for (i = 0; i < mp->natoms; i++) {
4040                         Vector r;
4041                         ap = ATOM_AT_INDEX(mp->atoms, i);
4042                         r = ap->r;
4043                         fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->aname);
4044                 }
4045                 fprintf(fp, "\n");
4046 #if 0
4047                 if (mp->nframes > 0) {
4048                         int fn;  /*  Frame number  */
4049                         for (fn = 0; fn < ap->nframes; fn++) {
4050                                 fprintf(fp, "%8d !COORD: coordinates for frame %d\n", mp->natoms, fn);
4051                                 for (i = 0; i < mp->natoms; i++) {
4052                                         Vector r;
4053                                         ap = ATOM_AT_INDEX(mp->atoms, i);
4054                                         if (ap->frames == NULL || fn >= ap->nframes)
4055                                                 r = ap->r;
4056                                         else
4057                                                 r = ap->frames[fn];
4058                                         fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->name);
4059                                 }
4060                                 fprintf(fp, "\n");
4061                         }
4062                 }
4063 #endif
4064         }
4065                 
4066         fclose(fp);
4067         return 0;
4068 }
4069
4070 int
4071 MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
4072 {
4073         FILE *fp;
4074         int i, j;
4075         Atom *ap;
4076         fp = fopen(fname, "wb");
4077         if (fp == NULL) {
4078                 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
4079                 return 1;
4080         }
4081         errbuf[0] = 0;
4082         for (i = 0; i < mp->natoms; i++) {
4083                 char buf[6];
4084                 ap = ATOM_AT_INDEX(mp->atoms, i);
4085                 if (ap->resSeq >= 10000) {
4086                         snprintf(buf, sizeof buf, "%c%03d", 'A' + (ap->resSeq - 10000) / 1000, ap->resSeq % 1000);
4087                 } else {
4088                         snprintf(buf, sizeof buf, "%4d", ap->resSeq);
4089                 }
4090                 fprintf(fp, "ATOM  %5d %-4.4s%1.1s%-3.3s %1.1s%4.4s%1.1s   "
4091                                         "%8.3f%8.3f%8.3f %5.2f %5.2f      "
4092                                         "%-4.4s%-2.2s%-2d\n",
4093                         i + 1, ap->aname, " ", ap->resName, " ", buf, " ",
4094                         ap->r.x, ap->r.y, ap->r.z, ap->occupancy, ap->tempFactor,
4095                         ap->segName, ap->element, ap->intCharge);
4096         }
4097         for (i = 0; i < mp->natoms; i++) {
4098                 Int *cp;
4099                 ap = ATOM_AT_INDEX(mp->atoms, i);
4100                 cp = AtomConnectData(&ap->connect);
4101                 for (j = 0; j < ap->connect.count; j++) {
4102                         if (j % 4 == 0) {
4103                                 if (j > 0)
4104                                         fprintf(fp, "\n");
4105                                 fprintf(fp, "CONECT%5d", i + 1);
4106                         }
4107                         fprintf(fp, "%5d", cp[j] + 1);
4108                 }
4109                 if (j > 0)
4110                         fprintf(fp, "\n");
4111         }
4112         fprintf(fp, "END\n");
4113         fclose(fp);
4114         return 0;
4115 }
4116
4117 int
4118 MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
4119 {
4120         DcdRecord dcd;
4121         SFloat32 *xp, *yp, *zp;
4122         int n;
4123         errbuf[0] = 0;
4124         if (mp == NULL || mp->natoms == 0) {
4125                 snprintf(errbuf, errbufsize, "Molecule is empty");
4126                 return 1;
4127         }
4128         memset(&dcd, 0, sizeof(dcd));
4129         dcd.natoms = mp->natoms;
4130         dcd.nframes = MoleculeGetNumberOfFrames(mp);
4131         if (dcd.nframes == 0) {
4132                 snprintf(errbuf, errbufsize, "no frame is present");
4133                 return 1;
4134         }
4135         dcd.nstart = mp->startStep;
4136         dcd.ninterval = mp->stepsPerFrame;
4137         if (dcd.ninterval == 0)
4138                 dcd.ninterval = 1;
4139         dcd.nend = dcd.nstart + (dcd.nframes - 1) * dcd.ninterval;
4140         if (mp->cell != NULL)
4141                 dcd.nextra = 1;
4142         dcd.delta = mp->psPerStep;
4143         if (dcd.delta == 0.0)
4144                 dcd.delta = 1.0;
4145         dcd.ncharmver = 24;
4146         n = DcdCreate(fname, &dcd);
4147         if (n != 0) {
4148                 if (n < 0)
4149                         snprintf(errbuf, errbufsize, "Cannot create dcd file");
4150                 else
4151                         snprintf(errbuf, errbufsize, "Cannot write dcd header");
4152                 DcdClose(&dcd);
4153                 return 1;
4154         }
4155         
4156         xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4157         yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4158         zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4159         if (xp == NULL || yp == NULL || zp == NULL) {
4160                 snprintf(errbuf, errbufsize, "Cannot allocate memory");
4161                 if (xp) free(xp);
4162                 if (yp) free(yp);
4163                 if (zp) free(zp);
4164                 DcdClose(&dcd);
4165                 return 1;
4166         }
4167         for (n = 0; n < dcd.nframes; n++) {
4168                 int i;
4169                 Atom *ap;
4170                 for (i = 0, ap = mp->atoms; i < dcd.natoms && mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4171                         Vector r;
4172                         if (ap->frames == NULL || n >= ap->nframes)
4173                                 r = ap->r;
4174                         else
4175                                 r = ap->frames[n];
4176                         xp[i] = r.x;
4177                         yp[i] = r.y;
4178                         zp[i] = r.z;
4179                 }
4180                 if (i < dcd.natoms) {
4181                         size_t sz = (dcd.natoms - i) * sizeof(SFloat32);
4182                         memset(xp + i, 0, sz);
4183                         memset(yp + i, 0, sz);
4184                         memset(zp + i, 0, sz);
4185                 }
4186                 if (n < mp->nframe_cells && mp->frame_cells != NULL) {
4187                         Vector *cp = &(mp->frame_cells[n * 4]);
4188                         dcd.globalcell[0] = VecLength(cp[0]);
4189                         dcd.globalcell[2] = VecLength(cp[1]);
4190                         dcd.globalcell[5] = VecLength(cp[2]);
4191                         dcd.globalcell[1] = VecDot(cp[0], cp[1]) / (dcd.globalcell[0] * dcd.globalcell[2]);
4192                         dcd.globalcell[3] = VecDot(cp[0], cp[2]) / (dcd.globalcell[0] * dcd.globalcell[5]);
4193                         dcd.globalcell[4] = VecDot(cp[1], cp[2]) / (dcd.globalcell[2] * dcd.globalcell[5]);                     
4194                 }                       
4195                 if (DcdWriteFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
4196                         snprintf(errbuf, errbufsize, "Write error in dcd file");
4197                         goto exit;
4198                 }
4199         }
4200         
4201 exit:
4202         DcdClose(&dcd);
4203         free(xp);
4204         free(yp);
4205         free(zp);
4206         if (errbuf[0] == 0)
4207                 return 0;
4208         else return 1;
4209 }
4210
4211 int
4212 MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
4213 {
4214         FILE *fp;
4215         int i;
4216         Vector v;
4217         errbuf[0] = 0;
4218         fp = fopen(fname, "wb");
4219         if (fp == NULL) {
4220                 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
4221                 return 1;
4222         }
4223         if (mp->cell != NULL) {
4224                 fprintf(fp, "Bounding box:\n");
4225                 for (i = 0; i < 3; i++) {
4226                         v = mp->cell->axes[i];
4227                         fprintf(fp, "%.3f %.3f %.3f %d\n", v.x, v.y, v.z, mp->cell->flags[i]);
4228                 }
4229                 fprintf(fp, "Bounding box origin:\n");
4230                 v = mp->cell->origin;
4231                 fprintf(fp, "%.3f %.3f %.3f\n", v.x, v.y, v.z);
4232         }
4233         fclose(fp);
4234         return 0;
4235 }
4236                 
4237  static int
4238 sCompareByElement(const void *ap, const void *bp)
4239 {
4240         return ((*(Atom **)bp)->atomicNumber - (*(Atom **)ap)->atomicNumber);
4241 }
4242
4243 static int
4244 sMakeAdc(int n, int base, Symop symop)
4245 {
4246         int an, sym;
4247         if (SYMOP_ALIVE(symop)) {
4248                 an = base;
4249                 sym = (symop.dx + 5) * 10000 + (symop.dy + 5) * 1000 + (symop.dz + 5) * 100 + symop.sym + 1;
4250         } else {
4251                 an = n;
4252                 sym = 55501;
4253         }
4254         return (an + 1) * 100000 + sym;
4255 }
4256
4257 static int
4258 sCompareAdc(const void *ap, const void *bp)
4259 {
4260         int n = *((Int *)ap) % 100000 - *((Int *)bp) % 100000;
4261         if (n == 0)
4262                 n = *((Int *)ap) / 100000 - *((Int *)bp) / 100000;
4263         return n;
4264 }
4265
4266 static void
4267 sOutputAtomListInstructions(FILE *fp, int natoms, Atom *atoms)
4268 {
4269         int i, j, k, an, sym;
4270         Atom *ap;
4271         Int *adc;
4272         adc = (Int *)malloc(sizeof(Int) * natoms);
4273         if (adc == NULL)
4274                 return;
4275         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4276                 if (ap->exflags & kAtomHiddenFlag)
4277                         continue;
4278                 adc[i] = sMakeAdc(i, ap->symbase, ap->symop);
4279         }
4280         mergesort(adc, natoms, sizeof(Int), sCompareAdc);
4281         
4282         /*  Create the atom list  */
4283         an = sym = -1;
4284         for (i = j = k = 0; i < natoms; i++) {
4285                 int an1 = adc[i] / 100000;
4286                 int sym1 = adc[i] % 100000;
4287                 if (sym == sym1 && an1 == an + 1) {
4288                         /*  Continuous  */
4289                         an = an1;
4290                         k++;
4291                         continue;
4292                 }
4293                 if (k > 0)
4294                         /*  Output the last atom with a minus sign  */
4295                         adc[j++] = -(an * 100000 + sym);
4296                 /*  Output this atom  */
4297                 adc[j++] = adc[i];
4298                 an = an1;
4299                 sym = sym1;
4300                 k = 0;
4301         }
4302         if (k > 0)
4303                 adc[j++] = -(an * 100000 + sym);
4304         
4305         /*  Create the instruction cards  */
4306         for (i = k = 0; i < j; i++) {
4307                 if (k == 0)
4308                         fprintf(fp, "      401");
4309                 fprintf(fp, "%9d", adc[i]);
4310                 k++;
4311                 if (i == j - 1 || k == 6 || (k == 5 && i < j - 2 && adc[i + 2] < 0)) {
4312                         fprintf(fp, "\n");
4313                         k = 0;
4314                 }
4315         }
4316         free(adc);
4317 }
4318
4319 static int
4320 sEllipsoidType(int an)
4321 {
4322         return (an >= 18 ? 3 : (an >= 2 && an != 6 ? 2 : (an > 0 ? 1 : 0)));
4323 }
4324
4325 static void
4326 sOutputAtomTypeInstructions(FILE *fp, int natoms, Atom *atoms)
4327 {
4328         int i;
4329         Atom *ap;
4330         int etype, elast, istart, ilast, n1, n2;
4331         elast = istart = ilast = -1;
4332         for (i = 0, ap = atoms; i <= natoms; i++, ap++) {
4333                 if (i < natoms) {
4334                         if (SYMOP_ALIVE(ap->symop))
4335                                 continue;
4336                         if (ap->exflags & kAtomHiddenFlag)
4337                                 continue;
4338                         etype = sEllipsoidType(ap->atomicNumber);
4339                         if (elast < 0) {
4340                                 istart = ilast = i;
4341                                 elast = etype;
4342                                 continue;
4343                         } else if (elast == etype && ilast == i - 1) {
4344                                 ilast++;
4345                                 continue;
4346                         }
4347                 }
4348                 /*  Output the instruction card for the 'last' block of atoms  */
4349                 switch (etype) {
4350                         case 2:
4351                                 n1 = 4; n2 = 0; break;
4352                         case 3:
4353                                 n1 = 4; n2 = 5; break;
4354                         default:
4355                                 n1 = 1; n2 = 0; break;
4356                 }
4357                 fprintf(fp, "  1   715 %8d        0 %8d        0    0.100    0.000    0.000\n", n1, n2);
4358                 fprintf(fp, "                           %9d%9d\n", istart + 1, ilast + 1);
4359                 elast = etype;
4360                 ilast = istart = i;
4361         }
4362 }
4363
4364 static int
4365 sCompareBondType(const void *ap, const void *bp)
4366 {
4367         /*  Descending order  */
4368         return *((int *)bp) - *((int *)ap);
4369 }
4370
4371 static void
4372 sOutputBondInstructions(FILE *fp, int natoms, Atom *atoms, int overlap_correction)
4373 {
4374         Atom *ap, *ap2;
4375         char buf[96];
4376         int i, j, n[5], an, count, n1, n2, k;
4377         Int *cp;
4378         Int nexbonds;
4379         Int *exbonds;
4380         static const float sBondRad[4] = {0.060, 0.060, 0.060, 0.040};
4381         static const int sBondShade[4] = {5, 3, 1, 1};
4382
4383         n[0] = n[1] = n[2] = n[3] = 0;  /*  Start index of 3rd row atoms (and higher), 2nd row, 1st row, and H */
4384         n[4] = natoms;
4385         for (i = natoms - 1, ap = atoms + i; i >= 0; i--, ap--) {
4386                 an = ap->atomicNumber;
4387                 if (an < 2)
4388                         n[3] = i;
4389                 if (an < 10)
4390                         n[2] = i;
4391                 if (an < 18)
4392                         n[1] = i;
4393         }
4394         nexbonds = 0;
4395         exbonds = NULL;
4396         count = 0;
4397
4398         if (overlap_correction)
4399                 strcpy(buf, "  2  1001    0.000\n");
4400         else
4401                 strcpy(buf, "  2   812\n");
4402         
4403         for (i = 0; i < 4; i++) {
4404                 for (j = i; j < 4; j++) {
4405                         /*  Examine bonds between "group i" and "group j"  */
4406                         Vector dr;
4407                         double d;
4408                         double min_bond = 10000.0;     /*  Minimum distance between bound atoms  */
4409                         double min_nonbond = 10000.0;  /*  Minimum distance between non-bound atoms  */
4410                         double max_bond = -10000.0;    /*  Maximum distance between bound atoms  */
4411                         int count_exbond = 0;          /*  Number of explicit bonds in this group  */
4412                         for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
4413                                 for (n2 = n[j], ap2 = atoms + n2; n2 < n[j + 1]; n2++, ap2++) {
4414                                         if (n1 == n2)
4415                                                 continue;
4416                                         VecSub(dr, ap->r, ap2->r);
4417                                         d = VecLength(dr);
4418                                         cp = AtomConnectData(&ap->connect);
4419                                         for (k = ap->connect.count - 1; k >= 0; k--) {
4420                                                 if (cp[k] == n2)
4421                                                         break;
4422                                         }
4423                                         if (k >= 0) {
4424                                                 /*  n1 and n2 are bound  */
4425                                                 if (d < min_bond)
4426                                                         min_bond = d;
4427                                                 if (d > max_bond)
4428                                                         max_bond = d;
4429                                         } else {
4430                                                 /*  n1 and n2 are not bound  */
4431                                                 if (d < min_nonbond)
4432                                                         min_nonbond = d;
4433                                         }
4434                                 }
4435                         }
4436                         if (min_bond == 10000.0)
4437                                 continue;  /*  No bonds between these groups  */
4438                         min_bond *= 0.9;
4439                         if (max_bond + 0.002 < min_nonbond)
4440                                 max_bond += 0.002;
4441                         else {
4442                                 max_bond = min_nonbond - 0.002;
4443                                 /*  Some bonds may be omitted, so scan all bonds again  */
4444                                 for (n1 = n[i], ap = ATOM_AT_INDEX(atoms, n1); n1 < n[i + 1]; n1++, ap = ATOM_NEXT(ap)) {
4445                                         cp = AtomConnectData(&ap->connect);
4446                                         for (k = ap->connect.count - 1; k >= 0; k--) {
4447                                                 n2 = cp[k];
4448                                                 if (n2 < n[j] || n2 >= n[j + 1])
4449                                                         continue;
4450                                                 ap2 = atoms + n2;
4451                                                 VecSub(dr, ap->r, ap2->r);
4452                                                 d = VecLength(dr);
4453                                                 if (d > max_bond) {
4454                                                         /*  This bond should be explicitly defined  */
4455                                                         Int adc1, adc2;
4456                                                         if (count_exbond == 0) {
4457                                                                 adc1 = -(i + 1);  /*  Bond type  */
4458                                                                 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4459                                                         }
4460                                                         adc1 = sMakeAdc(n1, ap->symbase, ap->symop);
4461                                                         adc2 = sMakeAdc(n2, ap2->symbase, ap2->symop);
4462                                                         AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4463                                                         AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc2);
4464                                                         count_exbond++;
4465                                                 }
4466                                         }
4467                                 }
4468                         }
4469                         /*  Output the last instruction card  */
4470                         fputs(buf, fp);
4471                         /*  Make a new trailer card  */
4472                         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]);
4473                         count++;
4474                 }
4475         }
4476         if (count > 0) {
4477                 /*  Output the last trailer card  */
4478                 buf[2] = ' ';
4479                 fputs(buf, fp);
4480         }
4481         if (nexbonds > 0) {
4482                 if (count == 0 && overlap_correction) {
4483                         /*  1001 card is not yet written, so write it  */
4484                         buf[2] = ' ';
4485                         fputs(buf, fp);
4486                 }
4487                 snprintf(buf, sizeof(buf), "  1   %3d", (overlap_correction ? 821 : 811));
4488                 k = -exbonds[0] - 1;  /*  Bond type for the first block  */
4489                 i = 1;  /*  Index for exbonds[]  */
4490                 j = 0;  /*  Count in this block  */
4491                 while (i <= nexbonds) {
4492                         if (j >= 29 || i == nexbonds || exbonds[i] < 0) {
4493                                 /*  End of block  */
4494                                 buf[2] = '2';
4495                                 fputs(buf, fp);
4496                                 /*  The trailer card  */
4497                                 fprintf(fp, "                     %3d            %6.3f\n", sBondShade[k], sBondRad[k]);
4498                                 if (i == nexbonds)
4499                                         break;
4500                                 if (exbonds[i] < 0)
4501                                         k = -exbonds[i++] - 1;  /*  The new bond type  */
4502                                 j = 0;
4503                         } else if (j > 0 && j % 3 == 0) {
4504                                 buf[2] = '1';
4505                                 fputs(buf, fp);
4506                         }
4507                         n1 = exbonds[i++];
4508                         n2 = exbonds[i++];
4509                         snprintf(buf + 9 + (j % 3) * 18, sizeof(buf) - 9 - (j % 3) * 18, "%9d%9d\n", n1, n2);
4510                         j++;
4511                 }
4512                 free(exbonds);
4513         }
4514 }
4515         
4516 #if 0
4517 {
4518         /*  Explicit bond table, sorted by bond type  */
4519         for (i = j = 0; i < mp->nbonds; i++) {
4520                 n1 = mp->bonds[i * 2];
4521                 n2 = mp->bonds[i * 2 + 1];
4522                 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
4523                 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
4524                 if ((ap1->exflags & kAtomHiddenFlag) || (ap2->exflags & kAtomHiddenFlag))
4525                         continue;
4526                 if (ap1->atomicNumber > 18 || ap2->atomicNumber > 18) {
4527                         type = 3;
4528                 } else if (ap1->atomicNumber > 1 && ap1->atomicNumber > 1) {
4529                         type = 2;
4530                 } else {
4531                         type = 1;
4532                 }
4533                 ip[j * 3] = type;
4534                 ip[j * 3 + 1] = sMakeAdc(n1, ap1->symbase, ap1->symop);
4535                 ip[j * 3 + 2] = sMakeAdc(n2, ap2->symbase, ap2->symop);
4536                 j++;
4537         }
4538         mergesort(ip, j, sizeof(int) * 3, sCompareBondType);
4539         
4540         /*  Output instruction cards  */
4541         strcpy(buf, "  1   811");
4542         for (i = n1 = 0; i < j; i++) {
4543                 n2 = (n1 % 3) * 18 + 9;
4544                 snprintf(buf + n2, 80 - n2, "%9d%9d\n", ip[i * 3 + 1], ip[i * 3 + 2]);
4545                 if (i == j - 1 || n1 >= 29 || ip[i * 3] != ip[i * 3 + 3]) {
4546                         /*  End of this instruction  */
4547                         buf[2] = '2';
4548                         fputs(buf, fp);
4549                         switch (ip[i * 3]) {
4550                                 case 3: rad = 0.06; nshades = 5; break;
4551                                 case 2: rad = 0.06; nshades = 1; break;
4552                                 default: rad = 0.04; nshades = 1; break;
4553                         }
4554                         fprintf(fp, "                     %3d            %6.3f\n", nshades, rad);
4555                         strcpy(buf, "  1   811");
4556                         n1 = 0;
4557                         continue;
4558                 } else if (n1 % 3 == 2) {
4559                         fputs(buf, fp);
4560                         strcpy(buf, "  1      ");
4561                 }
4562                 n1++;
4563         }
4564         free(ip);
4565 }
4566 #endif
4567
4568 int
4569 MoleculeWriteToTepFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
4570 {
4571         FILE *fp;
4572         int i, j, natoms, *ip;
4573         Int *cp;
4574         Atom *ap, *atoms, **app;
4575         Double *dp;
4576         static Double sUnit[] = {1, 1, 1, 90, 90, 90};
4577         
4578         errbuf[0] = 0;
4579
4580         /*  Create sorted array of atoms  */
4581         natoms = mp->natoms;
4582         atoms = (Atom *)calloc(sizeof(Atom), natoms);
4583         app = (Atom **)calloc(sizeof(Atom *), natoms);
4584         ip = (int *)calloc(sizeof(int), natoms);
4585         if (atoms == NULL || app == NULL || ip == NULL) {
4586                 snprintf(errbuf, errbufsize, "Cannot allocate memory");
4587                 return 1;
4588         }
4589         /*  Sort the atom pointer by atomic number  */
4590         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
4591                 app[i] = ap;
4592         mergesort(app, natoms, sizeof(Atom *), sCompareByElement);
4593         for (i = 0; i < natoms; i++) {
4594                 /*  ip[old_index] is new_index  */
4595                 ip[app[i] - mp->atoms] = i;
4596         }
4597         /*  Copy the atom record to atoms[]  */
4598         /*  The 'v' member contains crystallographic coordinates  */
4599         /*  The connection table and symbase are renumbered  */
4600         /*  Hidden flags are modified to reflect the visibility in the MainView  */
4601         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4602                 AtomDuplicateNoFrame(ap, app[i]);
4603         /*      memmove(ap, app[i], gSizeOfAtomRecord); */
4604                 MoleculeCartesianToXtal(mp, &(ap->v), &(ap->r));
4605                 cp = AtomConnectData(&ap->connect);
4606                 for (j = ap->connect.count - 1; j >= 0; j--) {
4607                         cp[j] = ip[cp[j]];
4608                 }
4609                 if (SYMOP_ALIVE(ap->symop))
4610                         ap->symbase = ip[ap->symbase];
4611                 if (MainView_isAtomHidden(mp->mview, i)) {
4612                         ap->exflags |= kAtomHiddenFlag;
4613                 } else {
4614                         ap->exflags &= ~kAtomHiddenFlag;
4615                 }
4616         }
4617         free(ip);
4618         free(app);
4619         
4620         fp = fopen(fname, "wb");
4621         if (fp == NULL) {
4622                 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
4623                 return 1;
4624         }
4625         errbuf[0] = 0;
4626
4627         /*  Title line  */
4628         fprintf(fp, "Generated by Molby\n");
4629         
4630         /*  XtalCell  */
4631         if (mp->cell != NULL) {
4632                 dp = mp->cell->cell;
4633         } else {
4634                 dp = sUnit;
4635         }
4636         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]);
4637         
4638         /*  Symmetry operations  */
4639         if (mp->nsyms > 0) {
4640                 for (i = 0; i < mp->nsyms; i++) {
4641                         dp = mp->syms[i];
4642                         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]);
4643                 }
4644         } else {
4645                 fprintf(fp, "1             0  1  0  0              0  0  1  0              0  0  0  1\n");
4646         }
4647         
4648         /*  Atoms  */
4649         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4650                 /*  The 'v' field contains crystallographic coordinates  */
4651                 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);
4652                 if (ap->aniso != NULL) {
4653                         dp = ap->aniso->bij;
4654                         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);
4655                 } else {
4656                         Double temp = ap->tempFactor;
4657                         if (temp <= 0)
4658                                 temp = 1.2;
4659                         fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", temp, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4660                 }
4661         }
4662         /*  Special points  */
4663         {
4664                 Vector camera, lookat, up, xvec, yvec, zvec;
4665                 MainView_getCamera(mp->mview, &camera, &lookat, &up);
4666                 VecSub(zvec, lookat, camera);
4667                 VecCross(xvec, zvec, up);
4668                 NormalizeVec(&xvec, &xvec);
4669                 NormalizeVec(&yvec, &up);
4670                 VecInc(xvec, lookat);
4671                 VecInc(yvec, lookat);
4672                 MoleculeCartesianToXtal(mp, &lookat, &lookat);
4673                 MoleculeCartesianToXtal(mp, &xvec, &xvec);
4674                 MoleculeCartesianToXtal(mp, &yvec, &yvec);
4675                 fprintf(fp, " ORGN                      %9g%9g%9g        0\n", 0.0, 0.0, 0.0);
4676                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4677                 fprintf(fp, " CNTR                      %9g%9g%9g        0\n", lookat.x, lookat.y, lookat.z);
4678                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4679                 fprintf(fp, " X                         %9g%9g%9g        0\n", xvec.x, xvec.y, xvec.z);
4680                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4681                 fprintf(fp, " Y                         %9g%9g%9g        0\n", yvec.x, yvec.y, yvec.z);
4682                 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);
4683         }
4684         
4685         /*  Instructions  */
4686         fprintf(fp, "      201\n");
4687         fprintf(fp, "      205       12\n");
4688         fprintf(fp, "      301      6.6      6.6        0      0.8\n");
4689         sOutputAtomListInstructions(fp, natoms, atoms);
4690         fprintf(fp, "      501%4d55501%4d55501%4d55501%4d55501%4d55501                 1\n", natoms + 2, natoms + 2, natoms + 3, natoms + 2, natoms + 4);
4691         fprintf(fp, "      502        1      0.0        2      0.0        3      0.0\n");
4692         fprintf(fp, "      604                               1.538\n");
4693
4694         sOutputBondInstructions(fp, natoms, atoms, 1);
4695         sOutputAtomTypeInstructions(fp, natoms, atoms);
4696         sOutputBondInstructions(fp, natoms, atoms, 0);
4697
4698         for (i = 0; i < natoms; i++) {
4699                 AtomClean(atoms + i);
4700         }
4701         free(atoms);
4702
4703         fprintf(fp, "      202\n");
4704         fprintf(fp, "  0    -1\n");
4705         fclose(fp);
4706         return 0;
4707 }
4708
4709 void
4710 MoleculeDump(Molecule *mol)
4711 {
4712         int i, j;
4713         Int *cp;
4714         Atom *ap;
4715         for (i = 0; i < mol->natoms; i++) {
4716                 char buf1[8];
4717                 ap = ATOM_AT_INDEX(mol->atoms, i);
4718                 snprintf(buf1, sizeof buf1, "%3.4s.%d", ap->resName, ap->resSeq);
4719                 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);
4720                 cp = AtomConnectData(&ap->connect);
4721                 for (j = 0; j < ap->connect.count; j++) {
4722                         fprintf(stderr, "%s%d", (j > 0 ? "," : ""), cp[j]);
4723                 }
4724                 fprintf(stderr, "]\n");
4725         }
4726 }
4727
4728 #pragma mark ====== MD support (including modification of Molecule) ======
4729
4730 /*  Call md_prepare for the MDArena. If MDArena has not been created, a new arena is created.
4731         If something goes wrong, returns 1 (for missing parameters) or -1 (more serious error).
4732     If retmsg is not NULL, a message describing the problem is returned there. This message
4733     must be free'd by the caller.  */
4734 int
4735 MoleculePrepareMDArena(Molecule *mol, int check_only, char **retmsg)
4736 {
4737         const char *msg;
4738         Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
4739         Int missing = 0;
4740         IntGroup *ig1, *ig2, *ig3;
4741         MDArena *arena = mol->arena;
4742
4743         if (arena == NULL) {
4744                 md_arena_new(mol);
4745                 arena = mol->arena;
4746         } else if (arena->xmol != mol)
4747                 md_arena_set_molecule(arena, mol);
4748
4749         arena->is_initialized = 0;
4750         
4751         /*  Rebuild the tables  */
4752         ig1 = ig2 = ig3 = NULL;
4753         nangles = MoleculeFindMissingAngles(mol, &angles);
4754         ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
4755         nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
4756         if (nangles > 0) {
4757                 ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
4758                 MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
4759                 free(angles);
4760                 IntGroupRelease(ig1);
4761         }
4762         if (ndihedrals > 0) {
4763                 ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
4764                 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
4765                 free(dihedrals);
4766                 IntGroupRelease(ig2);
4767         }
4768         if (nimpropers > 0) {
4769                 ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
4770                 MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
4771                 free(impropers);
4772                 IntGroupRelease(ig3);
4773         }
4774         
4775         /*  Prepare parameters and internal information  */
4776         msg = md_prepare(arena, check_only);
4777         
4778         /*  Some parameters are missing?  */
4779         if (msg != NULL) {
4780                 if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
4781                         missing = 1;
4782                 else {
4783                         if (retmsg != NULL)
4784                                 asprintf(retmsg, "cannot initialize for MD: %s", msg);
4785                         return -1;
4786                 }
4787         }
4788         
4789         /*  The local parameter list is updated  */
4790         {
4791                 Int parType, idx;
4792                 if (mol->par == NULL)
4793                         mol->par = ParameterNew();
4794                 for (parType = kFirstParType; parType <= kLastParType; parType++) {
4795                         /*  Delete global and undefined parameters  */
4796                         UnionPar *up, *upbuf;
4797                         Int nparams, count;
4798                         ig1 = IntGroupNew();
4799                         for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
4800                                 if (up->bond.src != 0)
4801                                         IntGroupAdd(ig1, idx, 1);
4802                         }
4803                         if (IntGroupGetCount(ig1) > 0)
4804                                 MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
4805                         IntGroupRelease(ig1);
4806                         /*  Copy global and undefined parameters from arena and insert to mol->par  */
4807                         nparams = ParameterGetCountForType(arena->par, parType);
4808                         if (nparams == 0)
4809                                 continue;
4810                         upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
4811                         ig1 = IntGroupNew();
4812                         ig2 = IntGroupNew();
4813                         for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
4814                                 if (up->bond.src > 0)
4815                                         IntGroupAdd(ig1, idx, 1); /* Global parameter */
4816                                 else if (up->bond.src < 0)
4817                                         IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
4818                         }
4819                         if ((count = IntGroupGetCount(ig1)) > 0) {
4820                                 /*  Insert global parameters (at the top)  */
4821                                 ParameterCopy(arena->par, parType, upbuf, ig1);
4822                                 ig3 = IntGroupNewWithPoints(0, count, -1);
4823                                 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
4824                                 IntGroupRelease(ig3);
4825                         }
4826                         if ((count = IntGroupGetCount(ig2)) > 0) {
4827                                 /*  Insert undefined parameters (at the bottom)  */
4828                                 ParameterCopy(arena->par, parType, upbuf, ig2);
4829                                 idx = ParameterGetCountForType(mol->par, parType);
4830                                 ig3 = IntGroupNewWithPoints(idx, count, -1);
4831                                 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
4832                                 IntGroupRelease(ig3);
4833                         }
4834                         IntGroupRelease(ig2);
4835                         IntGroupRelease(ig1);
4836                         free(upbuf);
4837                 }
4838                 mol->needsMDRebuild = 0;  /*  We know the "modified" parameters are consistent with the MDArena  */
4839         }
4840         
4841         if (missing) {
4842                 if (retmsg != NULL)
4843                         *retmsg = strdup(msg);
4844                 return 1;
4845         } else return 0;
4846 }
4847
4848 #pragma mark ====== Serialize ======
4849
4850 Molecule *
4851 MoleculeDeserialize(const char *data, Int length, Int *timep)
4852 {
4853         Molecule *mp;
4854         Parameter *par;
4855 /*      int result; */
4856
4857         mp = MoleculeNew();
4858         if (mp == NULL)
4859                 goto out_of_memory;
4860         par = ParameterNew();
4861         if (par == NULL)
4862                 goto out_of_memory;
4863
4864         while (length >= 12) {
4865                 const char *ptr = data + 8 + sizeof(Int);
4866                 int len = *((const Int *)(data + 8));
4867                 int i, j, n;
4868                 if (strcmp(data, "ATOM") == 0) {
4869                         n = len / gSizeOfAtomRecord;
4870                         NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, n);
4871                         memmove(mp->atoms, ptr, len);
4872                 } else if (strcmp(data, "ANISO") == 0) {
4873                         Atom *ap;
4874                         n = len / (sizeof(Int) + sizeof(Aniso));
4875                         for (i = 0; i < n; i++) {
4876                                 j = *((const Int *)ptr);
4877                                 if (j < 0 || j >= mp->natoms)
4878                                         goto bad_format;
4879                                 ap = ATOM_AT_INDEX(mp->atoms, j);
4880                                 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
4881                                 if (ap->aniso == NULL)
4882                                         goto out_of_memory;
4883                                 *(ap->aniso) = *((Aniso *)(ptr + sizeof(Int)));
4884                                 ptr += sizeof(Int) + sizeof(Aniso);
4885                         }
4886                 } else if (strcmp(data, "FRAME") == 0) {
4887                         Atom *ap;
4888                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4889                                 if (ap->nframes == 0)
4890                                         continue;
4891                                 ap->frames = (Vector *)malloc(sizeof(Vector) * ap->nframes);
4892                                 if (ap->frames == NULL)
4893                                         goto out_of_memory;
4894                                 memmove(ap->frames, ptr, sizeof(Vector) * ap->nframes);
4895                                 ptr += sizeof(Vector) * ap->nframes;
4896                         }
4897                 } else if (strcmp(data, "EXTCON") == 0) {
4898                         Atom *ap;
4899                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4900                                 if (ap->connect.count <= ATOM_CONNECT_LIMIT)
4901                                         continue;
4902                                 n = ap->connect.count;
4903                                 ap->connect.count = 0;
4904                                 ap->connect.u.ptr = NULL;
4905                                 NewArray(&(ap->connect.u.ptr), &(ap->connect.count), sizeof(Int), n);
4906                                 memmove(ap->connect.u.ptr, ptr, sizeof(Int) * n);
4907                                 ptr += sizeof(Int) * n;
4908                         }
4909                 } else if (strcmp(data, "BOND") == 0) {
4910                         n = len / (sizeof(Int) * 2);
4911                         NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, n);
4912                         memmove(mp->bonds, ptr, len);
4913                 } else if (strcmp(data, "ANGLE") == 0) {
4914                         n = len / (sizeof(Int) * 3);
4915                         NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, n);
4916                         memmove(mp->angles, ptr, len);
4917                 } else if (strcmp(data, "DIHED") == 0) {
4918                         n = len / (sizeof(Int) * 4);
4919                         NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, n);
4920                         memmove(mp->dihedrals, ptr, len);
4921                 } else if (strcmp(data, "IMPROP") == 0) {
4922                         n = len / (sizeof(Int) * 4);
4923                         NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, n);
4924                         memmove(mp->impropers, ptr, len);
4925                 } else if (strcmp(data, "RESIDUE") == 0) {
4926                         n = len / 4;
4927                         NewArray(&mp->residues, &mp->nresidues, 4, n);
4928                         memmove(mp->residues, ptr, len);
4929                 } else if (strcmp(data, "CELL") == 0) {
4930                         mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
4931                         if (mp->cell == NULL)
4932                                 goto out_of_memory;
4933                         memmove(mp->cell, ptr, sizeof(XtalCell));
4934                 } else if (strcmp(data, "SYMOP") == 0) {
4935                         n = len / sizeof(Transform);
4936                         NewArray(&mp->syms, &mp->nsyms, sizeof(Transform), n);
4937                         memmove(mp->syms, ptr, len);
4938                 } else if (strcmp(data, "TIME") == 0) {
4939                         if (timep != NULL)
4940                                 *timep = *((Int *)ptr);
4941                 } else if (strcmp(data, "BONDPAR") == 0) {
4942                         mp->par = par;
4943                         n = len / sizeof(BondPar);
4944                         NewArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), n);
4945                         memmove(par->bondPars, ptr, len);
4946                 } else if (strcmp(data, "ANGPAR") == 0) {
4947                         mp->par = par;
4948                         n = len / sizeof(AnglePar);
4949                         NewArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), n);
4950                         memmove(par->anglePars, ptr, len);
4951                 } else if (strcmp(data, "DIHEPAR") == 0) {
4952                         mp->par = par;
4953                         n = len / sizeof(TorsionPar);
4954                         NewArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), n);
4955                         memmove(par->dihedralPars, ptr, len);
4956                 } else if (strcmp(data, "IMPRPAR") == 0) {
4957                         mp->par = par;
4958                         n = len / sizeof(TorsionPar);
4959                         NewArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), n);
4960                         memmove(par->improperPars, ptr, len);
4961                 } else if (strcmp(data, "VDWPAR") == 0) {
4962                         mp->par = par;
4963                         n = len / sizeof(VdwPar);
4964                         NewArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), n);
4965                         memmove(par->vdwPars, ptr, len);
4966                 } else if (strcmp(data, "VDWPPAR") == 0) {
4967                         mp->par = par;
4968                         n = len / sizeof(VdwPairPar);
4969                         NewArray(&par->vdwpPars, &par->nvdwpPars, sizeof(VdwPairPar), n);
4970                         memmove(par->vdwpPars, ptr, len);
4971                 } else if (strcmp(data, "VCUTPAR") == 0) {
4972                         mp->par = par;
4973                         n = len / sizeof(VdwCutoffPar);
4974                         NewArray(&par->vdwCutoffPars, &par->nvdwCutoffPars, sizeof(VdwCutoffPar), n);
4975                         memmove(par->vdwCutoffPars, ptr, len);
4976                 }
4977                 len += 8 + sizeof(Int);
4978                 data += len;
4979                 length -= len;
4980         }
4981         if (mp->par == NULL)
4982                 ParameterRelease(par);
4983 /*      result = MoleculeRebuildTablesFromConnects(mp);
4984         if (result != 0)
4985                 goto bad_format; */
4986         return mp;
4987         
4988   out_of_memory:
4989         Panic("Low memory while deserializing molecule data");
4990         return NULL; /* Not reached */
4991
4992   bad_format:
4993         Panic("internal error: bad format during deserializing molecule data");
4994         return NULL; /* Not reached */
4995 }
4996
4997 char *
4998 MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep)
4999 {
5000         char *ptr, *p;
5001         int len, len_all, i, naniso, nframes, nconnects;
5002
5003         /*  Array of atoms  */
5004         len = 8 + sizeof(Int) + gSizeOfAtomRecord * mp->natoms;
5005         ptr = (char *)malloc(len);
5006         if (ptr == NULL)
5007                 goto out_of_memory;
5008         memmove(ptr, "ATOM\0\0\0\0", 8);
5009         *((Int *)(ptr + 8)) = gSizeOfAtomRecord * mp->natoms;
5010         p = ptr + 8 + sizeof(Int);
5011         memmove(p, mp->atoms, gSizeOfAtomRecord * mp->natoms);
5012         naniso = nframes = nconnects = 0;
5013         for (i = 0; i < mp->natoms; i++) {
5014                 Atom *ap = ATOM_AT_INDEX(p, i);
5015                 if (ap->aniso != NULL) {
5016                         naniso++;
5017                         ap->aniso = NULL;
5018                 }
5019                 if (ap->frames != NULL) {
5020                         nframes += ap->nframes;
5021                         ap->frames = NULL;
5022                 }
5023                 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5024                         nconnects += ap->connect.count;
5025                         ap->connect.u.ptr = NULL;
5026                 }
5027         }
5028         len_all = len;
5029
5030         /*  Array of aniso  */
5031         if (naniso > 0) {
5032                 len = 8 + sizeof(Int) + (sizeof(Int) + sizeof(Aniso)) * naniso;
5033                 ptr = (char *)realloc(ptr, len_all + len);
5034                 if (ptr == NULL)
5035                         goto out_of_memory;
5036                 p = ptr + len_all;
5037                 memmove(p, "ANISO\0\0\0", 8);
5038                 *((Int *)(p + 8)) = (sizeof(Int) + sizeof(Aniso)) * naniso;
5039                 p += 8 + sizeof(Int);
5040                 for (i = 0; i < mp->natoms; i++) {
5041                         Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
5042                         if (ap->aniso != NULL) {
5043                                 *((Int *)p) = i;
5044                                 *((Aniso *)(p + sizeof(Int))) = *(ap->aniso);
5045                                 p += sizeof(Int) + sizeof(Aniso);
5046                         }
5047                 }
5048                 len_all += len;
5049         }
5050         
5051         /*  Array of frames  */
5052         if (nframes > 0) {
5053                 len = 8 + sizeof(Int) + sizeof(Vector) * nframes;
5054                 ptr = (char *)realloc(ptr, len_all + len);
5055                 if (ptr == NULL)
5056                         goto out_of_memory;
5057                 p = ptr + len_all;
5058                 memmove(p, "FRAME\0\0\0", 8);
5059                 *((Int *)(p + 8)) = sizeof(Vector) * nframes;
5060                 p += 8 + sizeof(Int);
5061                 for (i = 0; i < mp->natoms; i++) {
5062                         Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
5063                         if (ap->frames != NULL) {
5064                                 memmove(p, ap->frames, sizeof(Vector) * ap->nframes);
5065                                 p += sizeof(Vector) * ap->nframes;
5066                         }
5067                 }
5068                 len_all += len;
5069         }
5070         
5071         /*  Array of connects  */
5072         if (nconnects > 0) {
5073                 len = 8 + sizeof(Int) + sizeof(Int) * nconnects;
5074                 ptr = (char *)realloc(ptr, len_all + len);
5075                 if (ptr == NULL)
5076                         goto out_of_memory;
5077                 p = ptr + len_all;
5078                 memmove(p, "EXTCON\0\0", 8);
5079                 *((Int *)(p + 8)) = sizeof(Int) * nconnects;
5080                 p += 8 + sizeof(Int);
5081                 for (i = 0; i < mp->natoms; i++) {
5082                         Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
5083                         if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5084                                 memmove(p, ap->connect.u.ptr, sizeof(Int) * ap->connect.count);
5085                                 p += sizeof(Int) * ap->connect.count;
5086                         }
5087                 }
5088                 len_all += len;
5089         }
5090         
5091         /*  Bonds, angles, dihedrals, impropers  */
5092         if (mp->nbonds > 0) {
5093                 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nbonds;
5094                 ptr = (char *)realloc(ptr, len_all + len);
5095                 if (ptr == NULL)
5096                         goto out_of_memory;
5097                 p = ptr + len_all;
5098                 memmove(p, "BOND\0\0\0\0", 8);
5099                 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nbonds;
5100                 p += 8 + sizeof(Int);
5101                 memmove(p, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
5102                 len_all += len;
5103         }
5104         if (mp->nangles > 0) {
5105                 len = 8 + sizeof(Int) + sizeof(Int) * 3 * mp->nangles;
5106                 ptr = (char *)realloc(ptr, len_all + len);
5107                 if (ptr == NULL)
5108                         goto out_of_memory;
5109                 p = ptr + len_all;
5110                 memmove(p, "ANGLE\0\0\0", 8);
5111                 *((Int *)(p + 8)) = sizeof(Int) * 3 * mp->nangles;
5112                 p += 8 + sizeof(Int);
5113                 memmove(p, mp->angles, sizeof(Int) * 3 * mp->nangles);
5114                 len_all += len;
5115         }
5116         if (mp->ndihedrals > 0) {
5117                 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->ndihedrals;
5118                 ptr = (char *)realloc(ptr, len_all + len);
5119                 if (ptr == NULL)
5120                         goto out_of_memory;
5121                 p = ptr + len_all;
5122                 memmove(p, "DIHED\0\0\0", 8);
5123                 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->ndihedrals;
5124                 p += 8 + sizeof(Int);
5125                 memmove(p, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
5126                 len_all += len;
5127         }
5128         if (mp->nimpropers > 0) {
5129                 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->nimpropers;
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, "IMPROP\0\0", 8);
5135                 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->nimpropers;
5136                 p += 8 + sizeof(Int);
5137                 memmove(p, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
5138                 len_all += len;
5139         }
5140         
5141         /*  Array of residues  */
5142         if (mp->nresidues > 0) {
5143                 len = 8 + sizeof(Int) + 4 * mp->nresidues;
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, "RESIDUE\0", 8);
5149                 *((Int *)(p + 8)) = 4 * mp->nresidues;
5150                 p += 8 + sizeof(Int);
5151                 memmove(p, mp->residues, 4 * mp->nresidues);
5152                 len_all += len;
5153         }
5154
5155         /*  Unit cell  */
5156         if (mp->cell != NULL) {
5157                 len = 8 + sizeof(Int) + sizeof(XtalCell);
5158                 ptr = (char *)realloc(ptr, len_all + len);
5159                 if (ptr == NULL)
5160                         goto out_of_memory;
5161                 p = ptr + len_all;
5162                 memmove(p, "CELL\0\0\0\0", 8);
5163                 *((Int *)(p + 8)) = sizeof(XtalCell);
5164                 p += 8 + sizeof(Int);
5165                 memmove(p, mp->cell, sizeof(XtalCell));
5166                 len_all += len;
5167         }
5168         
5169         /*  Symmetry operations  */
5170         if (mp->nsyms > 0) {
5171                 len = 8 + sizeof(Int) + sizeof(Transform) * mp->nsyms;
5172                 ptr = (char *)realloc(ptr, len_all + len);
5173                 if (ptr == NULL)
5174                         goto out_of_memory;
5175                 p = ptr + len_all;
5176                 memmove(p, "SYMOP\0\0\0", 8);
5177                 *((Int *)(p + 8)) = sizeof(Transform) * mp->nsyms;
5178                 p += 8 + sizeof(Int);
5179                 memmove(p, mp->syms, sizeof(Transform) * mp->nsyms);
5180                 len_all += len;
5181         }
5182         
5183         /*  Parameters  */
5184         if (mp->par != NULL) {
5185                 int type;
5186                 for (type = kFirstParType; type <= kLastParType; type++) {
5187                         const char *parname;
5188                         Int parsize, parcount;
5189                         void *parptr;
5190                         switch (type) {
5191                                 case kBondParType:
5192                                         parname = "BONDPAR\0";
5193                                         parsize = sizeof(BondPar);
5194                                         parcount = mp->par->nbondPars;
5195                                         parptr = mp->par->bondPars;
5196                                         break;
5197                                 case kAngleParType:
5198                                         parname = "ANGPAR\0\0";
5199                                         parsize = sizeof(AnglePar);
5200                                         parcount = mp->par->nanglePars;
5201                                         parptr = mp->par->anglePars;
5202                                         break;
5203                                 case kDihedralParType:
5204                                         parname = "DIHEPAR\0";
5205                                         parsize = sizeof(TorsionPar);
5206                                         parcount = mp->par->ndihedralPars;
5207                                         parptr = mp->par->dihedralPars;
5208                                         break;
5209                                 case kImproperParType:
5210                                         parname = "IMPRPAR\0";
5211                                         parsize = sizeof(TorsionPar);
5212                                         parcount = mp->par->nimproperPars;
5213                                         parptr = mp->par->improperPars;
5214                                         break;
5215                                 case kVdwParType:
5216                                         parname = "VDWPAR\0\0";
5217                                         parsize = sizeof(VdwPar);
5218                                         parcount = mp->par->nvdwPars;
5219                                         parptr = mp->par->vdwPars;
5220                                         break;
5221                                 case kVdwPairParType:
5222                                         parname = "VDWPPAR\0";
5223                                         parsize = sizeof(VdwPairPar);
5224                                         parcount = mp->par->nvdwpPars;
5225                                         parptr = mp->par->vdwpPars;
5226                                         break;
5227                                 case kVdwCutoffParType:
5228                                         parname = "VCUTPAR\0";
5229                                         parsize = sizeof(VdwCutoffPar);
5230                                         parcount = mp->par->nvdwCutoffPars;
5231                                         parptr = mp->par->vdwCutoffPars;
5232                                         break;
5233                                 default:
5234                                         continue;
5235                         }
5236                         if (parcount > 0) {
5237                                 len = 8 + sizeof(Int) + parsize * parcount;
5238                                 ptr = (char *)realloc(ptr, len_all + len);
5239                                 if (ptr == NULL)
5240                                         goto out_of_memory;
5241                                 p = ptr + len_all;
5242                                 memmove(p, parname, 8);
5243                                 *((Int *)(p + 8)) = parsize * parcount;
5244                                 p += 8 + sizeof(Int);
5245                                 memmove(p, parptr, parsize * parcount);
5246                                 len_all += len;
5247                         }
5248                 }
5249         }
5250         
5251         /*  Time stamp  */
5252         {
5253                 time_t tm = time(NULL);
5254                 len = 8 + sizeof(Int) + sizeof(Int);
5255                 ptr = (char *)realloc(ptr, len_all + len);
5256                 if (ptr == NULL)
5257                         goto out_of_memory;
5258                 p = ptr + len_all;
5259                 memmove(p, "TIME\0\0\0\0", 8);
5260                 *((Int *)(p + 8)) = sizeof(Int);
5261                 p += 8 + sizeof(Int);
5262                 *((Int *)p) = (Int)tm;
5263                 len_all += len;
5264                 if (timep != NULL)
5265                         *timep = (Int)tm;
5266         }
5267         
5268         if (outLength != NULL)
5269                 *outLength = len_all;
5270         return ptr;
5271
5272   out_of_memory:
5273     Panic("Low memory while serializing a molecule data");
5274         return NULL; /* Not reached */  
5275 }
5276
5277 #pragma mark ====== Search for bonds, angles, dihedrals, impropers ======
5278
5279 static IntGroup *
5280 sMoleculeSearchIncludingAtoms(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5281 {
5282         int i, j;
5283         Int *ip;
5284         IntGroup *gp = NULL;
5285         if (atomgroup == NULL)
5286                 return NULL;
5287         for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5288                 for (j = 0; j < nsize; j++) {
5289                         if (IntGroupLookup(atomgroup, ip[j], NULL) != 0) {
5290                                 if (gp == NULL)
5291                                         gp = IntGroupNew();
5292                                 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5293                                         Panic("Low memory while searching %s", msg);
5294                                 break;
5295                         }
5296                 }
5297         }
5298         return gp;
5299 }
5300
5301 IntGroup *
5302 MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5303 {
5304         if (mp == NULL)
5305                 return NULL;
5306         return sMoleculeSearchIncludingAtoms(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5307 }
5308
5309 IntGroup *
5310 MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5311 {
5312         if (mp == NULL)
5313                 return NULL;
5314         return sMoleculeSearchIncludingAtoms(mp->nangles, mp->angles, 3, atomgroup, "angles");
5315 }
5316
5317 IntGroup *
5318 MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5319 {
5320         if (mp == NULL)
5321                 return NULL;
5322         return sMoleculeSearchIncludingAtoms(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5323 }
5324
5325 IntGroup *
5326 MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5327 {
5328         if (mp == NULL)
5329                 return NULL;
5330         return sMoleculeSearchIncludingAtoms(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5331 }
5332
5333 static IntGroup *
5334 sMoleculeSearchAcrossAtomGroup(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5335 {
5336         int i, j;
5337         Int *ip;
5338         IntGroup *gp = NULL;
5339         if (atomgroup == NULL)
5340                 return NULL;
5341         for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5342                 int k = -1;
5343                 for (j = 0; j < nsize; j++) {
5344                         int kk;
5345                         kk = (IntGroupLookup(atomgroup, ip[j], NULL) != 0);
5346                         if (k < 0)
5347                                 k = kk;
5348                         else if (k != kk) {
5349                                 /*  This bond etc. crosses the atom group border  */
5350                                 if (gp == NULL)
5351                                         gp = IntGroupNew();
5352                                 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5353                                         Panic("Low memory while searching %s", msg);
5354                                 break;
5355                         }
5356                 }
5357         }
5358         return gp;
5359 }
5360
5361 IntGroup *
5362 MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5363 {
5364         if (mp == NULL)
5365                 return NULL;
5366         return sMoleculeSearchAcrossAtomGroup(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5367 }
5368
5369 IntGroup *
5370 MoleculeSearchAnglesAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5371 {
5372         if (mp == NULL)
5373                 return NULL;
5374         return sMoleculeSearchAcrossAtomGroup(mp->nangles, mp->angles, 3, atomgroup, "angles");
5375 }
5376
5377 IntGroup *
5378 MoleculeSearchDihedralsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5379 {
5380         if (mp == NULL)
5381                 return NULL;
5382         return sMoleculeSearchAcrossAtomGroup(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5383 }
5384
5385 IntGroup *
5386 MoleculeSearchImpropersAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5387 {
5388         if (mp == NULL)
5389                 return NULL;
5390         return sMoleculeSearchAcrossAtomGroup(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5391 }
5392
5393 /*  Subroutine for MoleculeGuessBonds. It can be also used independently, but make sure that *outNbonds/*outBonds 
5394     _correctly_ represents an array of two integers (as in mp->nbonds/mp->bonds).  */
5395 /*  Find atoms within the given "distance" from the given atom.  */
5396 /*  If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5397  the threshold distance is given by the sum of van der Waals radii times limit.  */
5398 /*  If triangle is non-zero, then only atoms with lower indexes than index are looked for.  */
5399 int
5400 MoleculeFindCloseAtoms(Molecule *mp, Int index, Double limit, Int *outNbonds, Int **outBonds, Int triangle)
5401 {
5402         Int n1, n2, j, nlim, newbond[2];
5403         Double a1, a2, alim;
5404         Vector dr, r1, r2;
5405         Atom *ap = ATOM_AT_INDEX(mp->atoms, index);
5406         n1 = ap->atomicNumber;
5407         if (n1 >= 0 && n1 < gCountElementParameters)
5408                 a1 = gElementParameters[n1].radius;
5409         else a1 = gElementParameters[6].radius;
5410         r1 = ap->r;
5411         nlim = (triangle ? index : mp->natoms);
5412         for (j = 0; j < nlim; j++) {
5413                 Atom *bp = ATOM_AT_INDEX(mp->atoms, j);
5414                 if (index == j)
5415                         continue;
5416                 n2 = bp->atomicNumber;
5417                 if (n2 >= 0 && n2 < gCountElementParameters)
5418                         a2 = gElementParameters[n2].radius;
5419                 else a2 = gElementParameters[6].radius;
5420                 r2 = bp->r;
5421                 VecSub(dr, r1, r2);
5422                 if (limit < 0)
5423                         alim = -limit;
5424                 else
5425                         alim = limit * (a1 + a2);
5426                 if (VecLength2(dr) < alim * alim) {
5427                         newbond[0] = index;
5428                         newbond[1] = j;
5429                         /*      MoleculeAddBonds(mp, 1, newbonds); */
5430                         AssignArray(outBonds, outNbonds, sizeof(Int) * 2, *outNbonds, newbond);
5431                 }
5432         }
5433         return 0;
5434 }
5435
5436 /*  Guess the bonds from the coordinates  */
5437 /*  If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5438     the threshold distance is given by the sum of van der Waals radii times limit.  */
5439 int
5440 MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds)
5441 {
5442         Int nbonds, *bonds, i, newbond[2];
5443 /*      int i, j, n1, n2;
5444         Atom *ap, *bp;
5445         Vector r1, r2, dr;
5446         Double a1, a2, alim;
5447         Int newbond[2];
5448         ElementPar *p = gElementParameters; */
5449         nbonds = 0;
5450         bonds = NULL;
5451         for (i = 0; i < mp->natoms; i++) {
5452                 MoleculeFindCloseAtoms(mp, i, limit, &nbonds, &bonds, 1);
5453                 /*
5454                 ap = ATOM_AT_INDEX(mp->atoms, i);
5455                 n1 = ap->atomicNumber;
5456                 if (n1 >= 0 && n1 < gCountElementParameters)
5457                         a1 = p[n1].radius;
5458                 else a1 = p[6].radius;
5459                 r1 = ap->r;
5460                 for (j = 0; j < i; j++) {
5461                         bp = ATOM_AT_INDEX(mp->atoms, j);
5462                         n2 = bp->atomicNumber;
5463                         if (n2 >= 0 && n2 < gCountElementParameters)
5464                                 a2 = p[n2].radius;
5465                         else a2 = p[6].radius;
5466                         r2 = bp->r;
5467                         VecSub(dr, r1, r2);
5468                         if (limit < 0)
5469                                 alim = -limit;
5470                         else
5471                                 alim = limit * (a1 + a2);
5472                         if (VecLength2(dr) < alim * alim) {
5473                                 newbond[0] = i;
5474                                 newbond[1] = j;
5475                                 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5476                         }
5477                 }
5478                 */
5479         }
5480         if (nbonds > 0) {
5481                 newbond[0] = kInvalidIndex;
5482                 newbond[1] = 0;
5483                 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5484                 nbonds--;
5485         }
5486         if (outNbonds != NULL)
5487                 *outNbonds = nbonds;
5488         if (outBonds != NULL)
5489                 *outBonds = bonds;
5490         return 0;
5491 }
5492
5493 /*  Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information  */
5494 int
5495 MoleculeRebuildTablesFromConnects(Molecule *mp)
5496 {
5497         int i, j, k, retval;
5498         Atom *ap;
5499         Int ibuf[6], *cp;
5500         
5501         __MoleculeLock(mp);
5502
5503         /*  Find bonds   */
5504         if (mp->nbonds == 0) {
5505                 for (i = 0; i < mp->natoms; i++) {
5506                         ap = ATOM_AT_INDEX(mp->atoms, i);
5507                         cp = AtomConnectData(&ap->connect);
5508                         for (j = 0; j < ap->connect.count; j++) {
5509                                 k = cp[j];
5510                                 if (i >= k)
5511                                         continue;
5512                                 ibuf[0] = i;
5513                                 ibuf[1] = k;
5514                                 /*  MoleculeAddBonds() should not be used, because it assumes connects[] and
5515                                     bonds are already in sync  */
5516                                 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
5517                         /*      retval = MoleculeAddBonds(mp, 1, ibuf);
5518                                 if (retval != 0)
5519                                         goto abort; */
5520                         }
5521                 }
5522         }
5523         
5524         /*  Find angles  */
5525         if (mp->nangles == 0) {
5526                 for (i = 0; i < mp->natoms; i++) {
5527                         ap = ATOM_AT_INDEX(mp->atoms, i);
5528                         cp = AtomConnectData(&ap->connect);
5529                         for (j = 0; j < ap->connect.count; j++) {
5530                                 for (k = j + 1; k < ap->connect.count; k++) {
5531                                         ibuf[0] = cp[j];
5532                                         ibuf[1] = i;
5533                                         ibuf[2] = cp[k];
5534                                         ibuf[3] = -1;
5535                                         retval = MoleculeAddAngles(mp, ibuf, NULL);
5536                                         if (retval < 0)
5537                                                 goto abort;
5538                                 }
5539                         }
5540                 }
5541         }
5542         
5543         /*  Find dihedrals  */
5544         if (mp->ndihedrals == 0) {
5545                 for (i = 0; i < mp->natoms; i++) {
5546                         ap = ATOM_AT_INDEX(mp->atoms, i);
5547                         cp = AtomConnectData(&ap->connect);
5548                         for (j = 0; j < ap->connect.count; j++) {
5549                                 int jj, kk, mm, m;
5550                                 Atom *apjj;
5551                                 Int *cpjj;
5552                                 jj = cp[j];
5553                                 if (i >= jj)
5554                                         continue;
5555                                 apjj = ATOM_AT_INDEX(mp->atoms, jj);
5556                                 cpjj = AtomConnectData(&apjj->connect);
5557                                 for (k = 0; k < ap->connect.count; k++) {
5558                                         if (k == j)
5559                                                 continue;
5560                                         kk = cp[k];
5561                                         for (m = 0; m < apjj->connect.count; m++) {
5562                                                 mm = cpjj[m];
5563                                                 if (mm == i || mm == kk)
5564                                                         continue;
5565                                                 ibuf[0] = kk;
5566                                                 ibuf[1] = i;
5567                                                 ibuf[2] = jj;
5568                                                 ibuf[3] = mm;
5569                                                 ibuf[4] = -1;
5570                                                 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
5571                                                 if (retval < 0)
5572                                                         goto abort;
5573                                         }
5574                                 }
5575                         }
5576                 }
5577         }
5578         
5579         /*  Find impropers  */
5580         if (mp->nimpropers == 0) {
5581                 for (i = 0; i < mp->natoms; i++) {
5582                         int i1, i2, i4, n1, n2, n4;
5583                         ap = ATOM_AT_INDEX(mp->atoms, i);
5584                         cp = AtomConnectData(&ap->connect);
5585                         for (i1 = 0; i1 < ap->connect.count; i1++) {
5586                                 n1 = cp[i1];
5587                                 for (i2 = i1 + 1; i2 < ap->connect.count; i2++) {
5588                                         n2 = cp[i2];
5589                                         for (i4 = i2 + 1; i4 < ap->connect.count; i4++) {
5590                                                 n4 = cp[i4];
5591                                                 ibuf[0] = n1;
5592                                                 ibuf[1] = n2;
5593                                                 ibuf[2] = i;
5594                                                 ibuf[3] = n4;
5595                                                 ibuf[4] = -1;
5596                                                 retval = MoleculeAddImpropers(mp, ibuf, NULL);
5597                                                 if (retval < 0)
5598                                                         goto abort;
5599                                         }
5600                                 }
5601                         }
5602                 }
5603         }
5604
5605         mp->needsMDRebuild = 1;
5606         __MoleculeUnlock(mp);
5607         return 0;
5608
5609   abort:
5610         __MoleculeUnlock(mp);
5611         return retval;
5612 }
5613
5614 #pragma mark ====== Atom names ======
5615
5616 /*  Look for the n1-th atom in resno-th residue (n1 is 0-based)  */
5617 int
5618 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
5619 {
5620         int i, j, lasti;
5621         Atom *ap;
5622         if (mp == NULL || mp->natoms == 0)
5623                 return -1;
5624         lasti = -1;
5625         for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5626                 if (ap->resSeq == resno) {
5627                         lasti = i;
5628                         if (j++ == n1)
5629                                 return i;
5630                 }
5631         }
5632         if (n1 == -1)
5633                 return lasti; /* max */
5634         return -1;
5635 }
5636
5637 int
5638 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
5639 {
5640     int n;
5641     char *p;
5642         n = strtol(s, &p, 0);
5643         if (p > s) {
5644                 while (isspace(*p))
5645                         p++;
5646                 if (*p == 0) {
5647                   resName[0] = 0;
5648                   *resSeq = -1;
5649                   atomName[0] = 0;
5650                   return n;
5651                 }
5652         }
5653
5654         if ((p = strchr(s, ':')) != NULL) {
5655                 /*  Residue is specified  */
5656                 char *pp;
5657                 if ((pp = strchr(s, '.')) != NULL && pp < p) {
5658                         /*  Residue number is also specified  */
5659                         char *ppp;
5660                         n = pp - s;
5661                         *resSeq = strtol(pp + 1, &ppp, 0);
5662                         if (ppp == pp + 1)
5663                                 return -2;  /*  Bad format  */
5664                         while (isspace(*ppp))
5665                                 ppp++;
5666                         if (ppp != p)
5667                                 return -2;  /*  Bad format  */
5668                 } else {
5669                         *resSeq = -1;
5670                         /*  Check whether the "residue name" is an integer  */
5671                         n = strtol(s, &pp, 0);
5672                         if (pp > s) {
5673                                 while (isspace(*pp))
5674                                         pp++;
5675                                 if (*pp == 0 || *pp == ':') {
5676                                         *resSeq = n;
5677                                         if (*resSeq < 0)
5678                                                 return -2;  /*  Bad format  */
5679                                 }
5680                         }
5681                         if (*resSeq >= 0)
5682                                 n = 0;
5683                         else
5684                                 n = p - s;
5685                 }
5686                 if (n >= sizeof(resName))
5687                         n = sizeof(resName) - 1;
5688                 strncpy(resName, s, n);
5689                 resName[n] = 0;
5690                 p++;
5691         } else {
5692                 resName[0] = 0;
5693                 *resSeq = -1;
5694                 p = (char *)s;
5695         }
5696         strncpy(atomName, p, 4);
5697         atomName[4] = 0;
5698         return 0;
5699 }
5700
5701 /*  Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer  */
5702 int
5703 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
5704 {
5705         char resName[6];
5706         int resSeq, n;
5707         char atomName[6];
5708         /*      char *p; */
5709
5710         n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
5711         if (atomName[0] == 0) {
5712           if (n >= mp->natoms)
5713             n = -1;  /* Out of range */
5714           return n;
5715         }
5716         for (n = 0; n < mp->natoms; n++) {
5717                 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
5718                 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
5719                         && (resSeq < 0 || ap->resSeq == resSeq)
5720                         && strncmp(atomName, ap->aname, 4) == 0) {
5721                         return n;
5722                 }
5723         }
5724         return -1;  /*  Not found  */
5725 }
5726
5727 int
5728 MoleculeAreAtomsConnected(Molecule *mp, int n1, int n2)
5729 {
5730         Atom *ap;
5731         Int i, *cp;
5732         if (mp == NULL || n1 < 0 || n1 >= mp->natoms || n2 < 0 || n2 >= mp->natoms)
5733                 return 0;
5734         ap = ATOM_AT_INDEX(mp->atoms, n1);
5735         cp = AtomConnectData(&ap->connect);
5736         for (i = 0; i < ap->connect.count; i++)
5737                 if (cp[i] == n2)
5738                         return 1;
5739         return 0;
5740 }
5741
5742
5743 void
5744 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
5745 {
5746         Atom *ap;
5747         int n;
5748         if (mp == NULL || index < 0 || index >= mp->natoms) {
5749                 buf[0] = 0;
5750                 return;
5751         }
5752         ap = mp->atoms + index;
5753         if (ap->resSeq != 0) {
5754                 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
5755                 buf += n;
5756                 bufsize -= n;
5757         }
5758         snprintf(buf, bufsize, "%.4s", ap->aname);
5759 }
5760
5761 #pragma mark ====== Selection ======
5762
5763 static void
5764 sMoleculeNotifyChangeSelection(Molecule *mp)
5765 {
5766         /*  TODO: Finer control of notification types may be necessary  */
5767         MoleculeCallback_notifyModification(mp, 0);
5768 }
5769
5770 void
5771 MoleculeSetSelection(Molecule *mp, IntGroup *select)
5772 {
5773         if (mp == NULL)
5774                 return;
5775         if (select != NULL)
5776                 IntGroupRetain(select);
5777         if (mp->selection != NULL)
5778                 IntGroupRelease(mp->selection);
5779         mp->selection = select;
5780         sMoleculeNotifyChangeSelection(mp);
5781 }
5782
5783 IntGroup *
5784 MoleculeGetSelection(Molecule *mp)
5785 {
5786         if (mp == NULL)
5787                 return NULL;
5788         else return mp->selection;
5789 }
5790
5791 void
5792 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
5793 {
5794         if (mp->selection == NULL)
5795                 mp->selection = IntGroupNew();
5796         if (!extending)
5797                 IntGroupClear(mp->selection);
5798         IntGroupAdd(mp->selection, n1, 1);
5799         sMoleculeNotifyChangeSelection(mp);
5800 }
5801
5802 void
5803 MoleculeUnselectAtom(Molecule *mp, int n1)
5804 {
5805         if (mp->selection != NULL)
5806                 IntGroupRemove(mp->selection, n1, 1);
5807         sMoleculeNotifyChangeSelection(mp);
5808 }
5809
5810 void
5811 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
5812 {
5813         if (mp->selection == NULL)
5814                 mp->selection = IntGroupNew();
5815         IntGroupReverse(mp->selection, n1, 1);
5816         sMoleculeNotifyChangeSelection(mp);
5817 }
5818
5819 int
5820 MoleculeIsAtomSelected(Molecule *mp, int n1)
5821 {
5822         if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
5823                 return 1;
5824         else return 0;
5825 }
5826
5827 int
5828 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
5829 {
5830         if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
5831                 return 1;
5832         else return 0;
5833 }
5834
5835 IntGroup *
5836 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
5837 {
5838         int status;
5839         IntGroup *remain, *ig1, *ig2;
5840         ig1 = ig2 = NULL;
5841         remain = IntGroupNewFromIntGroup(remove);
5842         if (remain == NULL)
5843                 status = -1;
5844         else
5845                 status = IntGroupReverse(remain, 0, mp->natoms);
5846         if (status == 0) {
5847                 ig1 = IntGroupNew();
5848                 if (ig1 == NULL)
5849                         status = -1;
5850                 else
5851                         status = IntGroupDifference(selection, remove, ig1);
5852         }
5853         if (status == 0) {
5854                 ig2 = IntGroupNew();
5855                 if (ig2 == NULL)
5856                         status = -1;
5857                 else
5858                         status = IntGroupDeconvolute(ig1, remain, ig2);
5859         }
5860         if (remain != NULL)
5861                 IntGroupRelease(remain);
5862         if (ig1 != NULL)
5863                 IntGroupRelease(ig1);
5864         if (status == 0)
5865                 return ig2;
5866         else {
5867                 if (ig2 != NULL)
5868                         IntGroupRelease(ig2);
5869                 return NULL;
5870         }
5871 }
5872
5873 #pragma mark ====== Atom Equivalence ======
5874
5875 struct sEqList {
5876         int i[2];
5877         struct sEqList *next;
5878         struct sEqList *link;
5879 };
5880
5881 static struct sEqList *sListBase = NULL;
5882 static struct sEqList *sListFree = NULL;
5883
5884 static struct sEqList *
5885 sAllocEqList(void)
5886 {
5887         struct sEqList *lp;
5888         if (sListFree != NULL) {
5889                 lp = sListFree;
5890                 sListFree = lp->next;
5891                 lp->i[0] = lp->i[1] = 0;
5892                 lp->next = NULL;
5893                 return lp;
5894         }
5895         lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
5896         lp->link = sListBase;
5897         sListBase = lp;
5898         return lp;
5899 }
5900
5901 static void
5902 sFreeEqList(struct sEqList *list)
5903 {
5904         list->next = sListFree;
5905         sListFree = list;
5906 }
5907
5908 static void
5909 sDeallocateEqLists(void)
5910 {
5911         struct sEqList *lp, *lp_link;
5912         for (lp = sListBase; lp != NULL; lp = lp_link) {
5913                 lp_link = lp->link;
5914                 free(lp);
5915         }
5916         sListBase = NULL;
5917         sListFree = NULL;
5918 }
5919
5920 static int
5921 sExistInEqList(int i, int idx, struct sEqList *list)
5922 {
5923         while (list != NULL) {
5924                 if (list->i[idx] == i)
5925                         return 1;
5926                 list = list->next;
5927         }
5928         return 0;
5929 }
5930
5931 static struct sEqList *
5932 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
5933 {
5934         Atom *api, *apj;
5935         struct sEqList *list1, *list2;
5936         Int ii, jj, ni, nj, *cpi, *cpj;
5937         api = ATOM_AT_INDEX(mol->atoms, i);
5938         apj = ATOM_AT_INDEX(mol->atoms, j);
5939         if (api->atomicNumber != apj->atomicNumber)
5940                 return NULL;
5941         list1 = sAllocEqList();
5942         if (list1 == NULL)
5943                 return NULL;
5944         list1->i[0] = i;
5945         list1->i[1] = j;
5946         list1->next = list;
5947         if (i == j || (db[i] != NULL && db[i] == db[j]))
5948                 return list1;
5949         cpi = AtomConnectData(&api->connect);
5950         cpj = AtomConnectData(&apj->connect);
5951         for (ni = 0; ni < api->connect.count; ni++) {
5952                 ii = cpi[ni];
5953                 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
5954                         continue;
5955                 if (sExistInEqList(ii, 0, list1))
5956                         continue;
5957                 list2 = NULL;
5958                 for (nj = 0; nj < apj->connect.count; nj++) {
5959                         jj = cpj[nj];
5960                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
5961                                 continue;
5962                         if (sExistInEqList(jj, 1, list1))
5963                                 continue;
5964                         list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
5965                         if (list2 != NULL)
5966                                 break;
5967                 }
5968                 if (list2 == NULL) {
5969                         sFreeEqList(list1);
5970                         return NULL;    /*  No equivalent to ii  */
5971                 }
5972                 list1 = list2;      /*  ii is OK, try next  */
5973         }
5974         return list1;
5975 }
5976
5977 int
5978 sDBInclude(Int *ip, int i)
5979 {
5980         int j;
5981         if (ip == NULL)
5982                 return -1;
5983         for (j = ip[0] - 1; j >= 0; j--) {
5984                 if (ip[j] == i)
5985                         return j;
5986         }
5987         return -1;
5988 }
5989
5990 Int *
5991 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
5992 {
5993         Int **db;  /*  List of equivalents for each atom  */
5994         Int *ip, *result;
5995         Atom *api, *apj, *apk;
5996         Int *cpi, *cpj, *ibuf, nibuf;
5997         int i, j, k, ii, jj, kk;
5998         if (mol == NULL || mol->natoms == 0)
5999                 return NULL;
6000         db = (Int **)calloc(sizeof(Int *), mol->natoms);
6001         ibuf = NULL;
6002         nibuf = 0;
6003
6004         /*  Find the equivalent univalent atoms  */
6005         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6006                 if (api->connect.count < 2)
6007                         continue;
6008                 cpi = AtomConnectData(&api->connect);
6009                 for (j = 0; j < api->connect.count; j++) {
6010                         Int n;
6011                         n = 0;
6012                         jj = cpi[j];
6013                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6014                                 continue;
6015                         AssignArray(&ibuf, &nibuf, sizeof(Int), n, &jj);
6016                         n++;
6017                         apj = ATOM_AT_INDEX(mol->atoms, jj);
6018                         if (apj->connect.count != 1 || db[jj] != NULL)
6019                                 continue;
6020                         cpj = AtomConnectData(&apj->connect);
6021                         for (k = j + 1; k < api->connect.count; k++) {
6022                                 kk = cpj[k];
6023                                 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
6024                                         continue;
6025                                 apk = ATOM_AT_INDEX(mol->atoms, kk);
6026                                 if (apk->connect.count != 1 || db[kk] != NULL)
6027                                         continue;
6028                                 if (apj->atomicNumber == apk->atomicNumber) {
6029                                         AssignArray(&ibuf, &nibuf, sizeof(Int), n, &kk);
6030                                         n++;
6031                                 }
6032                         }
6033                         if (n > 1) {
6034                                 ip = (Int *)calloc(sizeof(Int), n + 1);
6035                                 if (ip == NULL)
6036                                         return NULL;
6037                                 ip[0] = n;
6038                                 memmove(ip + 1, ibuf, sizeof(Int) * n);
6039                                 for (k = 0; k < n; k++)
6040                                         db[ip[k + 1]] = ip;
6041                         }
6042                 }
6043         }
6044         if (ibuf != NULL) {
6045                 free(ibuf);
6046                 ibuf = NULL;
6047         }
6048         
6049         /*  Try matching (i,j) pair  */
6050         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6051                 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
6052                         continue;
6053                 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
6054                         struct sEqList *list;
6055                         if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
6056                                 continue;
6057                         if (api->atomicNumber != apj->atomicNumber)
6058                                 continue;  /*  Different elements do not match  */
6059                         if (db[i] != NULL && db[i] == db[j])
6060                                 continue;  /*  Already equivalent  */
6061                         list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
6062                         if (list == NULL)
6063                                 continue;  /*  (i,j) do not match  */
6064                         while (list != NULL) {
6065                                 ii = list->i[0];
6066                                 jj = list->i[1];
6067                                 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
6068                                         /*  Merge db[ii] and db[jj]  */
6069                                         k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
6070                                         ip = (Int *)calloc(sizeof(Int), k + 1);
6071                                         if (ip == NULL)
6072                                                 return NULL;  /*  Out of memory  */
6073                                         if (db[ii] == NULL) {
6074                                                 ip[1] = ii;
6075                                                 k = 2;
6076                                         } else {
6077                                                 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
6078                                                 k = db[ii][0] + 1;
6079                                         }
6080                                         if (db[jj] == NULL) {
6081                                                 ip[k++] = jj;
6082                                         } else {
6083                                                 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
6084                                                 k += db[jj][0];
6085                                         }
6086                                         ip[0] = k - 1;
6087                                         /*  Free old ones  */
6088                                         if (db[ii] != NULL)
6089                                                 free(db[ii]);
6090                                         if (db[jj] != NULL)
6091                                                 free(db[jj]);
6092                                         for (k = 0; k < ip[0]; k++)
6093                                                 db[ip[k + 1]] = ip;
6094                                         if (0) {
6095                                                 /*  For debug  */
6096                                                 printf("(%d,%d) matched: ", ii, jj);
6097                                                 for (k = 0; k < ip[0]; k++) {
6098                                                         printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
6099                                                 }
6100                                                 printf("]\n");
6101                                         }
6102                                 }
6103                                 list = list->next;
6104                         }
6105                 }
6106         }
6107         
6108         /*  Record the equivalent atoms with the lowest index for each atom  */
6109         result = (Int *)calloc(sizeof(Int), mol->natoms);
6110         for (i = 0; i < mol->natoms; i++)
6111                 result[i] = -1;
6112         for (i = 0; i < mol->natoms; i++) {
6113                 if (result[i] >= 0 || (ip = db[i]) == NULL)
6114                         continue;
6115                 k = mol->natoms;
6116                 for (j = 0; j < ip[0]; j++) {
6117                         kk = ip[j + 1];
6118                         if (kk < k)
6119                                 k = kk;
6120                 }
6121                 for (j = 0; j < ip[0]; j++) {
6122                         result[ip[j + 1]] = k;
6123                         db[ip[j + 1]] = NULL;
6124                 }
6125                 free(ip);
6126         }
6127         sDeallocateEqLists();
6128         return result;
6129 }
6130
6131 #pragma mark ====== Symmetry expansion ======
6132
6133 int
6134 MoleculeGetTransformForSymop(Molecule *mp, Symop symop, Transform *tf, int is_cartesian)
6135 {
6136         Transform t;
6137         if (mp == NULL || mp->cell == NULL)
6138                 return -1;
6139         if (symop.sym >= mp->nsyms && symop.sym != 0)
6140                 return -2;
6141         memmove(*tf, SYMMETRY_AT_INDEX(mp->syms, symop.sym), sizeof(Transform));
6142         (*tf)[9] += symop.dx;
6143         (*tf)[10] += symop.dy;
6144         (*tf)[11] += symop.dz;
6145         if (is_cartesian) {
6146                 TransformMul(t, *tf, mp->cell->rtr);
6147                 TransformMul(*tf, mp->cell->tr, t);
6148         }
6149         return 0;
6150 }
6151
6152 int
6153 MoleculeGetSymopForTransform(Molecule *mp, const Transform tf, Symop *symop, int is_cartesian)
6154 {
6155         Transform t;
6156         int i, j, n[3];
6157         if (mp == NULL || mp->cell == NULL)
6158                 return -1;
6159         if (mp->nsyms == 0)
6160                 return -2;
6161         if (is_cartesian) {
6162                 TransformMul(t, tf, mp->cell->tr);
6163                 TransformMul(t, mp->cell->rtr, t);
6164         } else {
6165                 memmove(t, tf, sizeof(Transform));
6166         }
6167         for (i = 0; i < mp->nsyms; i++) {
6168                 Transform *tp = mp->syms + i;
6169                 for (j = 0; j < 9; j++) {
6170                         if (fabs((*tp)[j] - t[j]) > 1e-4)
6171                                 break;
6172                 }
6173                 if (j == 9) {
6174                         for (j = 9; j < 12; j++) {
6175                                 double f1 = t[j] - (*tp)[j];
6176                                 double f2 = floor(f1 + 0.5);
6177                                 if (fabs(f1 - f2) > 1e-4)
6178                                         break;
6179                                 n[j - 9] = f2;
6180                         }
6181                         if (j == 12) {
6182                                 /*  Found  */
6183                                 symop->sym = i;
6184                                 symop->dx = n[0];
6185                                 symop->dy = n[1];
6186                                 symop->dz = n[2];
6187                                 symop->alive = (SYMOP_ALIVE((*symop)) != 0);
6188                                 return 0;
6189                         }
6190                 }
6191         }
6192         return -3;  /*  Not found  */
6193 }
6194
6195 int
6196 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
6197 {
6198         if (mp == NULL)
6199                 return 1;
6200         if (symop.sym >= mp->nsyms && symop.sym != 0)
6201                 return 2;
6202         if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
6203                 TransformVec(vpout, mp->cell->rtr, vpin);
6204                 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpout);
6205                 vpout->x += symop.dx;
6206                 vpout->y += symop.dy;
6207                 vpout->z += symop.dz;
6208                 TransformVec(vpout, mp->cell->tr, vpout);
6209         } else {
6210                 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpin);
6211                 vpout->x += symop.dx;
6212                 vpout->y += symop.dy;
6213                 vpout->z += symop.dz;
6214         }
6215         return 0;
6216 }
6217
6218 /*  Add expanded atoms. Returns the number of newly created atoms.
6219         If indices is non-NULL, it should be an array of Int with at least 
6220         IntGroupGetCount(group) entries, and on return it contains the
6221     indices of the expanded atoms (may be existing atoms if the expanded
6222     atoms are already present)  */
6223 int
6224 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group, Int *indices)
6225 {
6226         int i, n, n0, n1, count, *table;
6227         Atom *ap;
6228         IntGroupIterator iter;
6229         Transform tr;
6230
6231         if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
6232                 return -1;
6233         if (symop.sym >= mp->nsyms)
6234                 return -2;
6235
6236         /*  Create atoms, with avoiding duplicates  */
6237         n0 = n1 = mp->natoms;
6238         table = (int *)malloc(sizeof(int) * n0);
6239         if (table == NULL)
6240                 return -3;
6241         for (i = 0; i < n0; i++)
6242                 table[i] = -1;
6243         IntGroupIteratorInit(group, &iter);
6244         MoleculeGetTransformForSymop(mp, symop, &tr, 0);
6245         __MoleculeLock(mp);
6246         for (i = 0; i < count; i++) {
6247                 int n2, base;
6248                 Symop symop1;
6249                 Atom *ap2;
6250                 Vector nr, dr;
6251                 n = IntGroupIteratorNext(&iter);
6252                 ap = ATOM_AT_INDEX(mp->atoms, n);
6253                 if (SYMOP_ALIVE(ap->symop)) {
6254                         /*  Calculate the cumulative symop  */
6255                         Transform t1;
6256                         MoleculeGetTransformForSymop(mp, ap->symop, &t1, 0);
6257                         TransformMul(t1, tr, t1);
6258                         if (MoleculeGetSymopForTransform(mp, t1, &symop1, 0) != 0) {
6259                                 if (indices != NULL)
6260                                         indices[i] = -1;
6261                                 continue;  /*  Skip this atom  */
6262                         }
6263                         base = ap->symbase;
6264                 } else {
6265                         symop1 = symop;
6266                         base = n;
6267                 }
6268                 /*  Is this expansion already present?  */
6269                 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
6270                         if (ap2->symbase == base && SYMOP_EQUAL(symop1, ap2->symop))
6271                                 break;
6272                 }
6273                 if (n2 < n0) {
6274                         /*  If yes, then skip it  */
6275                         if (indices != NULL)
6276                                 indices[i] = n2;
6277                         continue;
6278                 }
6279                 /*  Is the expanded position coincides with itself?  */
6280                 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
6281                 VecSub(dr, ap->r, nr);
6282                 if (VecLength2(dr) < 1e-6) {
6283                         /*  If yes, then this atom is included but no new atom is created  */
6284                         table[n] = n;
6285                         if (indices != NULL)
6286                                 indices[i] = n;
6287                 } else {
6288                         /*  Create a new atom  */
6289                         Atom newAtom;
6290                         AtomDuplicate(&newAtom, ap);
6291                         MoleculeCreateAnAtom(mp, &newAtom, -1);
6292                         AtomClean(&newAtom);
6293                         ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
6294                         ap2->r = nr;
6295                         ap2->symbase = n;
6296                         ap2->symop = symop;
6297                         ap2->symop.alive = (symop.dx != 0 || symop.dy != 0 || symop.dz != 0 || symop.sym != 0);
6298                         table[n] = n1;  /*  The index of the new atom  */
6299                         MoleculeSetAnisoBySymop(mp, n1);  /*  Recalculate anisotropic parameters according to symop  */
6300                         if (indices != NULL)
6301                                 indices[i] = n1;
6302                         n1++;
6303                 }
6304         }
6305         IntGroupIteratorRelease(&iter);
6306
6307         /*  Create bonds  */
6308         for (i = 0; i < n0; i++) {
6309                 int b[2];
6310                 Int *cp;
6311                 b[0] = table[i];
6312                 if (b[0] < 0 || b[0] == i)
6313                         continue;
6314                 ap = ATOM_AT_INDEX(mp->atoms, i);
6315                 cp = AtomConnectData(&ap->connect);
6316                 for (n = 0; n < ap->connect.count; n++) {
6317                         b[1] = table[cp[n]];
6318                         if (b[1] < 0)
6319                                 continue;
6320                         if (b[1] > n0 && b[0] > b[1])
6321                                 continue;
6322                         MoleculeAddBonds(mp, 1, b);
6323                 }
6324         }
6325         mp->needsMDRebuild = 1;
6326         __MoleculeUnlock(mp);
6327         free(table);
6328         return n1 - n0;  /*  The number of added atoms  */
6329 }
6330
6331 /*  Recalculate the coordinates of symmetry expanded atoms.
6332         Returns the number of affected atoms.
6333     If group is non-NULL, only the expanded atoms whose base atoms are in the
6334     given group are considered.
6335         If groupout and vpout are non-NULL, the indices of the affected atoms
6336         and the original positions are returned (for undo operation).
6337         The pointers returned in *groupout and *vpout must be released and 
6338         free()'ed by the caller  */
6339 int
6340 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
6341 {
6342         int i, count;
6343         Atom *ap, *bp;
6344         if (mp == NULL || mp->natoms == 0 || mp->nsyms == 0)
6345                 return 0;
6346         if (groupout != NULL && vpout != NULL) {
6347                 *groupout = IntGroupNew();
6348                 if (*groupout == NULL)
6349                         return -1;
6350                 *vpout = (Vector *)malloc(sizeof(Vector) * mp->natoms);
6351                 if (*vpout == NULL) {
6352                         IntGroupRelease(*groupout);
6353                         return -1;
6354                 }
6355         } else groupout = NULL; /* To simplify test for validity of groupout/vpout */
6356         
6357         __MoleculeLock(mp);
6358         count = 0;
6359         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6360                 Vector nr, dr;
6361                 if (!SYMOP_ALIVE(ap->symop))
6362                         continue;
6363                 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
6364                         continue;
6365                 bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6366                 MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
6367                 VecSub(dr, nr, ap->r);
6368                 if (VecLength2(dr) < 1e-20)
6369                         continue;
6370                 if (groupout != NULL) {
6371                         (*vpout)[count] = ap->r;
6372                         IntGroupAdd(*groupout, i, 1);
6373                 }
6374                 ap->r = nr;
6375                 count++;
6376         }
6377         mp->needsMDCopyCoordinates = 1;
6378         __MoleculeUnlock(mp);
6379         if (groupout != NULL) {
6380                 if (count == 0) {
6381                         free(*vpout);
6382                         *vpout = NULL;
6383                         IntGroupRelease(*groupout);
6384                         *groupout = NULL;
6385                 } else {
6386                         *vpout = (Vector *)realloc(*vpout, sizeof(Vector) * count);
6387                 }
6388         }
6389         return count;
6390 }
6391
6392 #pragma mark ====== Show/hide atoms ======
6393
6394 static void
6395 sMoleculeNotifyChangeAppearance(Molecule *mp)
6396 {
6397         /*  TODO: Finer control of notification types may be necessary  */
6398         MoleculeCallback_notifyModification(mp, 0);
6399 }
6400
6401
6402 static void
6403 sMoleculeUnselectHiddenAtoms(Molecule *mp)
6404 {
6405         int i;
6406         if (mp == NULL || mp->selection == NULL)
6407                 return;
6408         for (i = 0; i < mp->natoms; i++) {
6409                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6410                 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
6411                         IntGroupRemove(mp->selection, i, 1);
6412         }
6413         sMoleculeNotifyChangeAppearance(mp);
6414 }
6415
6416 int
6417 MoleculeShowAllAtoms(Molecule *mp)
6418 {
6419         int i;
6420         if (mp == NULL)
6421                 return 0;
6422         for (i = 0; i < mp->natoms; i++) {
6423                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6424                 ap->exflags &= ~kAtomHiddenFlag;
6425         }
6426         sMoleculeNotifyChangeAppearance(mp);
6427         return 1;
6428 }
6429
6430 int
6431 MoleculeShowReverse(Molecule *mp)
6432 {
6433         int i;
6434         if (mp == NULL)
6435                 return 0;
6436         for (i = 0; i < mp->natoms; i++) {
6437                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6438                 ap->exflags ^= kAtomHiddenFlag;
6439         }
6440         sMoleculeUnselectHiddenAtoms(mp);
6441         sMoleculeNotifyChangeAppearance(mp);
6442         return 1;
6443 }
6444
6445 int
6446 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
6447 {
6448         int i;
6449         if (mp == NULL || ig == NULL)
6450                 return 0;
6451         for (i = 0; i < mp->natoms; i++) {
6452                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6453                 if (ap->exflags & kAtomHiddenFlag)
6454                         continue;  /*  Already hidden  */
6455                 if (IntGroupLookupPoint(ig, i) >= 0)
6456                         ap->exflags |= kAtomHiddenFlag;
6457         }
6458         sMoleculeUnselectHiddenAtoms(mp);
6459         sMoleculeNotifyChangeAppearance(mp);
6460         return 1;
6461 }
6462
6463 #pragma mark ====== Reversible Editing ======
6464
6465 /*
6466 static void
6467 sMoleculeNotifyModification(Molecule *mp)
6468 {
6469         **  TODO: Finer control of notification types may be necessary  **
6470         MoleculeCallback_notifyModification(mp, 0);
6471 }
6472 */
6473
6474 /*  Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup  */
6475 int
6476 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
6477 {
6478         int n1, n2, n3, i;
6479         if (where == NULL) {
6480                 /*  Append the new objects at the end  */
6481                 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
6482                 return 0;
6483         }
6484         n1 = IntGroupGetCount(where);  /*  Position to get new object  */
6485         n2 = nobjs;                    /*  Position to get old object  */
6486         n3 = n1 + n2;                  /*  Position to place new/old object  */
6487         for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
6488                 int start = IntGroupGetStartPoint(where, i);
6489                 int end = IntGroupGetEndPoint(where, i);
6490                 if (end < n3) {
6491                         /*  old[end-(n3-n2)..n2-1] is moved to old[end..n3-1]  */
6492                         memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
6493                         n2 = end - (n3 - n2);
6494                         n3 = end;
6495                 }
6496                 /*  new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1]  */
6497                 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
6498                 n3 -= end - start;
6499                 n1 -= end - start;
6500         }
6501         return 0;
6502 }
6503
6504 /*  Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
6505 int
6506 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6507 {
6508         int n1, n2, n3, start, end, i;
6509         if (objs == NULL || where == NULL)
6510                 return 1;  /*  Bad argument  */
6511         n1 = 0;  /*  Position to move remaining elements to */
6512         n2 = 0;  /*  Position to move remaining elements from  */
6513         n3 = 0;  /*  Position to move removed elements to  */
6514         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6515                 end = IntGroupGetEndPoint(where, i);
6516                 if (n2 < start) {
6517                         /*  Move (start - n2) elements from objs[n2] to objs[n1]  */
6518                         if (n1 < n2)
6519                                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
6520                         n1 += start - n2;
6521                         n2 = start;
6522                 }
6523                 /*  Move (end - start) elements from objs[n2] to clip[n3]  */
6524                 if (clip != NULL)
6525                         memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
6526                 n3 += (end - start);
6527                 n2 += (end - start);
6528         }
6529         /*  Move (nobjs - n2) elements from objs[n2] to objs[n1]  */
6530         if (nobjs > n2)
6531                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
6532         return 0;
6533 }
6534
6535 /*  Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
6536 int
6537 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6538 {
6539         int n1, start, end, i;
6540         if (objs == NULL || where == NULL)
6541                 return 1;  /*  Bad argument  */
6542         n1 = 0;  /*  Position to move removed elements to  */
6543         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6544                 end = IntGroupGetEndPoint(where, i);
6545                 /*  Copy (end - start) elements from objs[start] to clip[n1]  */
6546                 if (clip != NULL)
6547                         memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
6548                 n1 += (end - start);
6549         }
6550         return 0;
6551 }
6552
6553 /*  Create a new atom with no bonding information. ap must _not_ be inside the given molecule
6554    (Use AtomDuplicate() first) */
6555 int
6556 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
6557 {
6558     Atom *ap1, *api;
6559         int i;
6560         if (mp == NULL || ap == NULL || mp->noModifyTopology)
6561                 return -1;
6562         __MoleculeLock(mp);
6563         if (pos < 0 || pos >= mp->natoms)
6564                 pos = mp->natoms;
6565         ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
6566         if (ap1 == NULL)
6567                 goto error;  /*  Out of memory  */
6568         ap1 = ATOM_AT_INDEX(mp->atoms, pos);
6569         if (pos < mp->natoms - 1) {
6570                 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6571         }
6572         if (AtomDuplicate(ap1, ap) == NULL) {
6573                 /*  Cannot duplicate: restore the original state  */
6574                 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6575                 mp->natoms--;
6576                 goto error;
6577         }
6578         ap1->connect.count = 0;
6579         if (ap1->resSeq >= mp->nresidues)
6580                 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
6581         if (ap1->resName[0] == 0)
6582           strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
6583         if (ap1->segName[0] == 0)
6584           strncpy(ap1->segName, "MAIN", 4);
6585         if (pos < mp->natoms - 1) {
6586                 /*  Renumber the connect table, bonds, angles, etc. */
6587                 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
6588                         int j;
6589                         Int *cp;
6590                         for (j = 0; j < api->connect.count; j++) {
6591                                 cp = AtomConnectData(&api->connect);
6592                                 if (cp[j] >= pos)
6593                                         cp[j]++;
6594                         }
6595                 }
6596                 for (i = 0; i < mp->nbonds * 2; i++) {
6597                         if (mp->bonds[i] >= pos)
6598                                 mp->bonds[i]++;
6599                 }
6600                 for (i = 0; i < mp->nangles * 3; i++) {
6601                         if (mp->angles[i] >= pos)
6602                                 mp->angles[i]++;
6603                 }
6604                 for (i = 0; i < mp->ndihedrals * 4; i++) {
6605                         if (mp->dihedrals[i] >= pos)
6606                                 mp->dihedrals[i]++;
6607                 }
6608                 for (i = 0; i < mp->nimpropers * 4; i++) {
6609                         if (mp->impropers[i] >= pos)
6610                                 mp->impropers[i]++;
6611                 }
6612         }
6613         mp->nframes = -1;  /*  Should be recalculated later  */
6614         MoleculeIncrementModifyCount(mp);
6615         mp->needsMDRebuild = 1;
6616         __MoleculeUnlock(mp);
6617         return pos;
6618 error:
6619         __MoleculeUnlock(mp);
6620         return -1;
6621 }
6622
6623 /*  Merge two molecules. We use this procedure for all add-atom operations.  */
6624 /*  resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
6625 int
6626 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, int resSeqOffset)
6627 {
6628         Int nsrc, ndst;
6629         Int i, j, n1, n2, n3, n4, *cp;
6630         Int *new2old, *old2new;
6631         Atom *ap;
6632         if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
6633                 return 0;  /*  Do nothing  */
6634
6635         if (dst->noModifyTopology)
6636                 return 1;  /*  Prohibited operation  */
6637
6638         if (where != NULL && IntGroupGetCount(where) != src->natoms)
6639                 return 1;  /*  Bad parameter  */
6640
6641         __MoleculeLock(dst);
6642         nsrc = src->natoms;
6643         ndst = dst->natoms;
6644         if (resSeqOffset < 0)
6645                 resSeqOffset = 0;
6646
6647         /*  Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
6648             and ndst..ndst+nsrc-1 are for atoms in src.  */ 
6649         new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
6650         if (new2old == NULL)
6651                 goto panic;
6652         old2new = new2old + ndst + nsrc;
6653         n1 = 0;  /*  dst index  */
6654         n2 = 0;  /*  src index  */
6655         n3 = 0;  /*  "merged" index  */
6656         i = 0;
6657         while (n1 < ndst || n2 < nsrc) {
6658                 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
6659                         n4 = ndst - n1;
6660                 else n4 -= n3;
6661                 /*  n4 elements from dst[n1] will go to merged[n3]  */
6662                 for (j = 0; j < n4; j++) {
6663                         old2new[n1 + j] = n3 + j;
6664                         new2old[n3 + j] = n1 + j;
6665                 }
6666                 n3 += n4;
6667                 n1 += n4;
6668                 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
6669                         n4 = nsrc - n2;
6670                 /*  n4 elements from src[n2] will go to merged[n3]  */
6671                 for (j = 0; j < n4; j++) {
6672                         old2new[ndst + n2 + j] = n3 + j;
6673                         new2old[n3 + j] = ndst + n2 + j;
6674                 }
6675                 n3 += n4;
6676                 n2 += n4;
6677                 i++;
6678         }
6679
6680         /*  Expand the destination array  */
6681         if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
6682                 goto panic;
6683
6684         /*  Move the atoms  */
6685         if (where == NULL) {
6686                 /*  Duplicate atoms to the end of the destination array  */
6687                 for (i = 0; i < nsrc; i++) {
6688                         if (AtomDuplicate(ATOM_AT_INDEX(dst->atoms, ndst + i), ATOM_AT_INDEX(src->atoms, i)) == NULL)
6689                                 goto panic;
6690                 }
6691         //      memmove(ATOM_AT_INDEX(dst->atoms, ndst), src->atoms, gSizeOfAtomRecord * nsrc);
6692         } else {
6693                 /*  Duplicate to a temporary storage and then insert  */
6694                 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
6695                 if (tempatoms == NULL)
6696                         goto panic;
6697                 for (i = 0; i < nsrc; i++) {
6698                         if (AtomDuplicate(ATOM_AT_INDEX(tempatoms, i), ATOM_AT_INDEX(src->atoms, i)) == NULL)
6699                                 goto panic;
6700                 }
6701                 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
6702                         goto panic;
6703                 free(tempatoms);
6704         }
6705         dst->natoms = ndst + nsrc;
6706
6707         /*  Renumber the atom indices in connect[] and symbase, and modify the residue numbers  */
6708         for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
6709                 if (new2old[i] < ndst) {
6710                         /*  This atom is from dst  */
6711                         n1 = 0;
6712                 } else {
6713                         /*  This atom is from src  */
6714                         n1 = ndst;  /*  Offset to the internal number  */
6715                         if (ap->resSeq != 0)
6716                                 ap->resSeq += resSeqOffset;  /*  Modify residue number  */
6717                 }
6718                 cp = AtomConnectData(&ap->connect);
6719                 for (j = 0; j < ap->connect.count; j++)
6720                         cp[j] = old2new[cp[j] + n1];
6721                 if (SYMOP_ALIVE(ap->symop))
6722                         ap->symbase = old2new[ap->symbase + n1];
6723         }
6724         
6725         /*  Move the bonds, angles, dihedrals, impropers  */
6726         for (i = 0; i < 4; i++) {
6727                 Int *nitems, *nitems_src;
6728                 Int **items, **items_src;
6729                 Int nsize;  /*  Number of Ints in one element  */
6730                 switch (i) {
6731                         case 0:
6732                                 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
6733                         case 1:
6734                                 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
6735                         case 2:
6736                                 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
6737                         case 3:
6738                                 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
6739                 }
6740                 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
6741                 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
6742                 /*  Keep the old number of entries in dst, because it is updated by AssignArray()  */
6743                 n1 = *nitems;
6744                 /*  Also keep the old number of entries in src, in case src and dst point the same molecule  */
6745                 n2 = *nitems_src;
6746                 /*  Expand the array  */
6747                 if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
6748                         goto panic;
6749                 /*  Copy the items  */
6750                 memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
6751                 /*  Renumber  */
6752                 for (j = 0; j < n1 * nsize; j++)
6753                         (*items)[j] = old2new[(*items)[j]];
6754                 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
6755                         (*items)[j] = old2new[(*items)[j] + ndst];
6756         }
6757         
6758         /*  Merge parameters  */
6759         if (src->par != NULL) {
6760                 UnionPar *up1, *up2;
6761                 int type;
6762                 IntGroup *ig;
6763                 if (dst->par == NULL)
6764                         dst->par = ParameterNew();
6765                 else {
6766                         /*  Renumber existing parameters  */
6767                         for (type = kFirstParType; type <= kLastParType; type++) {
6768                                 n1 = ParameterGetCountForType(dst->par, type);
6769                                 for (i = 0; i < n1; i++) {
6770                                         up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
6771                                         ParameterRenumberAtoms(type, up1, ndst, old2new);
6772                                 }
6773                         }
6774                 }
6775                 ig = IntGroupNew();
6776                 for (type = kFirstParType; type <= kLastParType; type++) {
6777                         n1 = ParameterGetCountForType(src->par, type);
6778                         n2 = ParameterGetCountForType(dst->par, type);
6779                         if (n1 == 0)
6780                                 continue;
6781                         /*  Determine which parameter should be copied from src to dst  */
6782                         for (i = 0; i < n1; i++) {
6783                                 UInt types[4];
6784                                 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
6785                                 n3 = ParameterGetAtomTypes(type, up1, types);
6786                                 for (j = 0; j < n3; j++) {
6787                                         /*  If it includes explicit atom index, then it should be copied  */
6788                                         if (types[j] < kAtomTypeMinimum) {
6789                                                 IntGroupAdd(ig, i, 1);
6790                                                 break;
6791                                         }
6792                                 }
6793                                 if (j == n3) {
6794                                         for (j = 0; j < n2; j++) {
6795                                                 up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
6796                                                 if (ParameterCompare(up1, up2, type))
6797                                                         break;
6798                                         }
6799                                         if (j >= n2)
6800                                                 /*  This is an unknown parameter; should be copied  */
6801                                                 IntGroupAdd(ig, i, 1);
6802                                 }
6803                         }
6804                         n1 = IntGroupGetCount(ig);
6805                         if (n1 == 0)
6806                                 continue;
6807                         up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
6808                         if (up1 == NULL)
6809                                 goto panic;
6810                         /*  Copy parameters and renumber indices if necessary  */
6811                         for (i = 0; i < n1; i++) {
6812                                 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
6813                                 if (up2 == NULL)
6814                                         continue;
6815                                 up1[i] = *up2;
6816                                 ParameterRenumberAtoms(type, up1 + i, nsrc, old2new + ndst);
6817                         }
6818                         /*  Merge parameters  */
6819                         IntGroupClear(ig);
6820                         IntGroupAdd(ig, n2, n1);
6821                         if (ParameterInsert(dst->par, type, up1, ig) < n1)
6822                                 goto panic;
6823                         IntGroupClear(ig);
6824                         free(up1);
6825                 }
6826                 IntGroupRelease(ig);
6827         }
6828         
6829         /*  Copy the residues if necessary  */
6830         /*  src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
6831             However, 1+resSeqOffset should not overwrite the existing residue in dst;
6832                 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1].  */
6833         n1 = dst->nresidues;
6834         if (1 + resSeqOffset < n1) {
6835                 n2 = n1;
6836         } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
6837         if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
6838                 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
6839                         goto panic;
6840                 memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
6841         }
6842
6843         MoleculeCleanUpResidueTable(dst);
6844         
6845         free(new2old);
6846         dst->nframes = -1;  /*  Should be recalculated later  */
6847
6848         MoleculeIncrementModifyCount(dst);
6849         dst->needsMDRebuild = 1;
6850         __MoleculeUnlock(dst);
6851         return 0;
6852
6853   panic:
6854         __MoleculeUnlock(dst);
6855     Panic("Low memory while adding atoms");
6856         return 1;  /*  Not reached  */
6857 }
6858
6859 static int
6860 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag)
6861 {
6862         Int nsrc, ndst, nsrcnew;
6863         Int i, j, n1, n2, n3, n4, *cp;
6864         Int *new2old, *old2new;
6865         IntGroup *move_g, *del_g, *remain_g, *dst_par_g, *remove_par_g;
6866         Molecule *dst;
6867         Atom *ap, *dst_ap;
6868         UnionPar *up;
6869         
6870         if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
6871                 /*  Do nothing  */
6872                 if (dstp != NULL)
6873                         *dstp = NULL;
6874                 return 0;
6875         }
6876         
6877         if (src->noModifyTopology && moveFlag)
6878                 return 1;  /*  Prohibit editing  */
6879
6880         if ((ndst = IntGroupGetCount(where)) > src->natoms)
6881                 return 1;  /*  Bad parameter  */
6882
6883         __MoleculeLock(src);
6884         
6885         nsrc = src->natoms;
6886         nsrcnew = nsrc - ndst;
6887         if (resSeqOffset < 0)
6888                 resSeqOffset = 0;
6889
6890         /*  Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
6891             and nsrcnew..nsrc-1 are for atoms moved into dst.  */ 
6892         new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
6893         if (new2old == NULL)
6894                 goto panic;
6895         old2new = new2old + nsrc;
6896         n1 = 0;  /*  src index  */
6897         n2 = 0;  /*  dst index  */
6898         n3 = 0;  /*  src index after "unmerge"  */
6899         i = 0;
6900         while (n1 < nsrc || n2 < ndst) {
6901                 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
6902                         n4 = nsrc - n1;
6903                 else n4 -= n1;
6904                 /*  n4 elements from src[n1] will go to unmerged[n3]  */
6905                 for (j = 0; j < n4; j++) {
6906                         old2new[n1 + j] = n3 + j;
6907                         new2old[n3 + j] = n1 + j;
6908                 }
6909                 n3 += n4;
6910                 n1 += n4;
6911                 if ((n4 = IntGroupGetInterval(where, i)) < 0)
6912                         n4 = nsrc - n1;
6913                 /*  n4 elements from src[n1] will go to dst[n2]  */
6914                 for (j = 0; j < n4; j++) {
6915                         old2new[n1 + j] = nsrcnew + n2 + j;
6916                         new2old[nsrcnew + n2 + j] = n1 + j;
6917                 }
6918                 n1 += n4;
6919                 n2 += n4;
6920                 i++;
6921         }
6922
6923         /*  Atoms to remain in the source group  */
6924         if (moveFlag) {
6925                 remain_g = IntGroupNewWithPoints(0, nsrc, -1);
6926                 IntGroupRemoveIntGroup(remain_g, where);
6927         } else remain_g = NULL;
6928         
6929         /*  Find parameters to be moved to the dst (dst_par_g), and to be removed from the src (remove_par_g) */
6930         if (src->par != NULL) {
6931                 dst_par_g = IntGroupNew();
6932                 if (moveFlag)
6933                         remove_par_g = IntGroupNew();
6934                 else remove_par_g = NULL;
6935                 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
6936                         n2 = ParameterGetCountForType(src->par, n1);
6937                         if (n2 == 0)
6938                                 continue;
6939                         for (i = 0; i < n2; i++) {
6940                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
6941                                 if (ParameterIsRelevantToAtomGroup(n1, up, src->atoms, where)) {
6942                                         /*  This parameter is to be copied to dst  */
6943                                         IntGroupAdd(dst_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
6944                                 }
6945                                 if (moveFlag && !ParameterIsRelevantToAtomGroup(n1, up, src->atoms, remain_g)) {
6946                                         /*  This parameter is to be removed  */
6947                                         IntGroupAdd(remove_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
6948                                 }
6949                         }
6950                 }
6951         } else dst_par_g = remove_par_g = NULL;
6952                 
6953         /*  Make a new molecule  */
6954         if (dstp != NULL) {
6955                 dst = MoleculeNew();
6956                 if (dst == NULL)
6957                         goto panic;
6958                 /*  Expand the destination array  */
6959                 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
6960                         goto panic;
6961                 dst_ap = dst->atoms;
6962         } else {
6963                 dst = NULL;
6964                 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
6965                 if (dst_ap == NULL)
6966                         goto panic;
6967         }
6968         
6969         /*  Move the atoms  */
6970         if (moveFlag) {
6971                 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
6972                         goto panic;
6973                 src->natoms = nsrcnew;
6974                 if (dst == NULL) {
6975                         /*  The atom record must be deallocated correctly  */
6976                         for (i = 0; i < ndst; i++)
6977                                 AtomClean(ATOM_AT_INDEX(dst_ap, i));
6978                 }
6979         } else {
6980                 if (dst != NULL) {
6981                         for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
6982                                 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
6983                 }
6984         }
6985         
6986         if (dst == NULL) {
6987                 /*  The dummy destination array is no longer needed  */
6988                 free(dst_ap);
6989                 dst_ap = NULL;
6990         }
6991         
6992         /*  Renumber the atom indices in connect[]  */
6993         if (moveFlag) {
6994                 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
6995                         cp = AtomConnectData(&ap->connect);
6996                         for (j = n1 = 0; j < ap->connect.count; j++) {
6997                                 n2 = old2new[cp[j]];
6998                                 if (n2 < nsrcnew)
6999                                         cp[n1++] = n2;
7000                         }
7001                         AtomConnectResize(&ap->connect, n1);
7002                 }
7003         }
7004         
7005         /*  Renumber the atom indices in connect[] and the residue indices  */
7006         if (dst != NULL) {
7007                 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7008                         if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
7009                                 ap->resSeq -= resSeqOffset;
7010                         else ap->resSeq = 0;
7011                         cp = AtomConnectData(&ap->connect);
7012                         for (j = n1 = 0; j < ap->connect.count; j++) {
7013                                 n2 = old2new[cp[j]] - nsrcnew;
7014                                 if (n2 >= 0)
7015                                         cp[n1++] = n2;
7016                         }
7017                         AtomConnectResize(&ap->connect, n1);
7018                 }
7019         }
7020
7021         /*  Separate the bonds, angles, dihedrals, impropers  */
7022         /*  TODO: Improper torsions should also be copied!  */
7023         move_g = IntGroupNew();
7024         del_g = IntGroupNew();
7025         if (move_g == NULL || del_g == NULL)
7026                 goto panic;
7027         for (i = 0; i < 4; i++) {
7028                 Int *nitems, *nitems_dst;
7029                 Int **items, **items_dst;
7030                 Int nsize;  /*  Number of Ints in one element  */
7031                 unsigned char *counts;
7032                 switch (i) {
7033                         case 0:
7034                                 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
7035                         case 1:
7036                                 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
7037                         case 2:
7038                                 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
7039                         case 3:
7040                                 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
7041                         default:
7042                                 nitems = NULL; items = NULL; nsize = 0; break;  /*  Not reached  */
7043                 }
7044                 if (dst != NULL) {
7045                         nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
7046                         items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
7047                 } else {
7048                         nitems_dst = NULL;
7049                         items_dst = NULL;
7050                 }
7051                 counts = (unsigned char *)calloc(1, *nitems);
7052                 /*  Find the entries that should be moved to dst  */
7053                 n2 = 0;
7054                 for (j = 0; j < *nitems * nsize; j++) {
7055                         n1 = old2new[(*items)[j]];
7056                         if (n1 >= nsrcnew)
7057                                 counts[j / nsize]++; /* Count the atom belonging to dst */ 
7058                 /*      if (n1 >= nsrcnew) {
7059                                 n1 -= nsrcnew;
7060                                 if (j % nsize == 0) {
7061                                         if (IntGroupAdd(sep, j / nsize, 1) != 0)
7062                                                 goto panic;
7063                                         n2++;
7064                                 }
7065                         }
7066                         (*items)[j] = n1; */
7067                 }
7068                 for (j = n2 = n3 = 0; j < *nitems; j++) {
7069                         if (counts[j] > 0) {
7070                                 /*  Remove from src  */
7071                                 n2++;
7072                                 if (IntGroupAdd(del_g, j, 1) != 0)
7073                                         goto panic;
7074                                 if (counts[j] == nsize) {
7075                                         /*  Move to dst  */
7076                                         n3++;
7077                                         if (IntGroupAdd(move_g, j, 1) != 0)
7078                                                 goto panic;
7079                                 }
7080                         }
7081                 }
7082                 if (n2 > 0) {
7083                         /*  Expand the destination array  */
7084                         if (items_dst != NULL && n3 > 0) {
7085                                 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
7086                                         goto panic;
7087                                 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
7088                                         goto panic;
7089                         }
7090                         /*  Remove from src  */
7091                         if (moveFlag) {
7092                                 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
7093                                         goto panic;
7094                                 (*nitems) -= n2;
7095                         }
7096                 }
7097                 /*  Renumber the entries  */
7098                 if (moveFlag) {
7099                         for (j = 0; j < *nitems * nsize; j++) {
7100                                 (*items)[j] = old2new[(*items)[j]];
7101                         }
7102                 }
7103                 if (items_dst != NULL) {
7104                         for (j = 0; j < *nitems_dst * nsize; j++) {
7105                                 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
7106                         }
7107                 }
7108                 free(counts);
7109                 IntGroupClear(move_g);
7110                 IntGroupClear(del_g);
7111         }
7112         IntGroupRelease(move_g);
7113         IntGroupRelease(del_g);
7114         
7115         /*  Copy the residues  */
7116         if (dst != NULL) {
7117                 /*  src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset)  */
7118                 n1 = src->nresidues - resSeqOffset;  /*  This will be dst->nresidues (if >0)  */
7119                 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
7120                         goto panic;
7121                 if (n1 > 1) {
7122                         memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
7123                 }
7124         }
7125
7126         /*  Copy the parameters to dst */
7127         if (dst != NULL && dst_par_g != NULL && (n2 = IntGroupGetCount(dst_par_g)) > 0) {
7128                 IntGroup *dst_new_g = IntGroupNew();
7129                 Int dst_par_count[kLastParType - kFirstParType + 1];
7130                 if (dst_new_g == NULL)
7131                         goto panic;
7132                 for (i = 0; i <= kLastParType - kFirstParType; i++)
7133                         dst_par_count[i] = 0;
7134                 up = (UnionPar *)calloc(sizeof(UnionPar), n2);
7135                 if (up == NULL)
7136                         goto panic;
7137                 if (ParameterCopy(src->par, kFirstParType, up, dst_par_g) < n2)
7138                         goto panic;
7139                 /*  Renumber the explicit atom indices  */
7140                 for (i = 0; i < nsrc; i++)
7141                         old2new[i] -= nsrcnew;  /*  new indices for atoms in dst; otherwise negative numbers  */
7142                 for (i = 0; i < n2; i++) {
7143                         /*  Renumber the indices, and count the number of parameters for each type  */
7144                         n1 = kFirstParType + IntGroupGetNthPoint(dst_par_g, i) / kParameterIndexOffset;
7145                         dst_par_count[n1 - kFirstParType]++;
7146                         ParameterRenumberAtoms(n1, up + i, nsrc, old2new);
7147                 }
7148                 for (i = 0; i < nsrc; i++)
7149                         old2new[i] += nsrcnew;
7150                 if (dst->par == NULL)
7151                         dst->par = ParameterNew();
7152                 for (i = 0; i <= kLastParType - kFirstParType; i++) {
7153                         if (dst_par_count[i] > 0)
7154                                 IntGroupAdd(dst_new_g, i * kParameterIndexOffset, dst_par_count[i]);
7155                 }
7156                 if (ParameterInsert(dst->par, kFirstParType, up, dst_new_g) < n2)
7157                         goto panic;
7158                 free(up);
7159                 IntGroupRelease(dst_new_g);
7160         }
7161         IntGroupRelease(dst_par_g);
7162
7163         /*  Remove the unused parameter. Note: the parameters that are in remove_par_g and not in 
7164             dst_par_g will disappear. To support undo, these parameters should be taken care separately.  */
7165         if (remove_par_g != NULL && (n2 = IntGroupGetCount(remove_par_g)) > 0) {
7166                 ParameterDelete(src->par, kFirstParType, NULL, remove_par_g);
7167         }
7168         IntGroupRelease(remove_par_g);
7169         
7170         /*  Renumber the parameter records remaining in the src  */
7171         if (moveFlag) {
7172                 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7173                         n2 = ParameterGetCountForType(src->par, n1);
7174                         for (i = 0; i < n2; i++) {
7175                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7176                                 ParameterRenumberAtoms(n1, up, nsrc, old2new);
7177                         }
7178                 }
7179         }
7180         
7181         /*  Clean up  */
7182         IntGroupRelease(remain_g);
7183         MoleculeCleanUpResidueTable(src);
7184         if (dst != NULL)
7185                 MoleculeCleanUpResidueTable(dst);
7186         free(new2old);
7187
7188         src->nframes = -1;  /*  Should be recalculated later  */
7189         if (dst != NULL)
7190                 dst->nframes = -1;  /*  Should be recalculated later  */
7191
7192         
7193         if (dstp != NULL)
7194                 *dstp = dst;
7195
7196         MoleculeIncrementModifyCount(src);
7197         src->needsMDRebuild = 1;
7198         __MoleculeUnlock(src);
7199         
7200         return 0;
7201
7202   panic:
7203         __MoleculeUnlock(src);
7204 /*    Panic("Low memory while removing atoms"); */
7205         return -1;
7206 }
7207
7208 /*  Separate molecule into two parts. The atoms specified by 'where' are moved
7209     from src to a new molecule, which is returned as *dstp. Dstp can be NULL, 
7210         in which case the moved atoms are discarded.  */
7211 int
7212 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset)
7213 {
7214         return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1);
7215 }
7216
7217 /*  Extract atoms from a given molecule into two parts. The atoms specified by 
7218         'where' are copied from src to a new molecule, which is returned as *dstp.
7219     If dummyFlag is non-zero, then the atoms that are not included in the group 
7220         but are connected to any atoms in the group are converted to "dummy" atoms 
7221         (i.e. with element "Du" and names beginning with an underscore) and included 
7222         in the new molecule object.  */
7223 int
7224 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
7225 {
7226         int retval;
7227
7228         /*  Extract the fragment  */
7229         retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0);
7230         if (retval != 0)
7231                 return retval;
7232
7233         if (dummyFlag) {
7234
7235                 /*  Search bonds crossing the molecule border  */
7236                 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
7237                 if (ig != NULL) {
7238                         IntGroupIterator iter;
7239                         Int i, idx;
7240                         idx = 1;
7241                         IntGroupIteratorInit(ig, &iter);
7242                         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7243                                 /*  The atoms at the border  */
7244                                 Int n1, n2, nn[3];
7245                                 Atom a, *ap;
7246                                 n1 = src->bonds[i*2];
7247                                 n2 = src->bonds[i*2+1];
7248                                 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
7249                                         int w = n1;
7250                                         n1 = n2;
7251                                         n2 = w;
7252                                         if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
7253                                                 continue;  /*  Actually this is an internal error  */
7254                                 }
7255                                 /*  n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule  */
7256                                 /*  Create a new dummy atom with the same segment/residue info with n1
7257                                     and the same position as n2  */
7258                                 ap = ATOM_AT_INDEX(src->atoms, n1);
7259                                 memset(&a, 0, gSizeOfAtomRecord);
7260                                 a.segSeq = ap->segSeq;
7261                                 memmove(a.segName, ap->segName, 4);
7262                                 a.resSeq = ap->resSeq;
7263                                 memmove(a.resName, ap->resName, 4);
7264                                 ElementToString(0, a.element);  /*  "Du"  */
7265                                 snprintf(a.aname, 4, "_%d", idx++);
7266                                 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
7267                                 /*  Add the dummy atom to the new molecule; nn[1] is the index
7268                                     of the new dummy atom in the new molecule  */
7269                                 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
7270                                 /*  Connect nn1 and nn2  */
7271                                 nn[2] = kInvalidIndex;
7272                                 MoleculeAddBonds(*dstp, 1, nn);
7273                         }
7274                         IntGroupIteratorRelease(&iter);
7275                         IntGroupRelease(ig);
7276                 }
7277         }
7278         
7279         return 0;
7280 }
7281
7282 int
7283 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds)
7284 {
7285         int i, j, n1, n2, n;
7286         Atom *ap;
7287         Int *bonds_tmp, *cp;
7288
7289         if (mp == NULL || bonds == NULL || nbonds <= 0)
7290                 return 0;
7291         if (mp->noModifyTopology)
7292                 return -4;  /*  Prohibited operation  */
7293
7294         /*  Check the bonds  */
7295         bonds_tmp = (Int *)malloc(sizeof(Int) * nbonds * 2);
7296         if (bonds_tmp == NULL)
7297                 return -4;  /*  Out of memory  */
7298         n = 0;
7299         for (i = 0; i < nbonds; i++) {
7300                 n1 = bonds[i * 2];
7301                 n2 = bonds[i * 2 + 1];
7302                 if (n1 < 0 || n1 >= mp->natoms || n2 < 0 || n2 >= mp->natoms)
7303                         return -1;  /*  Bad bond specification  */
7304                 if (n1 == n2)
7305                         return -5;
7306                 ap = ATOM_AT_INDEX(mp->atoms, n1);
7307                 /*  Check duplicates  */
7308                 cp = AtomConnectData(&ap->connect);
7309                 for (j = 0; j < ap->connect.count; j++) {
7310                         if (cp[j] == n2)
7311                                 break;
7312                 }
7313                 if (j == ap->connect.count) {
7314                         bonds_tmp[n * 2] = n1;
7315                         bonds_tmp[n * 2 + 1] = n2;
7316                         n++;
7317                 }
7318         }
7319         if (n == 0) {
7320                 /*  No bonds to add  */
7321                 free(bonds_tmp);
7322                 return 0;
7323         }
7324         
7325         __MoleculeLock(mp);
7326
7327         /*  Add connects[]  */
7328         for (i = 0; i < n; i++) {
7329                 n1 = bonds_tmp[i * 2];
7330                 n2 = bonds_tmp[i * 2 + 1];
7331                 ap = ATOM_AT_INDEX(mp->atoms, n1);
7332                 AtomConnectInsertEntry(&ap->connect, ap->connect.count, n2);
7333                 ap = ATOM_AT_INDEX(mp->atoms, n2);
7334                 AtomConnectInsertEntry(&ap->connect, ap->connect.count, n1);
7335         }
7336         
7337         /*  Expand the array and insert  */
7338         n1 = mp->nbonds;
7339 /*      if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, mp->nbonds + nb - 1, NULL) == NULL
7340         || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nb, sizeof(Int) * 2, where) != 0) */
7341         if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, mp->nbonds + n - 1, NULL) == NULL)
7342                 goto panic;
7343         memmove(mp->bonds + n1 * 2, bonds_tmp, sizeof(Int) * 2 * n);
7344
7345         /*  Add angles, dihedrals, impropers  */
7346         {
7347                 Int nangles, ndihedrals, nimpropers;
7348                 Int *angles, *dihedrals, *impropers;
7349                 Int k, n3, n4;
7350                 Int *ip, *cp1, *cp2;
7351                 Int temp[4];
7352                 Atom *ap1, *ap2;
7353
7354                 angles = dihedrals = impropers = NULL;
7355                 nangles = ndihedrals = nimpropers = 0;
7356
7357                 for (i = 0; i < n; i++) {
7358                         n1 = bonds_tmp[i * 2];
7359                         n2 = bonds_tmp[i * 2 + 1];
7360                         ap1 = ATOM_AT_INDEX(mp->atoms, n1);
7361                         ap2 = ATOM_AT_INDEX(mp->atoms, n2);
7362                         cp1 = AtomConnectData(&ap1->connect);
7363                         cp2 = AtomConnectData(&ap2->connect);
7364                         /*  Angles X-n1-n2  */
7365                         for (j = 0; j < ap1->connect.count; j++) {
7366                                 n3 = cp1[j];
7367                                 if (n3 == n2)
7368                                         continue;
7369                                 temp[0] = n3;
7370                                 temp[1] = n1;
7371                                 temp[2] = n2;
7372                                 for (k = 0; k < nangles; k++) {
7373                                         ip = angles + k * 3;
7374                                         if (ip[1] == n1 && ((ip[0] == n3 && ip[2] == n2) || (ip[0] == n2 && ip[2] == n3)))
7375                                                 break;
7376                                 }
7377                                 if (k == nangles) {
7378                                         if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7379                                                 goto panic;
7380                                 }
7381                                 /*  Dihedrals X-n1-n2-X  */
7382                                 for (k = 0; k < ap2->connect.count; k++) {
7383                                         n4 = cp2[k];
7384                                         if (n4 == n1 || n4 == n3)
7385                                                 continue;
7386                                         temp[3] = n4;
7387                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7388                                                 goto panic;
7389                                 }
7390                                 /*  Impropers X-n2-n1-X  */
7391                         /*      temp[1] = n2;
7392                                 temp[2] = n1;
7393                                 for (k = 0; k < ap1->connect.count; k++) {
7394                                         n4 = ap1->connects[k];
7395                                         if (n4 == n2 || n4 <= n3)
7396                                                 continue;
7397                                         temp[3] = n4;
7398                                         if (AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, temp) == NULL)
7399                                                 goto panic;
7400                                 } */
7401                         }
7402                         /*  Angles X-n2-n1  */
7403                         for (j = 0; j < ap2->connect.count; j++) {
7404                                 n3 = cp2[j];
7405                                 if (n3 == n1)
7406                                         continue;
7407                                 temp[0] = n1;
7408                                 temp[1] = n2;
7409                                 temp[2] = n3;
7410                                 for (k = 0; k < nangles; k++) {
7411                                         ip = angles + k * 3;
7412                                         if (ip[1] == n2 && ((ip[0] == n3 && ip[2] == n1) || (ip[0] == n1 && ip[2] == n3)))
7413                                                 break;
7414                                 }
7415                                 if (k == nangles) {
7416                                         if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7417                                                 goto panic;
7418                                 }
7419                         }
7420                 }
7421                 temp[0] = kInvalidIndex;
7422                 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7423                         goto panic;
7424                 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7425                         goto panic;
7426                 if (AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, temp) == NULL)
7427                         goto panic;
7428                 MoleculeAddAngles(mp, angles, NULL);
7429                 MoleculeAddDihedrals(mp, dihedrals, NULL);
7430                 MoleculeAddImpropers(mp, impropers, NULL);
7431                 if (angles != NULL)
7432                         free(angles);
7433                 if (dihedrals != NULL)
7434                         free(dihedrals);
7435                 if (impropers != NULL)
7436                         free(impropers);
7437         }
7438         
7439         MoleculeIncrementModifyCount(mp);
7440         mp->needsMDRebuild = 1;
7441         __MoleculeUnlock(mp);
7442
7443         free(bonds_tmp);
7444         return n;       
7445
7446   panic:
7447         __MoleculeUnlock(mp);
7448         Panic("Low memory while adding bonds");
7449         return -1;  /*  Not reached  */
7450 }
7451
7452 int
7453 MoleculeDeleteBonds(Molecule *mp, Int nbonds, const Int *bonds)
7454 {
7455         Int i, j, n1, n2, *cp;
7456         Atom *ap;
7457
7458         if (mp == NULL || nbonds <= 0)
7459                 return 0;
7460         if (mp->noModifyTopology)
7461                 return -4;  /*  Prohibited operation  */
7462
7463         __MoleculeLock(mp);
7464
7465         /*  Update connects[]  */
7466         for (i = 0; i < nbonds; i++) {
7467                 n1 = bonds[i * 2];
7468                 n2 = bonds[i * 2 + 1];
7469                 ap = ATOM_AT_INDEX(mp->atoms, n1);
7470                 cp = AtomConnectData(&ap->connect);
7471                 for (j = 0; j < ap->connect.count; j++) {
7472                         if (cp[j] == n2) {
7473                                 AtomConnectDeleteEntry(&ap->connect, j);
7474                                 break;
7475                         }
7476                 }
7477                 ap = ATOM_AT_INDEX(mp->atoms, n2);
7478                 cp = AtomConnectData(&ap->connect);
7479                 for (j = 0; j < ap->connect.count; j++) {
7480                         if (cp[j] == n1) {
7481                                 AtomConnectDeleteEntry(&ap->connect, j);
7482                                 break;
7483                         }
7484                 }
7485         }
7486
7487         /*  Remove bonds, angles, dihedrals, impropers  */      
7488         {
7489                 IntGroup *bg, *ag, *dg, *ig;
7490                 Int *ip;
7491
7492                 bg = IntGroupNew();
7493                 ag = IntGroupNew();
7494                 dg = IntGroupNew();
7495                 ig = IntGroupNew();
7496                 if (bg == NULL || ag == NULL || dg == NULL || ig == NULL)
7497                         goto panic;
7498                 for (i = 0; i < nbonds; i++) {
7499                         n1 = bonds[i * 2];
7500                         n2 = bonds[i * 2 + 1];
7501                         for (j = 0; j < mp->nbonds; j++) {
7502                                 ip = mp->bonds + j * 2;
7503                                 if ((ip[0] == n1 && ip[1] == n2)
7504                                  || (ip[1] == n1 && ip[0] == n2)) {
7505                                         if (IntGroupAdd(bg, j, 1) != 0)
7506                                                 goto panic;
7507                                 }
7508                         }
7509                         for (j = 0; j < mp->nangles; j++) {
7510                                 ip = mp->angles + j * 3;
7511                                 if ((ip[0] == n1 && ip[1] == n2)
7512                                  || (ip[1] == n1 && ip[0] == n2)
7513                                  || (ip[1] == n1 && ip[2] == n2)
7514                                  || (ip[2] == n1 && ip[1] == n2)) {
7515                                         if (IntGroupAdd(ag, j, 1) != 0)
7516                                                 goto panic;
7517                                 }
7518                         }
7519                         for (j = 0; j < mp->ndihedrals; j++) {
7520                                 ip = mp->dihedrals + j * 4;
7521                                 if ((ip[1] == n1 && ip[2] == n2)
7522                                  || (ip[2] == n1 && ip[1] == n2)) {
7523                                         if (IntGroupAdd(dg, j, 1) != 0)
7524                                                 goto panic;
7525                                 }
7526                         }
7527                         for (j = 0; j < mp->nimpropers; j++) {
7528                                 ip = mp->impropers + j * 4;
7529                                 if ((ip[0] == n1 && ip[2] == n2)
7530                                  || (ip[1] == n1 && ip[2] == n2)
7531                                  || (ip[3] == n1 && ip[2] == n2)
7532                                  || (ip[0] == n2 && ip[2] == n1)
7533                                  || (ip[1] == n2 && ip[2] == n1)
7534                                  || (ip[3] == n2 && ip[2] == n1)) {
7535                                         if (IntGroupAdd(ig, j, 1) != 0)
7536                                                 goto panic;
7537                                 }
7538                         }
7539                 }
7540                 if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, NULL, sizeof(Int) * 2, bg) != 0)
7541                         goto panic;
7542                 mp->nbonds -= IntGroupGetCount(bg);
7543                 
7544                 if (IntGroupGetCount(ag) > 0)
7545                         MoleculeDeleteAngles(mp, NULL, ag);
7546                 if (IntGroupGetCount(dg) > 0)
7547                         MoleculeDeleteDihedrals(mp, NULL, dg);
7548                 if (IntGroupGetCount(ig) > 0)
7549                         MoleculeDeleteImpropers(mp, NULL, ig);
7550                 IntGroupRelease(bg);
7551                 IntGroupRelease(ag);
7552                 IntGroupRelease(dg);
7553                 IntGroupRelease(ig);
7554         }
7555
7556         MoleculeIncrementModifyCount(mp);
7557         mp->needsMDRebuild = 1;
7558         __MoleculeUnlock(mp);
7559
7560         return nbonds;
7561
7562   panic:
7563         __MoleculeUnlock(mp);
7564         Panic("Low memory while removing bonds");
7565         return -1;  /*  Not reached  */
7566 }
7567
7568 int
7569 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
7570 {
7571         int n1, nc;
7572         if (mp == NULL || angles == NULL)
7573                 return 0;
7574         if (mp->noModifyTopology)
7575                 return -4;  /*  Prohibited operation  */
7576
7577         __MoleculeLock(mp);
7578         if (where != NULL)
7579                 nc = IntGroupGetCount(where);
7580         else {
7581                 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
7582                         ;
7583                 nc = n1;
7584         }
7585         if (nc > 0) {
7586                 n1 = mp->nangles;
7587                 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
7588                         || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
7589                         __MoleculeUnlock(mp);
7590                         Panic("Low memory while adding angles");
7591                 }
7592         }
7593         mp->needsMDRebuild = 1;
7594         __MoleculeUnlock(mp);
7595         return nc;
7596 }
7597
7598 int
7599 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
7600 {
7601         int nc;
7602         if (mp == NULL || where == NULL)
7603                 return 0;
7604         if (mp->noModifyTopology)
7605                 return -4;  /*  Prohibited operation  */
7606         __MoleculeLock(mp);
7607         if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
7608                 __MoleculeUnlock(mp);
7609                 Panic("Low memory while adding angles");
7610         }
7611         mp->nangles -= (nc = IntGroupGetCount(where));
7612         mp->needsMDRebuild = 1;
7613         __MoleculeUnlock(mp);
7614         return nc;
7615 }
7616
7617 int
7618 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
7619 {
7620         int n1, nc;
7621         if (mp == NULL || dihedrals == NULL)
7622                 return 0;
7623         if (mp->noModifyTopology)
7624                 return -4;  /*  Prohibited operation  */
7625         if (where != NULL)
7626                 nc = IntGroupGetCount(where);
7627         else {
7628                 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
7629                         ;
7630                 nc = n1;
7631         }
7632         if (nc <= 0)
7633                 return 0;
7634         n1 = mp->ndihedrals;
7635         __MoleculeLock(mp);
7636         if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
7637         || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
7638                 __MoleculeUnlock(mp);
7639                 Panic("Low memory while adding dihedrals");
7640         }
7641         mp->needsMDRebuild = 1;
7642         __MoleculeUnlock(mp);
7643         return nc;
7644 }
7645
7646 int
7647 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
7648 {       
7649         int nc;
7650         if (mp == NULL || where == NULL)
7651                 return 0;
7652         if (mp->noModifyTopology)
7653                 return -4;  /*  Prohibited operation  */
7654         __MoleculeLock(mp);
7655         if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
7656                 __MoleculeUnlock(mp);
7657                 Panic("Low memory while adding dihedrals");
7658         }
7659         mp->ndihedrals -= (nc = IntGroupGetCount(where));
7660         mp->needsMDRebuild = 1;
7661         __MoleculeUnlock(mp);
7662         return nc;
7663 }
7664
7665 int
7666 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
7667 {
7668         int n1, nc;
7669         if (mp == NULL || impropers == NULL)
7670                 return 0;
7671         if (mp->noModifyTopology)
7672                 return -4;  /*  Prohibited operation  */
7673         if (where != NULL)
7674                 nc = IntGroupGetCount(where);
7675         else {
7676                 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
7677                         ;
7678                 nc = n1;
7679         }
7680         if (nc <= 0)
7681                 return 0;
7682         n1 = mp->nimpropers;
7683         __MoleculeLock(mp);
7684         if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
7685         || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
7686                 __MoleculeUnlock(mp);
7687                 Panic("Low memory while adding impropers");
7688         }
7689         mp->needsMDRebuild = 1;
7690         __MoleculeUnlock(mp);
7691         return nc;
7692 }
7693
7694 int
7695 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
7696 {
7697         int nc;
7698         if (mp == NULL || where == NULL)
7699                 return 0;
7700         if (mp->noModifyTopology)
7701                 return -4;  /*  Prohibited operation  */
7702         __MoleculeLock(mp);
7703         if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
7704                 __MoleculeUnlock(mp);
7705                 Panic("Low memory while adding impropers");
7706         }
7707         mp->nimpropers -= (nc = IntGroupGetCount(where));
7708         __MoleculeUnlock(mp);
7709         return nc;
7710 }
7711
7712 int
7713 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
7714 {
7715         Int i, *ip;
7716         if (mp == NULL || mp->bonds == NULL)
7717                 return -1;
7718         for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
7719                 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
7720                         return i;
7721         }
7722         return -1;
7723 }
7724
7725 int
7726 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
7727 {
7728         Int i, *ip;
7729         if (mp == NULL || mp->angles == NULL)
7730                 return -1;
7731         for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
7732                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
7733                         (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
7734                         return i;
7735         }
7736         return -1;
7737 }
7738
7739 int
7740 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
7741 {
7742         Int i, *ip;
7743         if (mp == NULL || mp->dihedrals == NULL)
7744                 return -1;
7745         for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
7746                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
7747                         (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
7748                         return i;
7749         }
7750         return -1;
7751 }
7752
7753 int
7754 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
7755 {
7756         Int i, *ip;
7757         if (mp == NULL || mp->impropers == NULL)
7758                 return -1;
7759         for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
7760                 if (n3 != ip[2])
7761                         continue;
7762                 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
7763                         (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
7764                         (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
7765                         return i;
7766         }
7767         return -1;
7768 }
7769
7770 /*  Remove the bond at bondIndex and create two dummy atoms instead.
7771     The dummy atoms are placed at the end of atoms[], and the residue
7772         numbers are the same as the root atoms (i.e. the atoms to which
7773         the dummy atoms are connected). The indices are returned in
7774         dummyIndices[0,1].  */
7775 int
7776 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
7777 {
7778         Int roots[3], newBonds[5];
7779         Vector dr;
7780         Atom *rootp[2];
7781         Atom na[2], *nap;
7782         int i, natoms;
7783         if (mp == NULL || mp->noModifyTopology)
7784                 return 0;
7785         if (bondIndex < 0 || bondIndex >= mp->nbonds)
7786                 return -1;
7787         roots[0] = mp->bonds[bondIndex * 2];
7788         roots[1] = mp->bonds[bondIndex * 2 + 1];
7789         roots[2] = kInvalidIndex;
7790         rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
7791         rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
7792         VecSub(dr, rootp[0]->r, rootp[1]->r);
7793         for (i = 0; i < 2; i++) {
7794                 float w;
7795                 nap = &na[i];
7796                 memmove(nap, rootp[i], sizeof(na));
7797                 nap->aname[0] = '*';
7798                 strcpy(nap->element, "Du");
7799                 nap->type = 0;
7800                 nap->charge = nap->weight = 0.0;
7801                 nap->atomicNumber = 0;
7802                 nap->connect.count = 0;
7803                 w = (i == 0 ? 0.4 : -0.4);
7804                 VecScaleInc(nap->r, dr, w);
7805                 VecZero(nap->v);
7806                 VecZero(nap->f);
7807                 nap->intCharge = 0;
7808                 nap->exflags = 0;
7809         }
7810
7811         /*  Expand atoms array and append the dummy atoms at the end  */
7812         __MoleculeLock(mp);
7813         natoms = mp->natoms;
7814         if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
7815                 goto panic;
7816         memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
7817         dummyIndices[0] = natoms;
7818         dummyIndices[1] = natoms + 1;
7819
7820         /*  Remove the old bond and create new bonds  */
7821 /*      ig = IntGroupNewWithPoints(bondIndex, 1, -1);
7822         if (ig == NULL)
7823                 goto panic;
7824         MoleculeDeleteBonds(mp, NULL, ig);
7825         IntGroupRelease(ig); */
7826         MoleculeDeleteBonds(mp, 1, roots);
7827         newBonds[0] = roots[0];
7828         newBonds[1] = dummyIndices[0];
7829         newBonds[2] = roots[1];
7830         newBonds[3] = dummyIndices[1];
7831         newBonds[4] = kInvalidIndex;
7832         
7833         i = (MoleculeAddBonds(mp, 2, newBonds) < 0 ? -1 : 0);
7834         mp->needsMDRebuild = 1;
7835         __MoleculeUnlock(mp);
7836         return i;
7837
7838 panic:
7839         __MoleculeUnlock(mp);
7840         Panic("Low memory during creating dummy atoms");
7841         return 1;
7842 }
7843
7844 /*  Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
7845     a bond between the two root atoms. The value bondIndex is used as a
7846         hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
7847         the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
7848         is ignored and the new bond is stored at the end of bonds[].  */
7849 int
7850 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
7851 {
7852         return 0;
7853 }
7854
7855 /*
7856 Int
7857 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
7858 {
7859         Int n1, *np1;
7860         if (mol == NULL || mol->noModifyTopology)
7861                 return -1;
7862         n1 = mol->nangles;
7863         np1 = mol->angles;
7864         mol->nangles = 0;
7865         mol->angles = NULL;
7866         if (nangles > 0) {
7867                 __MoleculeLock(mol);
7868                 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
7869                 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
7870                 mol->needsMDRebuild = 1;
7871                 __MoleculeUnlock(mol);
7872         }
7873         *outAngles = np1;
7874         return n1;
7875 }
7876                                                 
7877 Int
7878 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
7879 {
7880         Int n1, *np1;
7881         if (mol == NULL || mol->noModifyTopology)
7882                 return -1;
7883         n1 = mol->ndihedrals;
7884         np1 = mol->dihedrals;
7885         mol->ndihedrals = 0;
7886         mol->dihedrals = NULL;
7887         if (ndihedrals > 0) {
7888                 __MoleculeLock(mol);
7889                 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
7890                 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
7891                 mol->needsMDRebuild = 1;
7892                 __MoleculeUnlock(mol);
7893         }
7894         *outDihedrals = np1;
7895         return n1;
7896 }
7897
7898 Int
7899 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
7900 {
7901         Int n1, *np1;
7902         if (mol == NULL || mol->noModifyTopology)
7903                 return -1;
7904         n1 = mol->nimpropers;
7905         np1 = mol->impropers;
7906         mol->nimpropers = 0;
7907         mol->impropers = NULL;
7908         if (nimpropers > 0) {
7909                 __MoleculeLock(mol);
7910                 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
7911                 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
7912                 mol->needsMDRebuild = 1;
7913                 __MoleculeUnlock(mol);
7914         }
7915         *outImpropers = np1;
7916         return n1;
7917 }
7918 */
7919
7920 Int
7921 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
7922 {
7923         Int i, j, k, *ip;
7924         Atom *ap;
7925         Int nangles;
7926         Int *angles;
7927         
7928         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
7929                 return 0;  /*  molecule is empty  */
7930         if (mol->noModifyTopology)
7931                 return -1;
7932         nangles = 0;
7933         angles = NULL;
7934         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7935                 Int *cp = AtomConnectData(&ap->connect);
7936                 for (j = 0; j < ap->connect.count; j++) {
7937                         Int j0 = cp[j];
7938                         for (k = j + 1; k < ap->connect.count; k++) {
7939                                 Int k0 = cp[k];
7940                                 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
7941                                         ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
7942                                         ip[0] = j0;
7943                                         ip[1] = i;
7944                                         ip[2] = k0;
7945                                 }
7946                         }
7947                 }
7948         }
7949         if (nangles > 0) {
7950                 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
7951                 ip[0] = -1;
7952                 nangles--;
7953         }
7954         if (outAngles != NULL)
7955                 *outAngles = angles;
7956         return nangles;
7957 }
7958
7959 Int
7960 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
7961 {
7962         Int n1, n2, n3, n4, *ip, *cp2, *cp3;
7963         Atom *ap2, *ap3;
7964         Int ndihedrals;
7965         Int *dihedrals;
7966         
7967         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
7968                 return 0;  /*  molecule is empty  */
7969         ndihedrals = 0;
7970         dihedrals = NULL;
7971         for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
7972                 Int i1, i3, i4, *ip;
7973                 cp2 = AtomConnectData(&ap2->connect);
7974                 for (i3 = 0; i3 < ap2->connect.count; i3++) {
7975                         n3 = cp2[i3];
7976                         if (n2 > n3)
7977                                 continue;
7978                         ap3 = ATOM_AT_INDEX(mol->atoms, n3);
7979                         cp3 = AtomConnectData(&ap3->connect);
7980                         for (i1 = 0; i1 < ap2->connect.count; i1++) {
7981                                 n1 = cp2[i1];
7982                                 if (n1 == n3)
7983                                         continue;
7984                                 for (i4 = 0; i4 < ap3->connect.count; i4++) {
7985                                         n4 = cp3[i4];
7986                                         if (n2 == n4 || n1 == n4)
7987                                                 continue;
7988                                         if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
7989                                                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
7990                                                 ip[0] = n1;
7991                                                 ip[1] = n2;
7992                                                 ip[2] = n3;
7993                                                 ip[3] = n4;
7994                                         }
7995                                 }
7996                         }
7997                 }
7998         }
7999         if (ndihedrals > 0) {
8000                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
8001                 ip[0] = -1;
8002                 ndihedrals--;
8003         }
8004         if (outDihedrals != NULL)
8005                 *outDihedrals = dihedrals;
8006         return ndihedrals;
8007 }
8008
8009 Int
8010 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
8011 {
8012         Int n1, n2, n3, n4, t1, t2, t3, t4, *ip, *cp;
8013         Parameter *par = mol->par;
8014         Atom *ap, *ap3;
8015         Int nimpropers;
8016         Int *impropers;
8017         
8018         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8019                 return 0;  /*  molecule is empty  */
8020         if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
8021                 return 0;  /*  No improper parameters are defined  */
8022         nimpropers = 0;
8023         impropers = NULL;
8024         ap = mol->atoms;
8025         for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
8026                 Int i1, i2, i4, found, *ip;
8027                 t3 = ap3->type;
8028                 cp = AtomConnectData(&ap3->connect);
8029                 for (i1 = 0; i1 < ap3->connect.count; i1++) {
8030                         n1 = cp[i1];
8031                         t1 = ATOM_AT_INDEX(ap, n1)->type;
8032                         for (i2 = i1 + 1; i2 < ap3->connect.count; i2++) {
8033                                 n2 = cp[i2];
8034                                 t2 = ATOM_AT_INDEX(ap, n2)->type;
8035                                 for (i4 = i2 + 1; i4 < ap3->connect.count; i4++) {
8036                                         n4 = cp[i4];
8037                                         t4 = ATOM_AT_INDEX(ap, n4)->type;
8038                                         found = 0;
8039                                         if (ParameterLookupImproperPar(par, t1, t2, t3, t4, n1, n2, n3, n4, 0) != NULL)
8040                                                 found = 1;
8041                                         else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, -1, -1, -1, -1, 0) != NULL)
8042                                                 found = 1;
8043                                         if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
8044                                                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8045                                                 ip[0] = n1;
8046                                                 ip[1] = n2;
8047                                                 ip[2] = n3;
8048                                                 ip[3] = n4;
8049                                         }
8050                                 }
8051                         }
8052                 }
8053         }
8054         if (nimpropers > 0) {
8055                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8056                 ip[0] = -1;
8057                 nimpropers--;
8058         }
8059         if (outImpropers != NULL)
8060                 *outImpropers = impropers;
8061         return nimpropers;
8062 }
8063
8064 #pragma mark ====== Residues ======
8065
8066 void
8067 MoleculeCleanUpResidueTable(Molecule *mp)
8068 {
8069         int i, maxres;
8070         Atom *ap;
8071         if (mp == NULL || mp->natoms == 0)
8072                 return;
8073         maxres = 0;
8074         __MoleculeLock(mp);
8075         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8076                 if (ap->resSeq >= maxres)
8077                         maxres = ap->resSeq + 1;
8078                 if (ap->resSeq < mp->nresidues) {
8079                         if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
8080                                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8081                 } else {
8082                         AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
8083                 }
8084         }
8085         if (maxres < mp->nresidues)
8086                 mp->nresidues = maxres;
8087         __MoleculeUnlock(mp);
8088 }
8089
8090 /*  Change the number of residues. If nresidues is greater than the current value,
8091     then the array mp->residues is expanded with null names. If nresidues is smaller
8092         than the current value, mp->nresidues is set to the smallest possible value
8093         that is no smaller than nresidues and larger than any of the resSeq values.  */
8094 int
8095 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
8096 {
8097         int n;
8098         if (mp == NULL)
8099                 return 0;
8100         if (mp->nresidues == nresidues)
8101                 return nresidues;
8102         else if (mp->nresidues < nresidues) {
8103                 __MoleculeLock(mp);
8104                 n = mp->nresidues;
8105                 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
8106                 while (n < nresidues)
8107                         mp->residues[n++][0] = 0;
8108                 __MoleculeUnlock(mp);
8109                 return nresidues;
8110         } else {
8111                 int i;
8112                 Atom *ap;
8113                 n = nresidues;
8114                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8115                         if (ap->resSeq >= n)
8116                                 n = ap->resSeq + 1;
8117                 }
8118                 mp->nresidues = n;
8119                 return n;
8120         }
8121 }
8122
8123 int
8124 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
8125 {
8126         IntGroupIterator iter;
8127         int withArray, resSeq, maxSeq;
8128         int i, j;
8129         Atom *ap;
8130         
8131         /*  If LSB of resSeqs is 1, then a constant value is used for all specified atoms  */
8132         if (((int)resSeqs & 1) == 0) {
8133                 withArray = 1;
8134                 resSeq = 0;
8135         } else {
8136                 withArray = 0;
8137                 resSeq = ((int)resSeqs - 1) / 2;
8138         }
8139         
8140         IntGroupIteratorInit(group, &iter);
8141
8142         /*  Change resSeqs  */
8143         maxSeq = 0;
8144         j = 0;
8145         __MoleculeLock(mp);
8146         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8147                 ap = ATOM_AT_INDEX(mp->atoms, i);
8148                 if (withArray)
8149                         resSeq = resSeqs[j++];
8150                 if (resSeq > maxSeq)
8151                         maxSeq = resSeq;
8152                 ap->resSeq = resSeq;
8153         }
8154         __MoleculeUnlock(mp);
8155
8156         /*  Expand array if necessary  */
8157         if (maxSeq >= mp->nresidues)
8158                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8159
8160         /*  Synchronize resName and residues[]  */
8161         __MoleculeLock(mp);
8162         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8163                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8164                         continue;
8165                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8166         }
8167         IntGroupIteratorRelease(&iter);
8168         __MoleculeUnlock(mp);
8169         
8170         MoleculeIncrementModifyCount(mp);
8171         
8172         return 0;
8173 }
8174
8175 int
8176 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
8177 {
8178         return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(resSeq * 2 + 1));
8179 }
8180
8181 /*  Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
8182     specifies the mp->nresidues after modifying the residue numbers.
8183         If all atoms are modified, then the table of residue names is also shifted. Otherwise,
8184         the table of residue names is not touched. */
8185 int
8186 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
8187 {
8188         int i, maxSeq, nmodatoms;
8189         Atom *ap;
8190         IntGroupIterator iter;
8191         IntGroupIteratorInit(group, &iter);
8192         maxSeq = 0;
8193         if (nresidues < 0)
8194                 nresidues = mp->nresidues;
8195         nmodatoms = 0;
8196         __MoleculeLock(mp);
8197         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8198                 ap = ATOM_AT_INDEX(mp->atoms, i);
8199                 ap->resSeq += offset;
8200                 if (ap->resSeq < 0) {
8201                         /*  Bad argument; undo change and returns this index + 1  */
8202                         int bad_index = i;
8203                         ap->resSeq -= offset;
8204                         while ((i = IntGroupIteratorLast(&iter)) >= 0) {
8205                                 ap = ATOM_AT_INDEX(mp->atoms, i);
8206                                 ap->resSeq -= offset;
8207                         }
8208                         IntGroupIteratorRelease(&iter);
8209                         return bad_index + 1;
8210                 }
8211                 if (ap->resSeq > maxSeq)
8212                         maxSeq = ap->resSeq;
8213                 nmodatoms++;
8214         }
8215         if (maxSeq >= nresidues)
8216                 nresidues = maxSeq + 1;
8217         if (offset < 0 && nmodatoms == mp->natoms) {
8218                 /*  Shift the residue names downward  */
8219                 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
8220         }
8221         __MoleculeUnlock(mp);
8222         MoleculeChangeNumberOfResidues(mp, nresidues);
8223         if (offset > 0 && nmodatoms == mp->natoms) {
8224                 /*  Shift the residue names upward  */
8225                 __MoleculeLock(mp);
8226                 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
8227                 __MoleculeUnlock(mp);
8228         }
8229         IntGroupIteratorRelease(&iter);
8230
8231         MoleculeIncrementModifyCount(mp);
8232         
8233         return 0;
8234 }
8235
8236 /*  Change residue names for the specified residue numbers. Names is an array of
8237     chars containing argc*4 characters, and every 4 characters represent a
8238         residue name; characters '\x01'-'\x1f' are converted to '\0', which allow 
8239         names to be handled as a C string.  */
8240 int
8241 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
8242 {
8243         int i, maxSeq;
8244         Atom *ap;
8245         maxSeq = 0;
8246         for (i = 0; i < argc; i++) {
8247                 if (maxSeq < resSeqs[i])
8248                         maxSeq = resSeqs[i];
8249         }
8250         if (maxSeq >= mp->nresidues)
8251                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8252         __MoleculeLock(mp);
8253         for (i = 0; i < argc; i++) {
8254                 char *p = mp->residues[resSeqs[i]];
8255                 int j;
8256                 strncpy(p, names + i * 4, 4);
8257                 for (j = 0; j < 4; j++) {
8258                         if (p[j] >= 0 && p[j] < 0x20)
8259                                 p[j] = 0;
8260                 }
8261         }
8262         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8263                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8264         }
8265         __MoleculeUnlock(mp);
8266
8267         MoleculeIncrementModifyCount(mp);
8268         
8269         return 0;
8270 }
8271
8272 /*  Returns the maximum residue number actually used  */
8273 int
8274 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
8275 {
8276         int i, maxSeq;
8277         Atom *ap;
8278         maxSeq = -1;
8279         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8280                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8281                         continue;
8282                 if (ap->resSeq > maxSeq)
8283                         maxSeq = ap->resSeq;
8284         }
8285         return maxSeq;
8286 }
8287
8288 /*  Returns the minimum residue number actually used  */
8289 int
8290 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
8291 {
8292         int i, minSeq;
8293         Atom *ap;
8294         minSeq = ATOMS_MAX_NUMBER;
8295         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8296                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8297                         continue;
8298                 if (ap->resSeq < minSeq)
8299                         minSeq = ap->resSeq;
8300         }
8301         return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
8302 }
8303
8304 #pragma mark ====== Sort by Residues ======
8305
8306 static int
8307 sAtomSortComparator(const void *a, const void *b)
8308 {
8309         const Atom *ap, *bp;
8310         ap = *((const Atom **)a);
8311         bp = *((const Atom **)b);
8312         if (ap->resSeq == bp->resSeq) {
8313                 /*  Retain the original order (i.e. atom with larger pointer address is larger)  */
8314                 if (ap < bp)
8315                         return -1;
8316                 else if (ap > bp)
8317                         return 1;
8318                 else return 0;
8319         } else {
8320                 /*  Compare the residue sequence. However, residue sequence 0 is always larger.  */
8321                 if (ap->resSeq == 0)
8322                         return 1;
8323                 else if (bp->resSeq == 0)
8324                         return -1;
8325                 else if (ap->resSeq < bp->resSeq)
8326                         return -1;
8327                 else if (ap->resSeq > bp->resSeq)
8328                         return 1;
8329                 else return 0;
8330         }
8331 }
8332
8333 static void
8334 sMoleculeReorder(Molecule *mp)
8335 {
8336         int i, res, prevRes;
8337         Atom **apArray;
8338         Int *old2new;
8339         Atom *newAtoms;
8340         if (mp == NULL || mp->natoms <= 1)
8341                 return;
8342
8343         /*  Sort the atoms, bonds, etc. */
8344         apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
8345         old2new = (Int *)calloc(sizeof(Int), mp->natoms);
8346         newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
8347         if (apArray == NULL || old2new == NULL || newAtoms == NULL)
8348                 Panic("Low memory during reordering atoms");
8349         for (i = 0; i < mp->natoms; i++)
8350                 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
8351
8352         /*  Sort the atoms. Note: apArray is an array of "Pointer to Atom"  */
8353         qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
8354         
8355         /*  Make a table of 'which atom becomes which'  */
8356         for (i = 0; i < mp->natoms; i++) {
8357                 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
8358                 old2new[j] = i;
8359         }
8360         
8361         /*  Renumber the bonds, etc.  */
8362         for (i = 0; i < mp->nbonds * 2; i++) {
8363                 mp->bonds[i] = old2new[mp->bonds[i]];
8364         }
8365         for (i = 0; i < mp->nangles * 3; i++) {
8366                 mp->angles[i] = old2new[mp->angles[i]];
8367         }
8368         for (i = 0; i < mp->ndihedrals * 4; i++) {
8369                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
8370         }
8371         for (i = 0; i < mp->nimpropers * 4; i++) {
8372                 mp->impropers[i] = old2new[mp->impropers[i]];
8373         }
8374         for (i = 0; i < mp->natoms; i++) {
8375                 Int *ip, j;
8376                 ip = AtomConnectData(&(apArray[i]->connect));
8377                 for (j = 0; j < apArray[i]->connect.count; j++, ip++)
8378                         *ip = old2new[*ip];
8379         }
8380         
8381         /*  Renumber the residues so that the residue numbers are contiguous  */
8382         res = prevRes = 0;
8383         for (i = 0; i < mp->natoms; i++) {
8384                 if (apArray[i]->resSeq == 0)
8385                         break;
8386                 if (apArray[i]->resSeq != prevRes) {
8387                         res++;
8388                         prevRes = apArray[i]->resSeq;
8389                         if (prevRes != res) {
8390                                 strncpy(mp->residues[res], mp->residues[prevRes], 4);
8391                         }
8392                 }
8393                 apArray[i]->resSeq = res;
8394         }
8395         mp->nresidues = res + 1;
8396
8397         /*  Sort the atoms and copy back to atoms[] */
8398         for (i = 0; i < mp->natoms; i++) {
8399                 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
8400         }
8401         memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
8402         
8403         /*  Free the locally allocated storage  */
8404         free(newAtoms);
8405         free(old2new);
8406         free(apArray);
8407
8408 }
8409
8410 /*  Renumber atoms  */
8411 int
8412 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
8413 {
8414         Int *old2new, i, j, retval;
8415         Atom *saveAtoms;
8416         if (mp == NULL)
8417                 return 0;
8418         if (mp->noModifyTopology)
8419                 return -1;
8420         if (old2new_out != NULL)
8421                 old2new = old2new_out;
8422         else
8423                 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
8424         saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
8425         if (old2new == NULL || saveAtoms == NULL)
8426                 Panic("Low memory during reordering atoms");
8427         memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
8428         __MoleculeLock(mp);
8429         for (i = 0; i < mp->natoms; i++)
8430                 old2new[i] = -1;
8431         for (i = 0; i < isize && i < mp->natoms; i++) {
8432                 j = new2old[i];
8433                 if (j < 0 || j >= mp->natoms) {
8434                         retval = 1; /* Out of range */
8435                         goto end;
8436                 }
8437                 if (old2new[j] != -1) {
8438                         retval = 2;  /*  Duplicate entry  */
8439                         goto end;
8440                 }
8441                 old2new[j] = i;
8442         }
8443         if (i < mp->natoms) {
8444                 for (j = 0; j < mp->natoms; j++) {
8445                         if (old2new[j] != -1)
8446                                 continue;
8447                         old2new[j] = i++;
8448                 }
8449         }
8450         if (i != mp->natoms) {
8451                 retval = 3;  /*  Internal inconsistency  */
8452                 goto end;
8453         }
8454
8455         /*  Renumber the bonds, etc.  */
8456         for (i = 0; i < mp->nbonds * 2; i++) {
8457                 mp->bonds[i] = old2new[mp->bonds[i]];
8458         }
8459         for (i = 0; i < mp->nangles * 3; i++) {
8460                 mp->angles[i] = old2new[mp->angles[i]];
8461         }
8462         for (i = 0; i < mp->ndihedrals * 4; i++) {
8463                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
8464         }
8465         for (i = 0; i < mp->nimpropers * 4; i++) {
8466                 mp->impropers[i] = old2new[mp->impropers[i]];
8467         }
8468         for (i = 0; i < mp->natoms; i++) {
8469                 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
8470                 Int *ip = AtomConnectData(&ap->connect);
8471                 for (j = 0; j < ap->connect.count; j++, ip++)
8472                         *ip = old2new[*ip];
8473         }
8474         if (mp->par != NULL) {
8475                 /*  Renumber the parameters  */
8476                 int n;
8477                 for (j = kFirstParType; j <= kLastParType; j++) {
8478                         n = ParameterGetCountForType(mp->par, j);
8479                         for (i = 0; i < n; i++) {
8480                                 UnionPar *up = ParameterGetUnionParFromTypeAndIndex(mp->par, j, i);
8481                                 if (up != NULL)
8482                                         ParameterRenumberAtoms(j, up, mp->natoms, old2new);
8483                         }
8484                 }
8485         }
8486         
8487         /*  Renumber the atoms  */
8488         for (i = 0; i < mp->natoms; i++)
8489                 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
8490         retval = 0;
8491
8492         MoleculeIncrementModifyCount(mp);
8493         mp->needsMDRebuild = 1;
8494
8495   end:
8496         __MoleculeUnlock(mp);
8497         free(saveAtoms);
8498         if (old2new_out == NULL)
8499                 free(old2new);
8500         return retval;
8501 }
8502
8503 #pragma mark ====== Coordinate Transform ======
8504
8505 void
8506 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
8507 {
8508         int i;
8509         Atom *ap;
8510         Symop new_symop;
8511         Transform rtr, symtr;
8512         if (mp == NULL || tr == NULL)
8513                 return;
8514         TransformInvert(rtr, tr);
8515         __MoleculeLock(mp);
8516         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8517                 if (group == NULL || IntGroupLookup(group, i, NULL) != 0) {
8518                         TransformVec(&ap->r, tr, &ap->r);
8519                         if (!SYMOP_ALIVE(ap->symop))
8520                                 continue;
8521                         /*  Transform symop  */
8522                         if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
8523                                 continue;
8524                         TransformMul(symtr, tr, symtr);
8525                         if (group == NULL || IntGroupLookup(group, ap->symbase, NULL) != 0)
8526                                 TransformMul(symtr, symtr, rtr);
8527                 } else {
8528                         if (!SYMOP_ALIVE(ap->symop))
8529                                 continue;
8530                         /*  Transform symop if the base atom is transformed  */
8531                         if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
8532                                 continue;
8533                         if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
8534                                 continue;
8535                         TransformMul(symtr, symtr, rtr);
8536                 }
8537                 if (MoleculeGetSymopForTransform(mp, symtr, &new_symop, 1) != 0)
8538                         continue;
8539                 ap->symop = new_symop;
8540         }
8541         mp->needsMDCopyCoordinates = 1;
8542         __MoleculeUnlock(mp);
8543         sMoleculeNotifyChangeAppearance(mp);
8544 }
8545
8546 /*
8547 void
8548 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
8549 {
8550         int i;
8551         Atom *ap;
8552         if (mp == NULL || tr == NULL)
8553                 return;
8554         __MoleculeLock(mp);
8555         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8556                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8557                         continue;
8558                 TransformVec(&ap->r, tr, &ap->r);
8559         }
8560         mp->needsMDCopyCoordinates = 1;
8561         __MoleculeUnlock(mp);
8562         sMoleculeNotifyChangeAppearance(mp);
8563 }
8564 */
8565
8566 void
8567 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
8568 {
8569         Transform tr;
8570         if (mp == NULL || vp == NULL)
8571                 return;
8572         memset(tr, 0, sizeof(tr));
8573         tr[0] = tr[4] = tr[8] = 1.0;
8574         tr[9] = vp->x;
8575         tr[10] = vp->y;
8576         tr[11] = vp->z;
8577         MoleculeTransform(mp, tr, group);
8578 }
8579
8580 void
8581 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
8582 {
8583         Transform tr;
8584         TransformForRotation(tr, axis, angle, center);
8585         MoleculeTransform(mp, tr, group);
8586 }
8587
8588 int
8589 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
8590 {
8591         int i;
8592         Atom *ap;
8593         Double w;
8594         if (mp == NULL || center == NULL)
8595                 return 1;
8596         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
8597                 return 2;   /*  Empty molecule  */
8598         w = 0.0;
8599         center->x = center->y = center->z = 0.0;
8600         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8601                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8602                         continue;
8603                 VecScaleInc(*center, ap->r, ap->weight);
8604                 w += ap->weight;
8605         }
8606         if (w < 1e-7)
8607                 return 3;  /*  Atomic weights are not defined?  */
8608         w = 1.0 / w;
8609         VecScaleSelf(*center, w);
8610         return 0;
8611 }
8612
8613 int
8614 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
8615 {
8616         Vector vmin, vmax;
8617         int i;
8618         Atom *ap;
8619         if (mp == NULL)
8620                 return 1;
8621         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
8622                 return 2;   /*  Empty molecule  */
8623         vmin.x = vmin.y = vmin.z = 1e50;
8624         vmax.x = vmax.y = vmax.z = -1e50;
8625         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8626                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8627                         continue;
8628                 if (vmin.x > ap->r.x)
8629                         vmin.x = ap->r.x;
8630                 if (vmin.y > ap->r.y)
8631                         vmin.y = ap->r.y;
8632                 if (vmin.z > ap->r.z)
8633                         vmin.z = ap->r.z;
8634                 if (vmax.x < ap->r.x)
8635                         vmax.x = ap->r.x;
8636                 if (vmax.y < ap->r.y)
8637                         vmax.y = ap->r.y;
8638                 if (vmax.z < ap->r.z)
8639                         vmax.z = ap->r.z;
8640         }
8641         if (min != NULL)
8642                 *min = vmin;
8643         if (max != NULL)
8644                 *max = vmax;
8645         return 0;       
8646 }
8647
8648 #pragma mark ====== Measurements ======
8649
8650 Double
8651 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
8652 {
8653         Vector r1, r2;
8654 /*      if (mp->is_xtal_coord) {
8655                 TransformVec(&r1, mp->cell->tr, vp1);
8656                 TransformVec(&r2, mp->cell->tr, vp2);
8657         } else */ {
8658                 r1 = *vp1;
8659                 r2 = *vp2;
8660         }
8661         VecDec(r1, r2);
8662         return VecLength(r1);
8663 }
8664
8665 Double
8666 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
8667 {
8668         Vector r1, r2, r3;
8669         double w;
8670 /*      if (mp->is_xtal_coord) {
8671                 TransformVec(&r1, mp->cell->tr, vp1);
8672                 TransformVec(&r2, mp->cell->tr, vp2);
8673                 TransformVec(&r3, mp->cell->tr, vp3);
8674         } else */ {
8675                 r1 = *vp1;
8676                 r2 = *vp2;
8677                 r3 = *vp3;
8678         }
8679         VecDec(r1, r2);
8680         VecDec(r3, r2);
8681         w = VecLength(r1) * VecLength(r3);
8682         if (w < 1e-20)
8683                 return NAN;
8684         return acos(VecDot(r1, r3) / w) * kRad2Deg;
8685 }
8686
8687 Double
8688 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
8689 {
8690         Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
8691         double w1, w2, w3;
8692 /*      if (mp->is_xtal_coord) {
8693                 TransformVec(&r1, mp->cell->tr, vp1);
8694                 TransformVec(&r2, mp->cell->tr, vp2);
8695                 TransformVec(&r3, mp->cell->tr, vp3);
8696                 TransformVec(&r4, mp->cell->tr, vp4);
8697         } else */ {
8698                 r1 = *vp1;
8699                 r2 = *vp2;
8700                 r3 = *vp3;
8701                 r4 = *vp4;
8702         }
8703         VecSub(r21, r1, r2);
8704         VecSub(r32, r2, r3);
8705         VecSub(r43, r3, r4);
8706         VecCross(v1, r21, r32);
8707         VecCross(v2, r32, r43);
8708         VecCross(v3, r32, v1);
8709         w1 = VecLength(v1);
8710         w2 = VecLength(v2);
8711         w3 = VecLength(v3);
8712         if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
8713                 return NAN;
8714         } else {
8715                 w1 = 1.0 / w1;
8716                 w2 = 1.0 / w2;
8717                 w3 = 1.0 / w3;
8718                 VecScaleSelf(v1, w1);
8719                 VecScaleSelf(v2, w2);
8720                 VecScaleSelf(v3, w3);
8721                 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
8722         }
8723 }
8724
8725 #pragma mark ====== XtalCell Parameters ======
8726
8727 void
8728 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
8729 {
8730         if (mp->cell != NULL) {
8731                 TransformVec(dst, mp->cell->tr, src);
8732         } else *dst = *src;
8733 }
8734
8735 void
8736 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
8737 {
8738         if (mp->cell != NULL) {
8739                 TransformVec(dst, mp->cell->rtr, src);
8740         } else *dst = *src;
8741 }
8742
8743 int
8744 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
8745 {
8746         static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
8747         int n1, n2, n3;
8748         Vector *vp1, *vp2, *vp3;
8749         Vector v1, v2;
8750
8751         if (cp == NULL)
8752                 return 0;
8753         for (n1 = 0; n1 < 3; n1++) {
8754                 if (cp->flags[n1] != 0)
8755                         break;
8756         }
8757         if (n1 == 3) {
8758                 /*  All directions are non-periodic  */
8759                 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
8760                 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
8761         } else {
8762                 n2 = (n1 + 1) % 3;
8763                 n3 = (n1 + 2) % 3;
8764                 vp1 = &(cp->axes[n1]);
8765                 vp2 = &(cp->axes[n2]);
8766                 vp3 = &(cp->axes[n3]);
8767                 cp->tr[n1*3] = vp1->x;
8768                 cp->tr[n1*3+1] = vp1->y;
8769                 cp->tr[n1*3+2] = vp1->z;
8770                 cp->tr[9] = cp->origin.x;
8771                 cp->tr[10] = cp->origin.y;
8772                 cp->tr[11] = cp->origin.z;
8773                 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
8774                         /*  1-dimensional or 2-dimensional system  */
8775                         /*  Create "dummy" axes, so that transforms between internal and cartesian coordinates are
8776                          possible with a single matrix  */
8777                         if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
8778                                 /*  1-dimensional  */
8779                                 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
8780                                 VecCross(v1, *vp1, xvec);
8781                                 VecCross(v2, *vp1, yvec);
8782                                 if (VecLength2(v1) < VecLength2(v2))
8783                                         v1 = v2;
8784                                 VecCross(v2, *vp1, v1);
8785                                 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
8786                                         return -1;   /*  Non-regular transform  */
8787                         } else if (cp->flags[n2] == 0) {
8788                                 v2 = *vp3;
8789                                 VecCross(v1, v2, *vp1);
8790                                 if (NormalizeVec(&v1, &v1))
8791                                         return -1;  /*  Non-regular transform  */
8792                         } else {
8793                                 v1 = *vp2;
8794                                 VecCross(v2, *vp1, v1);
8795                                 if (NormalizeVec(&v2, &v2))
8796                                         return -1;  /*  Non-regular transform  */
8797                         }
8798                         cp->tr[n2*3] = v1.x;
8799                         cp->tr[n2*3+1] = v1.y;
8800                         cp->tr[n2*3+2] = v1.z;
8801                         cp->tr[n3*3] = v2.x;
8802                         cp->tr[n3*3+1] = v2.y;
8803                         cp->tr[n3*3+2] = v2.z;
8804                 } else {
8805                         VecCross(v1, *vp1, *vp2);
8806                         if (fabs(VecDot(v1, *vp3)) < 1e-7)
8807                                 return -1;  /*  Non-regular transform  */
8808                         cp->tr[n2*3] = vp2->x;
8809                         cp->tr[n2*3+1] = vp2->y;
8810                         cp->tr[n2*3+2] = vp2->z;
8811                         cp->tr[n3*3] = vp3->x;
8812                         cp->tr[n3*3+1] = vp3->y;
8813                         cp->tr[n3*3+2] = vp3->z;
8814                 }
8815         }
8816         if (TransformInvert(cp->rtr, cp->tr))
8817                 return -1;  /*  Non-regular transform  */
8818
8819         /*  Calculate the reciprocal cell parameters  */
8820         cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[1] * cp->rtr[1] + cp->rtr[2] * cp->rtr[2]);
8821         cp->rcell[1] = sqrt(cp->rtr[3] * cp->rtr[3] + cp->rtr[4] * cp->rtr[4] + cp->rtr[5] * cp->rtr[5]);
8822         cp->rcell[2] = sqrt(cp->rtr[6] * cp->rtr[6] + cp->rtr[7] * cp->rtr[7] + cp->rtr[8] * cp->rtr[8]);
8823         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;
8824         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;
8825         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;
8826         
8827         if (calc_abc) {
8828                 /*  Calculate a, b, c, alpha, beta, gamma  */
8829                 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[1] * cp->tr[1] + cp->tr[2] * cp->tr[2]);
8830                 cp->cell[1] = sqrt(cp->tr[3] * cp->tr[3] + cp->tr[4] * cp->tr[4] + cp->tr[5] * cp->tr[5]);
8831                 cp->cell[2] = sqrt(cp->tr[6] * cp->tr[6] + cp->tr[7] * cp->tr[7] + cp->tr[8] * cp->tr[8]);
8832                 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;
8833                 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;
8834                 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;
8835         }
8836         
8837         return 0;
8838 }
8839
8840 void
8841 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
8842 {
8843         XtalCell *cp;
8844         int i;
8845         Atom *ap;
8846         Transform cmat;
8847         if (mp == NULL)
8848                 return;
8849         __MoleculeLock(mp);
8850         memset(&cmat, 0, sizeof(Transform));
8851         if (mp->cell != NULL)
8852                 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
8853         else
8854                 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
8855         if (a == 0.0) {
8856                 if (mp->cell != NULL) {
8857                         free(mp->cell);
8858                         mp->needsMDRebuild = 1;
8859                 }
8860                 mp->cell = NULL;
8861         } else {
8862                 cp = mp->cell;
8863                 if (cp == NULL) {
8864                         cp = (XtalCell *)calloc(sizeof(XtalCell), 1);
8865                         if (cp == NULL)
8866                                 Panic("Low memory during setting cell parameters");
8867                         mp->cell = cp;
8868                         mp->needsMDRebuild = 1;
8869                 }
8870                 /*  alpha, beta, gamma are in degree  */
8871                 cp->cell[0] = a;
8872                 cp->cell[1] = b;
8873                 cp->cell[2] = c;
8874                 cp->cell[3] = alpha;
8875                 cp->cell[4] = beta;
8876                 cp->cell[5] = gamma;
8877                 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
8878                         /*  c unique (hexagonal etc.)  */
8879                         Double cosa, cosb, sinb, cosg;
8880                         cosa = cos(alpha * kDeg2Rad);
8881                         cosb = cos(beta * kDeg2Rad);
8882                         sinb = sin(beta * kDeg2Rad);
8883                         cosg = cos(gamma * kDeg2Rad);
8884                         cp->axes[0].x = a * sinb;
8885                         cp->axes[0].y = 0;
8886                         cp->axes[0].z = a * cosb;
8887                         cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
8888                         cp->axes[1].z = b * cosa;
8889                         cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
8890                         cp->axes[2].x = 0;
8891                         cp->axes[2].y = 0;
8892                         cp->axes[2].z = c;
8893                 } else {
8894                         /*  b unique  */
8895                         Double cosg, sing, cosa, cosb;
8896                         cosa = cos(alpha * kDeg2Rad);
8897                         cosb = cos(beta * kDeg2Rad);
8898                         cosg = cos(gamma * kDeg2Rad);
8899                         sing = sin(gamma * kDeg2Rad);
8900                         cp->axes[0].x = a * sing;
8901                         cp->axes[0].y = a * cosg;
8902                         cp->axes[0].z = 0;
8903                         cp->axes[1].x = 0;
8904                         cp->axes[1].y = b;
8905                         cp->axes[1].z = 0;
8906                         cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
8907                         cp->axes[2].y = c * cosa;
8908                         cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
8909                 }
8910                 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
8911                 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
8912                 MoleculeCalculateCellFromAxes(cp, 0);
8913                 TransformMul(cmat, cp->tr, cmat);
8914         }
8915         
8916         /*  Update the coordinates (if requested)  */
8917         if (convertCoordinates) {
8918                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8919                         TransformVec(&(ap->r), cmat, &(ap->r));
8920                 }
8921         }
8922         
8923         /*  Update the anisotropic parameters  */
8924         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8925                 Aniso *anp = ap->aniso;
8926                 if (anp != NULL) {
8927                         MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
8928                 }
8929         }
8930         __MoleculeUnlock(mp);
8931         sMoleculeNotifyChangeAppearance(mp);
8932 }
8933
8934 void
8935 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x13, Double x23, const Double *sigmaptr)
8936 {
8937         Double d, dx;
8938         int u = 0;
8939         const Double log2 = 0.693147180559945;
8940         const Double pi22 = 19.7392088021787;  /* 2*pi**2 */
8941         Transform m1, m2;
8942         Aniso *anp;
8943         XtalCell *cp;
8944         Vector axis[3];
8945         Double val[3];
8946         if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
8947                 return;
8948         anp = mp->atoms[n1].aniso;
8949         __MoleculeLock(mp);
8950         if (anp == NULL) {
8951                 anp = (Aniso *)calloc(sizeof(Aniso), 1);
8952                 if (anp == NULL) {
8953                         __MoleculeUnlock(mp);
8954                         Panic("Low memory during setting anisotropic atom parameters");
8955                 }
8956                 mp->atoms[n1].aniso = anp;
8957         }
8958         switch (type) {
8959                 case 1: d = 1; dx = 0.5; break;
8960                 case 2: d = log2; dx = log2; break;
8961                 case 3: d = log2; dx = log2 * 0.5; break;
8962                 case 4: u = 1; d = 0.25; dx = 0.25; break;
8963                 case 5: u = 1; d = 0.25; dx = 0.125; break;
8964                 case 8: u = 1; d = pi22; dx = pi22; break;
8965                 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
8966                 case 10: d = pi22; dx = pi22; break;
8967                 default: d = dx = 1; break;
8968         }
8969         anp->bij[0] = x11 * d;
8970         anp->bij[1] = x22 * d;
8971         anp->bij[2] = x33 * d;
8972         anp->bij[3] = x12 * dx;
8973         anp->bij[4] = x13 * dx;
8974         anp->bij[5] = x23 * dx;
8975         if (sigmaptr != NULL) {
8976                 anp->has_bsig = 1;
8977                 anp->bsig[0] = sigmaptr[0] * d;
8978                 anp->bsig[1] = sigmaptr[1] * d;
8979                 anp->bsig[2] = sigmaptr[2] * d;
8980                 anp->bsig[3] = sigmaptr[3] * dx;
8981                 anp->bsig[4] = sigmaptr[4] * dx;
8982                 anp->bsig[5] = sigmaptr[5] * dx;
8983         } else {
8984                 anp->has_bsig = 0;
8985                 anp->bsig[0] = anp->bsig[1] = anp->bsig[2] = anp->bsig[3] = anp->bsig[4] = anp->bsig[5] = 0.0;
8986         }
8987         cp = mp->cell;
8988         if (cp != NULL && u == 1) {
8989                 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
8990                 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
8991                 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
8992                 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
8993                 anp->bij[4] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[3] * kDeg2Rad); */
8994                 anp->bij[5] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[4] * kDeg2Rad); */
8995                 if (sigmaptr != NULL) {
8996                         anp->bsig[0] *= cp->rcell[0] * cp->rcell[0];
8997                         anp->bsig[1] *= cp->rcell[1] * cp->rcell[1];
8998                         anp->bsig[2] *= cp->rcell[2] * cp->rcell[2];
8999                         anp->bsig[3] *= cp->rcell[0] * cp->rcell[1];
9000                         anp->bsig[4] *= cp->rcell[2] * cp->rcell[0];
9001                         anp->bsig[5] *= cp->rcell[1] * cp->rcell[2];
9002                 }
9003         }
9004         
9005         /*  Calculate the principal axes (in Cartesian coordinates)  */
9006         /*  The principal axes are the eigenvectors of matrix At(B^-1)A, where
9007                 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
9008                 in which x and z are the crystal-space and cartesian coordinates. */
9009         m1[0] = anp->bij[0] / pi22;
9010         m1[4] = anp->bij[1] / pi22;
9011         m1[8] = anp->bij[2] / pi22;
9012         m1[1] = m1[3] = anp->bij[3] / pi22;
9013         m1[2] = m1[6] = anp->bij[4] / pi22;
9014         m1[5] = m1[7] = anp->bij[5] / pi22;
9015         MatrixInvert(m1, m1);
9016         if (cp != NULL) {
9017                 memmove(m2, cp->rtr, sizeof(Mat33));
9018                 MatrixMul(m1, m1, m2);
9019                 MatrixTranspose(m2, m2);
9020                 MatrixMul(m1, m2, m1);
9021         }
9022         MatrixSymDiagonalize(m1, val, axis);
9023         for (u = 0; u < 3; u++) {
9024                 if (val[u] < 0) {
9025                         fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
9026                         val[u] = 0.001;
9027                 } else {
9028                         val[u] = 1 / sqrt(val[u]);
9029                 }
9030                 anp->pmat[u*3] = axis[u].x * val[u];
9031                 anp->pmat[u*3+1] = axis[u].y * val[u];
9032                 anp->pmat[u*3+2] = axis[u].z * val[u];
9033         }
9034         __MoleculeUnlock(mp);
9035 }
9036
9037 /*  Set the anisotropic parameter for atom idx according to the symop. If symop is not alive, nothing is done. */
9038 void
9039 MoleculeSetAnisoBySymop(Molecule *mp, int idx)
9040 {
9041         Atom *ap, *ap2;
9042         Transform t1, t2;
9043         if (mp == NULL || idx < 0 || idx >= mp->natoms)
9044                 return;
9045         ap = ATOM_AT_INDEX(mp->atoms, idx);
9046         if (!SYMOP_ALIVE(ap->symop))
9047                 return;
9048         ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
9049         if (ap2->aniso == NULL) {
9050                 if (ap->aniso != NULL) {
9051                         free(ap->aniso);
9052                         ap->aniso = NULL;
9053                 }
9054                 return;
9055         }
9056         if (ap->aniso == NULL)
9057                 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
9058         if (ap->symop.sym == 0 || ap->symop.sym >= mp->nsyms) {
9059                 /*  Just copy the aniso parameters  */
9060                 memmove(ap->aniso, ap2->aniso, sizeof(Aniso));
9061                 return;
9062         }
9063         memmove(t1, SYMMETRY_AT_INDEX(mp->syms, ap->symop.sym), sizeof(Transform));
9064         t1[9] = t1[10] = t1[11] = 0.0;
9065         memset(t2, 0, sizeof(Transform));
9066         t2[0] = ap2->aniso->bij[0];
9067         t2[4] = ap2->aniso->bij[1];
9068         t2[8] = ap2->aniso->bij[2];
9069         t2[1] = t2[3] = ap2->aniso->bij[3];
9070         t2[2] = t2[6] = ap2->aniso->bij[4];
9071         t2[5] = t2[7] = ap2->aniso->bij[5];
9072         TransformMul(t2, t1, t2);
9073         TransformInvert(t1, t1);
9074         TransformMul(t2, t2, t1);
9075         MoleculeSetAniso(mp, idx, 0, t2[0], t2[4], t2[8], t2[1], t2[2], t2[5], (ap2->aniso->has_bsig ? ap2->aniso->bsig : NULL));
9076 }
9077
9078 int
9079 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic, int convertCoordinates)
9080 {
9081         static Vector zeroVec = {0, 0, 0};
9082         XtalCell b;
9083         Transform cmat;
9084         int i, n;
9085         Atom *ap;
9086         if (mp == NULL)
9087                 return 0;
9088         if (mp->cell != NULL)
9089                 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
9090         else
9091                 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
9092         if (ax == NULL) {
9093                 if (mp->cell != NULL) {
9094                         free(mp->cell);
9095                         mp->needsMDRebuild = 1;
9096                 }
9097                 mp->cell = NULL;
9098                 return 0;
9099         }       
9100         memset(&b, 0, sizeof(b));
9101         b.axes[0] = (ax != NULL ? *ax : zeroVec);
9102         b.axes[1] = (ay != NULL ? *ay : zeroVec);
9103         b.axes[2] = (az != NULL ? *az : zeroVec);
9104         b.origin = *ao;
9105         memmove(b.flags, periodic, 3);
9106         if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
9107                 return -1;
9108         __MoleculeLock(mp);
9109         if (mp->cell == NULL) {
9110                 mp->needsMDRebuild = 1;
9111         } else {
9112                 if (mp->cell->has_sigma) {
9113                         /*  Keep the sigma  */
9114                         b.has_sigma = 1;
9115                         memmove(b.cellsigma, mp->cell->cellsigma, sizeof(mp->cell->cellsigma));
9116                 }
9117                 if ((b.flags[0] != mp->cell->flags[0]) || (b.flags[1] != mp->cell->flags[1]) || (b.flags[2] != mp->cell->flags[2])) {
9118                         mp->needsMDRebuild = 1;
9119                 }
9120                 free(mp->cell);
9121         }
9122         mp->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
9123         if (mp->cell != NULL) {
9124                 memmove(mp->cell, &b, sizeof(XtalCell));
9125                 TransformMul(cmat, b.tr, cmat);
9126                 /*  Update the coordinates (if requested)  */
9127                 if (convertCoordinates) {
9128                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9129                                 TransformVec(&(ap->r), cmat, &(ap->r));
9130                         }
9131                 }
9132                 
9133                 /*  Update the anisotropic parameters  */
9134                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9135                         Aniso *anp = ap->aniso;
9136                         if (anp != NULL) {
9137                                 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
9138                         }
9139                 }
9140                 n = 0;
9141         } else n = -2;  /*  Out of memory  */
9142         __MoleculeUnlock(mp);
9143         sMoleculeNotifyChangeAppearance(mp);
9144         return n;
9145 }
9146
9147 #pragma mark ====== Fragment manipulation ======
9148
9149 static void
9150 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
9151 {
9152         Atom *ap;
9153         Int i, *cp;
9154         if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
9155                 return;
9156         IntGroupAdd(result, idx, 1);
9157         ap = ATOM_AT_INDEX(mp->atoms, idx);
9158         cp = AtomConnectData(&ap->connect);
9159         for (i = 0; i < ap->connect.count; i++) {
9160                 int idx2 = cp[i];
9161                 if (IntGroupLookup(result, idx2, NULL))
9162                         continue;
9163                 sMoleculeFragmentSub(mp, idx2, result, exatoms);
9164         }
9165 }
9166
9167 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
9168     not containing the atoms in exatoms  */
9169 IntGroup *
9170 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
9171 {
9172         IntGroup *result;
9173         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9174                 return NULL;
9175         result = IntGroupNew();
9176         sMoleculeFragmentSub(mp, n1, result, exatoms);
9177         return result;
9178 }
9179
9180 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
9181     not containing the atoms n2, n3, ... (terminated by -1)  */
9182 IntGroup *
9183 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
9184 {
9185         int i;
9186         IntGroup *exatoms, *result;
9187         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9188                 return NULL;
9189         exatoms = IntGroupNew();
9190         for (i = 0; i < argc; i++)
9191                 IntGroupAdd(exatoms, argv[i], 1);
9192         result = IntGroupNew();
9193         sMoleculeFragmentSub(mp, n1, result, exatoms);
9194         IntGroupRelease(exatoms);
9195         return result;
9196 }
9197
9198 /*  The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
9199     not containing the atoms in exatoms  */
9200 IntGroup *
9201 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
9202 {
9203         IntGroupIterator iter;
9204         IntGroup *result;
9205         int i;
9206         if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
9207                 return NULL;
9208         IntGroupIteratorInit(inatoms, &iter);
9209         result = IntGroupNew();
9210         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9211                 sMoleculeFragmentSub(mp, i, result, exatoms);
9212         }
9213         IntGroupIteratorRelease(&iter);
9214         return result;
9215 }
9216
9217 /*  Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
9218     group is bound to the rest of the molecule via only one bond.
9219         If the result is true, then the atoms belonging to the (only) bond are returned
9220         in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
9221         and n2 can be NULL, if those informations are not needed.  */
9222 int
9223 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
9224 {
9225         Int i, i1, i2, j, k, bond_count, nval1, nval2, *cp;
9226         Atom *ap;
9227         if (mp == NULL || mp->natoms == 0 || group == NULL)
9228                 return 0;  /*  Invalid arguments  */
9229         bond_count = 0;
9230         for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
9231                 i2 = IntGroupGetEndPoint(group, i);
9232                 for (j = i1; j < i2; j++) {
9233                         if (j < 0 || j >= mp->natoms)
9234                                 return 0;  /*  Invalid atom group  */
9235                         ap = ATOM_AT_INDEX(mp->atoms, j);
9236                         cp = AtomConnectData(&ap->connect);
9237                         for (k = 0; k < ap->connect.count; k++) {
9238                                 if (IntGroupLookup(group, cp[k], NULL) == 0) {
9239                                         bond_count++;
9240                                         nval1 = j;
9241                                         nval2 = cp[k];
9242                                         if (bond_count > 1)
9243                                                 return 0;  /*  Too many bonds  */
9244                                 }
9245                         }
9246                 }
9247         }
9248         if (bond_count == 1) {
9249                 if (n1 != NULL)
9250                         *n1 = nval1;
9251                 if (n2 != NULL)
9252                         *n2 = nval2;
9253                 return 1;
9254         } else {
9255                 return 0;
9256         }       
9257 }
9258
9259 /*  Returns non-zero if the given group is 'rotatable' in the molecule. The group
9260     is said to be 'rotatable' when either of the following conditions are met; (1)
9261         the group is detachable, or (2) the group consists of two bonded atoms that define
9262         a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
9263         (either a new IntGroup or 'group' with incremented reference count; thus the caller
9264         is responsible for releasing the returned value).  */
9265 int
9266 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
9267 {
9268         int i1, i2;
9269         if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
9270                 if (rotGroup != NULL) {
9271                         IntGroupRetain(group);
9272                         *rotGroup = group;
9273                 }
9274                 return 1;
9275         }
9276         if (group != NULL && IntGroupGetCount(group) == 2) {
9277                 i1 = IntGroupGetNthPoint(group, 0);
9278                 i2 = IntGroupGetNthPoint(group, 1);
9279                 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
9280                         IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
9281                         if (frag == NULL)
9282                                 return 0;
9283                         i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
9284                         if (i1 == 0) {
9285                                 IntGroupRelease(frag);
9286                                 if (rotGroup != NULL)
9287                                         *rotGroup = NULL;
9288                                 return 0;
9289                         }
9290                         if (rotGroup != NULL)
9291                                 *rotGroup = frag;
9292                         else if (frag != NULL)
9293                                 IntGroupRelease(frag);
9294                         return i1;
9295                 }
9296         }
9297         return 0;
9298 }
9299
9300 #pragma mark ====== Multiple frame ======
9301
9302 int
9303 MoleculeGetNumberOfFrames(Molecule *mp)
9304 {
9305         if (mp == NULL)
9306                 return 0;
9307         if (mp->nframes <= 0) {
9308                 /*  Recalculate  */
9309                 int i, n;
9310                 Atom *ap;
9311                 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9312                         if (ap->nframes > n)
9313                                 n = ap->nframes;
9314                 }
9315                 if (n == 0)
9316                         n = 1;
9317                 mp->nframes = n;
9318         }
9319         return mp->nframes;
9320 }
9321
9322 int
9323 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
9324 {
9325         int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
9326         Vector *tempv, *vp;
9327         Atom *ap;
9328         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
9329                 return -1;
9330
9331         n_old = MoleculeGetNumberOfFrames(mp);
9332         n_new = n_old + count;
9333         last_inserted = IntGroupGetNthPoint(group, count - 1);
9334         if (n_new <= last_inserted) {
9335                 exframes = last_inserted - n_new + 1;  /*  number of extra frames that will be silently inserted  */
9336                 n_new += exframes;
9337         } else exframes = 0;
9338
9339         tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4);  /*  "*4" for handling cells  */
9340         if (tempv == NULL)
9341                 return -1;
9342
9343         __MoleculeLock(mp);
9344
9345         /*  Copy back the current coordinates  */
9346         /*  No change in the current coordinates, but the frame buffer is updated  */
9347         MoleculeSelectFrame(mp, mp->cframe, 1); 
9348         
9349         /*  Expand ap->frames for all atoms  */
9350         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9351                 if (ap->frames == NULL)
9352                         vp = (Vector *)calloc(sizeof(Vector), n_new);
9353                 else
9354                         vp = (Vector *)realloc(ap->frames, sizeof(Vector) * n_new);
9355                 if (vp == NULL) {
9356                         __MoleculeUnlock(mp);
9357                         return -1;
9358                 }
9359                 for (j = ap->nframes; j < n_new; j++)
9360                         vp[j] = ap->r;
9361                 ap->frames = vp;
9362         }
9363         if (mp->cell != NULL && (mp->useFlexibleCell || inFrameCell != NULL)) {
9364                 mp->useFlexibleCell = 1;
9365                 vp = mp->frame_cells;
9366                 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, n_new - 1, NULL);
9367                 if (vp == NULL) {
9368                         /*  Set the first cell parameters  */
9369                         mp->frame_cells[0] = mp->cell->axes[0];
9370                         mp->frame_cells[1] = mp->cell->axes[1];
9371                         mp->frame_cells[2] = mp->cell->axes[2];
9372                         mp->frame_cells[3] = mp->cell->origin;
9373                 }
9374         }
9375         
9376         /*  group = [n0..n1-1, n2..n3-1, ...]  */
9377         /*  s = t = 0,  */
9378         /*  tempv[0..n0-1] <- ap[0..n0-1], s += n0,
9379             tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
9380                 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
9381                 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
9382                 ...
9383                 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
9384                 At last, s will become n_old and t will become count.  */
9385         for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9386                 int s, t, ns, ne, mult;
9387                 Vector cr;
9388                 ne = s = t = 0;
9389                 if (i == mp->natoms) {
9390                         if (mp->cell == NULL || mp->frame_cells == NULL)
9391                                 break;
9392                         vp = mp->frame_cells;
9393                         mult = 4;
9394                 } else {
9395                         cr = ap->r;
9396                         vp = ap->frames;
9397                         mult = 1;
9398                 }
9399                 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
9400                         if (ns > ne) {
9401                                 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
9402                                 s += ns - ne;
9403                         }
9404                         ne = IntGroupGetEndPoint(group, j);
9405                         while (ns < ne) {
9406                                 if (i == mp->natoms) {
9407                                         if (inFrameCell != NULL) {
9408                                                 tempv[ns * 4] = inFrameCell[t * 4];
9409                                                 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
9410                                                 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
9411                                                 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
9412                                         } else {
9413                                                 tempv[ns * 4] = mp->cell->axes[0];
9414                                                 tempv[ns * 4 + 1] = mp->cell->axes[1];
9415                                                 tempv[ns * 4 + 2] = mp->cell->axes[2];
9416                                                 tempv[ns * 4 + 3] = mp->cell->origin;
9417                                         }
9418                                 } else {
9419                                         if (inFrame != NULL)
9420                                                 tempv[ns] = inFrame[natoms * t + i];
9421                                         else
9422                                                 tempv[ns] = cr;
9423                                 }
9424                                 t++;
9425                                 ns++;
9426                         }
9427                 }
9428                 if (n_new > ne) {
9429                         memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
9430                         s += n_new - ne;
9431                 }
9432                 if (i < mp->natoms)
9433                         ap->nframes = n_new;
9434                 memmove(vp, tempv, sizeof(Vector) * mult * n_new);
9435         }
9436         free(tempv);
9437         mp->nframes = n_new;
9438         MoleculeSelectFrame(mp, last_inserted, 0);
9439         MoleculeIncrementModifyCount(mp);
9440         __MoleculeUnlock(mp);
9441         return count;
9442 }
9443
9444 int
9445 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
9446 {
9447         int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
9448         Vector *tempv, *vp;
9449         Atom *ap;
9450         IntGroup *group, *group2;
9451
9452         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
9453                 return -1;
9454
9455         /*  outFrame[] should have enough size for Vector * natoms * group.count  */
9456         memset(outFrame, 0, sizeof(Vector) * natoms * count);
9457         if (mp->cell != NULL && mp->frame_cells != NULL)
9458                 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
9459
9460         n_old = MoleculeGetNumberOfFrames(mp);
9461         if (n_old == 1)
9462                 return -2;  /*  Cannot delete last frame  */
9463
9464         group = IntGroupNew();
9465         group2 = IntGroupNewWithPoints(0, n_old, -1);
9466         IntGroupIntersect(inGroup, group2, group);
9467         IntGroupRelease(group2);
9468         count = IntGroupGetCount(group);
9469         n_new = n_old - count;
9470         if (n_new < 1) {
9471                 IntGroupRelease(group);
9472                 return -2;  /*  Trying to delete too many frames  */
9473         }
9474         tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4);  /*  "*4" for handling cells  */
9475         if (tempv == NULL) {
9476                 IntGroupRelease(group);
9477                 return -1;
9478         }
9479
9480         __MoleculeLock(mp);
9481
9482         /*  Copy back the current coordinates  */
9483         /*  No change in the current coordinates, but the frame buffer is updated  */
9484         MoleculeSelectFrame(mp, mp->cframe, 1); 
9485
9486         /*  Determine which frame should be selected after removal is completed  */
9487         {
9488                 int n1;
9489                 if (IntGroupLookup(group, mp->cframe, &i)) {
9490                         /*  cframe will be removed  */
9491                         n1 = IntGroupGetStartPoint(group, i) - 1;
9492                         if (n1 < 0)
9493                                 n1 = IntGroupGetEndPoint(group, i);
9494                 } else n1 = mp->cframe;
9495                 /*  Change to that frame  */
9496                 MoleculeSelectFrame(mp, n1, 0);
9497                 group2 = IntGroupNewFromIntGroup(group);
9498                 IntGroupReverse(group2, 0, n_old);
9499                 new_cframe = IntGroupLookupPoint(group2, n1);
9500                 if (new_cframe < 0)
9501                         return -3;  /*  This cannot happen  */
9502                 IntGroupRelease(group2);
9503         }
9504
9505         /*  group = [n0..n1-1, n2..n3-1, ...]  */
9506         /*  s = t = 0, */
9507         /*  tempv[0..n0-1] -> ap[0..n0-1], s += n0,
9508             tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
9509                 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
9510                 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
9511                 ...
9512                 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
9513                 At last, s will become n_new and t will become count.  */
9514         nframes = 0;
9515         for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9516                 int s, t, j, ns, ne;
9517                 int mult;
9518                 /*  if i == mp->natoms, mp->frame_cells is handled  */
9519                 if (i == mp->natoms) {
9520                         if (mp->cell == NULL || mp->frame_cells == NULL)
9521                                 break;
9522                         mult = 4;
9523                         vp = mp->frame_cells;
9524                         old_count = n_old;
9525                 } else {
9526                         mult = 1;
9527                         vp = ap->frames;
9528                         if (vp == NULL) {
9529                                 ap->frames = vp = (Vector *)calloc(sizeof(Vector), n_old);
9530                                 if (vp == NULL) {
9531                                         __MoleculeUnlock(mp);
9532                                         return -1;
9533                                 }
9534                         }
9535                         old_count = ap->nframes;
9536                 }
9537
9538                 /*  Copy vp to tempv  */
9539                 memset(tempv, 0, sizeof(Vector) * mult * n_old);
9540                 memmove(tempv, vp, sizeof(Vector) * mult * (old_count > n_old ? n_old : old_count));
9541                 ne = ns = s = t = 0;
9542                 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
9543                         if (ns > n_old)
9544                                 ns = n_old;
9545                         if (ns > ne) {
9546                                 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (ns - ne));
9547                                 s += ns - ne;
9548                         }
9549                         ne = IntGroupGetEndPoint(group, j);
9550                         if (ne > n_old)
9551                                 ne = n_old;
9552                         while (ns < ne) {
9553                                 if (i < mp->natoms)
9554                                         outFrame[natoms * t + i] = tempv[ns];
9555                                 else if (outFrameCell != NULL) {
9556                                         outFrameCell[t * 4] = tempv[ns * 4];
9557                                         outFrameCell[t * 4 + 1] = tempv[ns * 4 + 1];
9558                                         outFrameCell[t * 4 + 2] = tempv[ns * 4 + 2];
9559                                         outFrameCell[t * 4 + 3] = tempv[ns * 4 + 3];
9560                                 }
9561                                 t++;
9562                                 ns++;
9563                         }
9564                 }
9565                 if (n_old > ne) {
9566                         memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (n_old - ne));
9567                         s += n_old - ne;
9568                 }
9569                 if (i < mp->natoms)
9570                         ap->nframes = s;
9571                 if (nframes < s)
9572                         nframes = s;
9573                 if (s <= 1) {
9574                         if (i < mp->natoms) {
9575                                 free(ap->frames);
9576                                 ap->frames = NULL;
9577                                 ap->nframes = 0;
9578                         } else {
9579                                 free(mp->frame_cells);
9580                                 mp->frame_cells = NULL;
9581                                 mp->nframe_cells = 0;
9582                         }
9583                 } else {
9584                         if (i < mp->natoms)
9585                                 ap->frames = (Vector *)realloc(ap->frames, sizeof(Vector) * s);
9586                         else {
9587                                 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, s - 1, NULL);
9588                                 mp->nframe_cells = s;
9589                         }
9590                 }
9591         }
9592         free(tempv);
9593         mp->nframes = nframes;
9594         
9595         /*  Select the "last" frame; do not "copy back" the coordinates to the frame table  */
9596 /*      i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
9597         MoleculeSelectFrame(mp, new_cframe, 0);
9598
9599         IntGroupRelease(group);
9600
9601         MoleculeIncrementModifyCount(mp);
9602         __MoleculeUnlock(mp);
9603         return count;
9604 }
9605
9606 int
9607 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
9608 {
9609         int i, cframe, nframes, modified;
9610         Atom *ap;
9611         cframe = mp->cframe;
9612         nframes = MoleculeGetNumberOfFrames(mp);
9613         if (frame == -1)
9614                 frame = mp->cframe;
9615         if (mp == NULL || mp->natoms == 0 || frame < 0 || frame >= nframes)
9616                 return -1;
9617         modified = 0;
9618         __MoleculeLock(mp);
9619         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9620                 if (copyback && cframe >= 0 && cframe < ap->nframes) {
9621                         /*  Write the current coordinate back to the frame array  */
9622                         ap->frames[cframe] = ap->r;
9623                 }
9624                 if (frame != cframe && frame >= 0 && frame < ap->nframes) {
9625                         /*  Read the coordinate from the frame array  */
9626                         ap->r = ap->frames[frame];
9627                         modified = 1;
9628                 }
9629         }
9630
9631         if (mp->cell != NULL && mp->frame_cells != NULL) {
9632                 /*  Write the current cell back to the frame_cells array  */
9633                 if (copyback && cframe >= 0) {
9634                         Vector *vp = (Vector *)AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, cframe, NULL);
9635                         vp[0] = mp->cell->axes[0];
9636                         vp[1] = mp->cell->axes[1];
9637                         vp[2] = mp->cell->axes[2];
9638                         vp[3] = mp->cell->origin;
9639                 }
9640                 /*  Set the cell from the frame array  */
9641                 if (frame != cframe && frame >= 0 && frame < mp->nframe_cells) {
9642                         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, 0);
9643                         modified = 1;
9644                         MoleculeAmendBySymmetry(mp, NULL, NULL, NULL);
9645                 }
9646         }
9647         mp->cframe = frame;
9648         if (modified)
9649                 mp->needsMDCopyCoordinates = 1;
9650         __MoleculeUnlock(mp);
9651         sMoleculeNotifyChangeAppearance(mp);
9652         return frame;
9653 }
9654
9655 /*  If molecule is multi-frame, then flush the current information to the frame buffer.
9656     Returns the number of frames.  */
9657 int
9658 MoleculeFlushFrames(Molecule *mp)
9659 {
9660         int nframes = MoleculeGetNumberOfFrames(mp);
9661         if (nframes > 1)
9662                 MoleculeSelectFrame(mp, mp->cframe, 1);
9663         return nframes;
9664 }
9665
9666 #pragma mark ====== MO calculation ======
9667
9668 /*  Calculate an MO value for a single point.  */
9669 /*  Index is the MO number (1-based)  */
9670 /*  tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom.  */
9671 static Double
9672 sCalcMOPoint(const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
9673 {
9674         ShellInfo *sp;
9675         PrimInfo *pp;
9676         Double val, tval, *cnp, *tmpp, *mobasep, *mop;
9677         Int i, j;
9678         /*  Cache dr and |dr|^2  */
9679         for (i = 0; i < bset->natoms; i++) {
9680                 Vector r = bset->pos[i];
9681                 tmp[i * 4] = r.x = vp->x - r.x;
9682                 tmp[i * 4 + 1] = r.y = vp->y - r.y;
9683                 tmp[i * 4 + 2] = r.z = vp->z - r.z;
9684                 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
9685         }
9686         /*  Iterate over all shells  */
9687         val = 0.0;
9688         mobasep = bset->mo + (index - 1) * bset->ncomps;
9689         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
9690                 pp = bset->priminfos + sp->p_idx;
9691                 cnp = bset->cns + sp->cn_idx;
9692                 tmpp = tmp + sp->a_idx * 4;
9693                 mop = mobasep + sp->m_idx;
9694                 switch (sp->sym) {
9695                         case kGTOType_S: {
9696                                 tval = 0;
9697                                 for (j = 0; j < sp->nprim; j++) {
9698                                         tval += *cnp++ * exp(-pp->A * tmpp[3]);
9699                                         pp++;
9700                                 }
9701                                 val += mop[0] * tval;
9702                                 break;
9703                         }
9704                         case kGTOType_P: {
9705                                 Double x, y, z;
9706                                 x = y = z = 0;
9707                                 for (j = 0; j < sp->nprim; j++) {
9708                                         tval = exp(-pp->A * tmpp[3]);
9709                                         x += *cnp++ * tval;
9710                                         y += *cnp++ * tval;
9711                                         z += *cnp++ * tval;
9712                                         pp++;
9713                                 }
9714                                 x *= mop[0] * tmpp[0];
9715                                 y *= mop[1] * tmpp[1];
9716                                 z *= mop[2] * tmpp[2];
9717                                 val += x + y + z;
9718                                 break;
9719                         }
9720                         case kGTOType_SP: {
9721                                 Double t, x, y, z;
9722                                 t = x = y = z = 0;
9723                                 for (j = 0; j < sp->nprim; j++) {
9724                                         tval = exp(-pp->A * tmpp[3]);
9725                                         t += *cnp++ * tval;
9726                                         x += *cnp++ * tval;
9727                                         y += *cnp++ * tval;
9728                                         z += *cnp++ * tval;
9729                                         pp++;
9730                                 }
9731                                 t *= mop[0];
9732                                 x *= mop[1] * tmpp[0];
9733                                 y *= mop[2] * tmpp[1];
9734                                 z *= mop[3] * tmpp[2];
9735                                 val += t + x + y + z;
9736                                 break;
9737                         }
9738                         case kGTOType_D: {
9739                                 Double xx, yy, zz, xy, xz, yz;
9740                                 xx = yy = zz = xy = xz = yz = 0;
9741                                 for (j = 0; j < sp->nprim; j++) {
9742                                         tval = exp(-pp->A * tmpp[3]);
9743                                         xx += *cnp++ * tval;
9744                                         yy += *cnp++ * tval;
9745                                         zz += *cnp++ * tval;
9746                                         xy += *cnp++ * tval;
9747                                         xz += *cnp++ * tval;
9748                                         yz += *cnp++ * tval;
9749                                         pp++;
9750                                 }
9751                                 xx *= mop[0] * tmpp[0] * tmpp[0];
9752                                 yy *= mop[1] * tmpp[1] * tmpp[1];
9753                                 zz *= mop[2] * tmpp[2] * tmpp[2];
9754                                 xy *= mop[3] * tmpp[0] * tmpp[1];
9755                                 xz *= mop[4] * tmpp[0] * tmpp[2];
9756                                 yz *= mop[5] * tmpp[1] * tmpp[2];
9757                                 val += xx + yy + zz + xy + xz + yz;
9758                                 break;
9759                         }
9760                         case kGTOType_D5: {
9761                                 Double d0, d1p, d1n, d2p, d2n;
9762                                 d0 = d1p = d1n = d2p = d2n = 0;
9763                                 for (j = 0; j < sp->nprim; j++) {
9764                                         tval = exp(-pp->A * tmpp[3]);
9765                                         d0 += *cnp++ * tval;
9766                                         d1p += *cnp++ * tval;
9767                                         d1n += *cnp++ * tval;
9768                                         d2p += *cnp++ * tval;
9769                                         d2n += *cnp++ * tval;
9770                                         pp++;
9771                                 }
9772                                 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
9773                                 d1p *= mop[1] * tmpp[0] * tmpp[2];
9774                                 d1n *= mop[2] * tmpp[1] * tmpp[2];
9775                                 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
9776                                 d2n *= mop[4] * tmpp[0] * tmpp[1];
9777                                 val += d0 + d1p + d1n + d2p + d2n;
9778                                 break;
9779                         }
9780                 }
9781         }
9782         return val;
9783 }
9784
9785 /*  Calculate one MO. The input vectors should be in bohr unit (angstrom * 1.889725989 = kAngstrom2Bohr).  */
9786 /*  mono is the MO number (1-based)  */
9787 int
9788 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)
9789 {
9790         int ix, iy, iz, n, nn;
9791         Cube *cp;
9792         Double *tmp;
9793         if (mp == NULL || mp->bset == NULL)
9794                 return -1;
9795         if (mp->bset->cns == NULL) {
9796                 if (sSetupGaussianCoefficients(mp->bset) != 0)
9797                         return -1;
9798         }
9799         cp = (Cube *)calloc(sizeof(Cube), 1);
9800         if (cp == NULL) {
9801                 return -1;
9802         }
9803         cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
9804         if (cp->dp == NULL) {
9805                 free(cp);
9806                 return -1;
9807         }
9808         cp->idn = mono;
9809         cp->origin = *op;
9810         cp->dx = *dxp;
9811         cp->dy = *dyp;
9812         cp->dz = *dzp;
9813         cp->nx = nx;
9814         cp->ny = ny;
9815         cp->nz = nz;
9816         
9817         /*  TODO: use multithread  */
9818         tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms * 4);
9819         if (tmp == NULL) {
9820                 free(cp->dp);
9821                 free(cp);
9822                 return -1;
9823         }
9824         n = nn = 0;
9825         for (ix = 0; ix < nx; ix++) {
9826                 Vector p;
9827                 for (iy = 0; iy < ny; iy++) {
9828                         for (iz = 0; iz < nz; iz++) {
9829                                 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
9830                                 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
9831                                 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
9832                                 cp->dp[n++] = sCalcMOPoint(mp->bset, mono, &p, tmp);
9833                         }
9834                         if (callback != NULL && n - nn > 100) {
9835                                 nn = n;
9836                                 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
9837                                         free(cp->dp);
9838                                         free(cp);
9839                                         free(tmp);
9840                                         return -2;  /*  User interrupt  */
9841                                 }
9842                         }
9843                 }
9844         }
9845         free(tmp);
9846
9847         AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
9848         return mp->bset->ncubes - 1;
9849 }
9850
9851 int
9852 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
9853 {
9854         int i;
9855         Vector rmin, rmax, *vp;
9856         Double dr, dx, dy, dz;
9857         if (mp == NULL || mp->bset == NULL || mp->bset->natoms == 0)
9858                 return -1;
9859         if (npoints <= 0)
9860                 npoints = 1000000;
9861         rmin.x = rmin.y = rmin.z = 1e10;
9862         rmax.x = rmax.y = rmax.z = -1e10;
9863         for (i = 0, vp = mp->bset->pos; i < mp->bset->natoms; i++, vp++) {
9864                 dr = RadiusForAtomicNumber(ATOM_AT_INDEX(mp->atoms, i)->atomicNumber);
9865                 if (dr == 0.0)
9866                         dr = 1.0;
9867                 dr = dr * kAngstrom2Bohr * 3.0 + 2.0;
9868                 if (rmin.x > vp->x - dr)
9869                         rmin.x = vp->x - dr;
9870                 if (rmin.y > vp->y - dr)
9871                         rmin.y = vp->y - dr;
9872                 if (rmin.z > vp->z - dr)
9873                         rmin.z = vp->z - dr;
9874                 if (rmax.x < vp->x + dr)
9875                         rmax.x = vp->x + dr;
9876                 if (rmax.y < vp->y + dr)
9877                         rmax.y = vp->y + dr;
9878                 if (rmax.z < vp->z + dr)
9879                         rmax.z = vp->z + dr;
9880         }
9881         dx = rmax.x - rmin.x;
9882         dy = rmax.y - rmin.y;
9883         dz = rmax.z - rmin.z;
9884         dr = pow(dx * dy * dz / npoints, 1.0/3.0);
9885         *nx = floor(dx / dr + 0.5);
9886         *ny = floor(dy / dr + 0.5);
9887         *nz = floor(dz / dr + 0.5);
9888         if (*nx == 0)
9889                 *nx = 1;
9890         if (*ny == 0)
9891                 *ny = 1;
9892         if (*nz == 0)
9893                 *nz = 1;
9894         *op = rmin;
9895         xp->x = yp->y = zp->z = dr;
9896         xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
9897         return 0;
9898 }
9899
9900 const Cube *
9901 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
9902 {
9903         if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
9904                 return NULL;
9905         return mp->bset->cubes[index];
9906 }
9907
9908 int
9909 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
9910 {
9911         int i;
9912         if (mp == NULL || mp->bset == NULL)
9913                 return -1;
9914         for (i = 0; i < mp->bset->ncubes; i++) {
9915                 if (mp->bset->cubes[i]->idn == mono)
9916                         return i;
9917         }
9918         return -1;
9919 }
9920
9921 int
9922 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
9923 {
9924         int n;
9925         if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
9926                 return -1;
9927         CubeRelease(mp->bset->cubes[index]);
9928         if (index < n - 1)
9929                 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
9930         if (--(mp->bset->ncubes) == 0) {
9931                 free(mp->bset->cubes);
9932                 mp->bset->cubes = NULL;
9933         }
9934         return mp->bset->ncubes;
9935 }
9936
9937 int
9938 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
9939 {
9940         const Cube *cp;
9941         int i, j, k, n;
9942         FILE *fp;
9943         if (mp == NULL || mp->bset == NULL)
9944                 return -1;  /*  Molecule or the basis set information is empty  */
9945         cp = MoleculeGetCubeAtIndex(mp, index);
9946         if (cp == NULL)
9947                 return -2;  /*  MO not yet calculated  */
9948         fp = fopen(fname, "wb");
9949         if (fp == NULL)
9950                 return -3;  /*  Cannot create file  */
9951
9952         /*  Comment lines  */
9953         fprintf(fp, "%s MO=%d\n", comment, cp->idn);
9954         fprintf(fp, " MO coefficients\n");
9955         
9956         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms), cp->origin.x, cp->origin.y, cp->origin.z);
9957         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx, cp->dx.x, cp->dx.y, cp->dx.z);
9958         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny, cp->dy.x, cp->dy.y, cp->dy.z);
9959         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz, cp->dz.x, cp->dz.y, cp->dz.z);
9960         
9961         /*  Atomic information  */
9962         for (i = 0; i < mp->bset->natoms; i++) {
9963                 Vector *vp = mp->bset->pos + i;
9964                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
9965                 /*  The second number should actually be the effective charge  */
9966                 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber, vp->x, vp->y, vp->z);
9967         }
9968         fprintf(fp, "%5d%5d\n", 1, 1);
9969         
9970         /*  3D data  */
9971         for (i = n = 0; i < cp->nx; i++) {
9972                 for (j = 0; j < cp->ny; j++) {
9973                         for (k = 0; k < cp->nz; k++) {
9974                                 fprintf(fp, " %12.5e", cp->dp[n++]);
9975                                 if (k == cp->nz - 1 || k % 6 == 5)
9976                                         fprintf(fp, "\n");
9977                         }
9978                 }
9979         }
9980         fclose(fp);
9981         return 0;
9982 }