OSDN Git Service

Ruby-2.0.0 is now included in the repository.
[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         if (src->anchor != NULL) {
81                 dst->anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
82                 if (dst->anchor != NULL)
83                         memmove(dst->anchor, src->anchor, sizeof(PiAnchor));
84                 if (dst->anchor->connect.count > ATOM_CONNECT_LIMIT) {
85                         dst->anchor->connect.u.ptr = NULL;
86                         dst->anchor->connect.count = 0;
87                         NewArray(&(dst->anchor->connect.u.ptr), &(dst->anchor->connect.count), sizeof(Int), src->anchor->connect.count);
88                         memmove(dst->anchor->connect.u.ptr, src->anchor->connect.u.ptr, sizeof(Int) * src->anchor->connect.count);
89                 }
90                 if (dst->anchor->ncoeffs > 0) {
91                         NewArray(&(dst->anchor->coeffs), &(dst->anchor->ncoeffs), sizeof(Double), src->anchor->ncoeffs);
92                         memmove(dst->anchor->coeffs, src->anchor->coeffs, sizeof(Double) * src->anchor->ncoeffs);
93                 }
94         }
95         return dst;
96 }
97
98 Atom *
99 AtomDuplicate(Atom *dst, const Atom *src)
100 {
101         return s_AtomDuplicate(dst, src, 1);
102 }
103
104 Atom *
105 AtomDuplicateNoFrame(Atom *dst, const Atom *src)
106 {
107         return s_AtomDuplicate(dst, src, 0);
108 }
109
110 void
111 AtomClean(Atom *ap)
112 {
113         if (ap->aniso != NULL) {
114                 free(ap->aniso);
115                 ap->aniso = NULL;
116         }
117         if (ap->frames != NULL) {
118                 free(ap->frames);
119                 ap->frames = NULL;
120                 ap->nframes = 0;
121         }
122         if (ap->connect.count > ATOM_CONNECT_LIMIT) {
123                 ap->connect.count = 0;
124                 free(ap->connect.u.ptr);
125                 ap->connect.u.ptr = NULL;
126         }
127 }
128
129 void
130 CubeRelease(Cube *cp)
131 {
132         if (cp != NULL) {
133                 if (cp->dp != NULL)
134                         free(cp->dp);
135                 free(cp);
136         }
137 }
138
139 void
140 BasisSetRelease(BasisSet *bset)
141 {
142         int i;
143         if (bset == NULL)
144                 return;
145         if (bset->shells != NULL)
146                 free(bset->shells);
147         if (bset->priminfos != NULL)
148                 free(bset->priminfos);
149         if (bset->mo != NULL)
150                 free(bset->mo);
151         if (bset->cns != NULL)
152                 free(bset->cns);
153         if (bset->moenergies != NULL)
154                 free(bset->moenergies);
155         if (bset->scfdensities != NULL)
156                 free(bset->scfdensities);
157         if (bset->pos != NULL)
158                 free(bset->pos);
159         if (bset->nuccharges != NULL)
160                 free(bset->nuccharges);
161         if (bset->cubes != NULL) {
162                 for (i = 0; i < bset->ncubes; i++) {
163                         CubeRelease(bset->cubes[i]);
164                 }
165                 free(bset->cubes);
166         }
167         free(bset);
168 }
169
170 Int *
171 AtomConnectData(AtomConnect *ac)
172 {
173         if (ac == NULL)
174                 return NULL;
175         return ATOM_CONNECT_PTR(ac);
176 }
177
178 void
179 AtomConnectResize(AtomConnect *ac, Int nconnects)
180 {
181         Int *p;
182         if (ac == NULL)
183                 return;
184         if (nconnects <= ATOM_CONNECT_LIMIT) {
185                 if (ac->count > ATOM_CONNECT_LIMIT) {
186                         p = ac->u.ptr;
187                         memmove(ac->u.data, p, sizeof(Int) * nconnects);
188                         free(p);
189                 }
190         } else {
191                 if (ac->count <= ATOM_CONNECT_LIMIT) {
192                         p = NULL;
193                         ac->count = 0;
194                         NewArray(&p, &(ac->count), sizeof(Int), nconnects);
195                         memmove(p, ac->u.data, sizeof(Int) * ac->count);
196                         ac->u.ptr = p;
197                 } else if (ac->count < nconnects) {
198                         /*  Reallocate  */
199                         AssignArray(&(ac->u.ptr), &(ac->count), sizeof(Int), nconnects - 1, NULL);
200                 }
201         }
202         ac->count = nconnects;
203 }
204
205 void
206 AtomConnectInsertEntry(AtomConnect *ac, Int idx, Int connect)
207 {
208         Int n, *p;
209         if (ac == NULL)
210                 return;
211         if (idx > ac->count)
212                 idx = ac->count;
213         else if (idx < 0) {
214                 /*  Insert after the last component that is smaller than connect
215                     (i.e. keep them sorted)  */
216                 p = ATOM_CONNECT_PTR(ac);
217                 for (idx = 0; idx < ac->count; idx++) {
218                         if (p[idx] >= connect)
219                                 break;
220                 }
221         }
222         AtomConnectResize(ac, ac->count + 1);
223         n = ac->count - idx - 1;  /*  Number of entries to be moved towards the end  */
224         p = ATOM_CONNECT_PTR(ac);
225         if (n > 0) {
226                 memmove(p + idx + 1, p + idx, sizeof(Int) * n);
227         }
228         p[idx] = connect;
229 }
230
231 void
232 AtomConnectDeleteEntry(AtomConnect *ac, Int idx)
233 {
234         Int n, *p;
235         if (ac == NULL)
236                 return;
237         if (idx < 0 || idx >= ac->count)
238                 return;
239         n = ac->count - idx - 1;  /*  Number of entries to be moved towards the top  */
240         p = ATOM_CONNECT_PTR(ac);
241         if (n > 0) {
242                 memmove(p + idx, p + idx + 1, sizeof(Int) * n);
243         }
244         AtomConnectResize(ac, ac->count - 1);
245 }
246
247 int
248 AtomConnectHasEntry(AtomConnect *ac, Int ent)
249 {
250         Int n, *p;
251         if (ac == NULL)
252                 return 0;
253         p = ATOM_CONNECT_PTR(ac);
254         for (n = 0; n < ac->count; n++) {
255                 if (ent == p[n])
256                         return 1;
257         }
258         return 0;
259 }
260
261 #pragma mark ====== Accessor types ======
262
263 MolEnumerable *
264 MolEnumerableNew(Molecule *mol, int kind)
265 {
266         MolEnumerable *mseq = (MolEnumerable *)calloc(sizeof(MolEnumerable), 1);
267         if (mseq != NULL) {
268                 mseq->mol = MoleculeRetain(mol);
269                 mseq->kind = kind;
270         }
271         return mseq;
272 }
273
274 void
275 MolEnumerableRelease(MolEnumerable *mseq)
276 {
277         if (mseq != NULL) {
278                 MoleculeRelease(mseq->mol);
279                 free(mseq);
280         }
281 }
282
283 AtomRef *
284 AtomRefNew(Molecule *mol, int idx)
285 {
286         AtomRef *aref = (AtomRef *)calloc(sizeof(AtomRef), 1);
287         if (aref != NULL) {
288                 aref->mol = MoleculeRetain(mol);
289                 aref->idx = idx;
290         }
291         return aref;
292 }
293
294 void
295 AtomRefRelease(AtomRef *aref)
296 {
297         if (aref != NULL) {
298                 MoleculeRelease(aref->mol);
299                 free(aref);
300         }
301 }
302
303 #pragma mark ====== Creation of molecules ======
304
305 Molecule *
306 MoleculeNew(void)
307 {
308         char name[40];
309         Molecule *mp = (Molecule *)calloc(sizeof(Molecule), 1);
310         if (mp == NULL)
311                 Panic("Cannot allocate new molecule record");
312         snprintf(name, sizeof name, "Untitled %d", sMoleculeUntitledCount++);
313         ObjectInit((Object *)mp, (Object **)&sMoleculeRoot, name);
314         mp->mview = MainView_new();
315         mp->mview->mol = mp;
316         return mp;
317 }
318
319 Molecule *
320 MoleculeNewWithName(const char *name)
321 {
322         Molecule *mp = MoleculeNew();
323         MoleculeSetName(mp, name);
324         return mp;
325 }
326
327 Molecule *
328 MoleculeInitWithAtoms(Molecule *mp, const Atom *atoms, int natoms)
329 {
330         int i;
331         if (mp == NULL)
332                 mp = MoleculeNew();
333         if (natoms == 0)
334                 return mp;
335         if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
336                 Panic("Cannot allocate memory for atoms");
337         for (i = 0; i < natoms; i++)
338                 AtomDuplicate(mp->atoms + i, atoms + i);
339         mp->nframes = -1;  /*  Should be recalculated later  */
340         return mp;
341 }
342
343 Molecule *
344 MoleculeInitWithMolecule(Molecule *mp2, Molecule *mp)
345 {
346         MoleculeFlushFrames(mp);
347         MoleculeInitWithAtoms(mp2, mp->atoms, mp->natoms);
348         if (mp->nbonds > 0) {
349                 if (NewArray(&mp2->bonds, &mp2->nbonds, sizeof(Int)*2, mp->nbonds) == NULL)
350                         goto error;
351                 memmove(mp2->bonds, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
352         }
353         if (mp->nangles > 0) {
354                 if (NewArray(&mp2->angles, &mp2->nangles, sizeof(Int)*3, mp->nangles) == NULL)
355                         goto error;
356                 memmove(mp2->angles, mp->angles, sizeof(Int) * 3 * mp->nangles);
357         }
358         if (mp->ndihedrals > 0) {
359                 if (NewArray(&mp2->dihedrals, &mp2->ndihedrals, sizeof(Int)*4, mp->ndihedrals) == NULL)
360                         goto error;
361                 memmove(mp2->dihedrals, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
362         }
363         if (mp->nimpropers > 0) {
364                 if (NewArray(&mp2->impropers, &mp2->nimpropers, sizeof(Int)*4, mp->nimpropers) == NULL)
365                         goto error;
366                 memmove(mp2->impropers, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
367         }
368         if (mp->nresidues > 0) {
369                 if (NewArray(&mp2->residues, &mp2->nresidues, sizeof(mp->residues[0]), mp->nresidues) == NULL)
370                         goto error;
371                 memmove(mp2->residues, mp->residues, sizeof(mp->residues[0]) * mp->nresidues);
372         }
373         if (mp->cell != NULL) {
374                 mp2->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
375                 memmove(mp2->cell, mp->cell, sizeof(XtalCell));
376         }
377         if (mp->nsyms > 0) {
378                 NewArray(&(mp2->syms), &(mp2->nsyms), sizeof(Transform), mp->nsyms);
379                 memmove(mp2->syms, mp->syms, sizeof(Transform) * mp2->nsyms);
380         }
381
382         /*      mp2->useFlexibleCell = mp->useFlexibleCell; */
383         if (mp->nframe_cells > 0) {
384                 if (NewArray(&mp2->frame_cells, &mp2->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells) == NULL)
385                         goto error;
386                 memmove(mp2->frame_cells, mp->frame_cells, sizeof(Vector) * 4 * mp->nframe_cells);
387         }
388         
389         /* FIXME: should bset (basis set info) and elpot be duplicated or not?  */
390
391         if (mp->par != NULL)
392                 mp2->par = ParameterDuplicate(mp->par);
393         if (mp->arena != NULL) {
394                 md_arena_new(mp2);
395                 md_arena_init_from_arena(mp2->arena, mp->arena);
396         }
397         
398         return mp2;
399   error:
400         Panic("Cannot allocate memory for duplicate molecule");
401         return NULL;  /*  Not reached  */
402 }
403
404 /*  Assign a unique name to this parameter record  */
405 void
406 MoleculeSetName(Molecule *mp, const char *name)
407 {
408         ObjectSetName((Object *)mp, name, (Object *)sMoleculeRoot);
409 }
410
411 const char *
412 MoleculeGetName(Molecule *mp)
413 {
414         return ObjectGetName((Object *)mp);
415 }
416
417 Molecule *
418 MoleculeWithName(const char *name)
419 {
420         return (Molecule *)ObjectWithName(name, (Object *)sMoleculeRoot);
421 }
422
423 void
424 MoleculeSetPath(Molecule *mol, const char *fname)
425 {
426         char *buf, *cwd;
427         if (mol == NULL || fname == NULL)
428                 return;
429         if (fname[0] == '/' || (isalpha(fname[0]) && fname[1] == ':')) {
430                 /*  Full path  */
431                 buf = strdup(fname);
432         } else {
433                 cwd = getcwd(NULL, 0);
434                 asprintf(&buf, "%s/%s", cwd, fname);
435                 free(cwd);
436         }
437         if (mol->path != NULL) {
438                 if (strcmp(mol->path, buf) == 0) {
439                         /*  No change  */
440                         free(buf);
441                         return;
442                 }
443                 free((void *)(mol->path));
444         }
445         mol->path = buf;
446         if (mol->arena != NULL) {
447                 md_close_output_files(mol->arena);
448         }
449 }
450
451 const char *
452 MoleculeGetPath(Molecule *mol)
453 {
454         if (mol == NULL)
455                 return NULL;
456         return mol->path;
457 }
458
459 Molecule *
460 MoleculeRetain(Molecule *mp)
461 {
462         ObjectIncrRefCount((Object *)mp);
463         MoleculeRetainExternalObj(mp);
464         return mp;
465 }
466
467 void
468 MoleculeClear(Molecule *mp)
469 {
470         if (mp == NULL)
471                 return;
472         if (mp->arena != NULL) {
473                 md_arena_set_molecule(mp->arena, NULL);
474                 mp->arena = NULL;
475         }
476         if (mp->par != NULL) {
477                 ParameterRelease(mp->par);
478                 mp->par = NULL;
479         }
480         if (mp->bset != NULL) {
481                 BasisSetRelease(mp->bset);
482                 mp->bset = NULL;
483         }
484         if (mp->atoms != NULL) {
485                 int i;
486                 for (i = 0; i < mp->natoms; i++)
487                         AtomClean(mp->atoms + i);
488                 free(mp->atoms);
489                 mp->atoms = NULL;
490                 mp->natoms = 0;
491         }
492         if (mp->bonds != NULL) {
493                 free(mp->bonds);
494                 mp->bonds = NULL;
495                 mp->nbonds = 0;
496         }
497         if (mp->angles != NULL) {
498                 free(mp->angles);
499                 mp->angles = NULL;
500                 mp->nangles = 0;
501         }
502         if (mp->dihedrals != NULL) {
503                 free(mp->dihedrals);
504                 mp->dihedrals = NULL;
505                 mp->ndihedrals = 0;
506         }
507         if (mp->impropers != NULL) {
508                 free(mp->impropers);
509                 mp->impropers = NULL;
510                 mp->nimpropers = 0;
511         }
512         if (mp->residues != NULL) {
513                 free(mp->residues);
514                 mp->residues = NULL;
515                 mp->nresidues = 0;
516         }
517         if (mp->cell != NULL) {
518                 free(mp->cell);
519                 mp->cell = NULL;
520         }
521         if (mp->syms != NULL) {
522                 free(mp->syms);
523                 mp->syms = NULL;
524                 mp->nsyms = 0;
525         }
526         if (mp->selection != NULL) {
527                 IntGroupRelease(mp->selection);
528                 mp->selection = NULL;
529         }
530         if (mp->frame_cells != NULL) {
531                 free(mp->frame_cells);
532                 mp->frame_cells = NULL;
533                 mp->nframe_cells = 0;
534         }
535         if (mp->bset != NULL) {
536                 BasisSetRelease(mp->bset);
537                 mp->bset = NULL;
538         }
539         if (mp->par != NULL) {
540                 ParameterRelease(mp->par);
541                 mp->par = NULL;
542         }
543         if (mp->elpots != NULL) {
544                 free(mp->elpots);
545                 mp->elpots = NULL;
546                 mp->nelpots = 0;
547         }
548         if (mp->path != NULL) {
549                 free((void *)mp->path);
550                 mp->path = NULL;
551         }
552 }
553
554 void
555 MoleculeRelease(Molecule *mp)
556 {
557         if (mp == NULL)
558                 return;
559         MoleculeReleaseExternalObj(mp);
560         if (ObjectDecrRefCount((Object *)mp) == 0) {
561                 MoleculeClear(mp);
562                 mp->mview->mol = NULL;
563                 MainView_release(mp->mview);
564                 ObjectDealloc((Object *)mp, (Object **)&sMoleculeRoot);
565         }
566 }
567
568 void
569 MoleculeExchange(Molecule *mp1, Molecule *mp2)
570 {
571         Molecule mp_temp;
572         struct MainView *mview1, *mview2;
573         struct MDArena *arena1, *arena2;
574         /*  mview and arena must be kept as they are  */
575         mview1 = mp1->mview;
576         mview2 = mp2->mview;
577         arena1 = mp1->arena;
578         arena2 = mp2->arena;
579         /*  'natoms' is the first member to be copied  */
580         int ofs = offsetof(Molecule, natoms);
581         memmove((char *)(&mp_temp) + ofs, (char *)mp1 + ofs, sizeof(Molecule) - ofs);
582         memmove((char *)mp1 + ofs, (char *)mp2 + ofs, sizeof(Molecule) - ofs);
583         memmove((char *)mp2 + ofs, (char *)(&mp_temp) + ofs, sizeof(Molecule) - ofs);
584         mp1->arena = arena1;
585         mp2->arena = arena2;
586         mp1->mview = mview1;
587         mp2->mview = mview2;
588 /*      if (mp1->arena != NULL && mp1->arena->mol == mp2)
589                 mp1->arena->mol = mp1;
590         if (mp1->arena != NULL && mp1->arena->xmol == mp2)
591                 mp1->arena->xmol = mp1;
592         if (mp2->arena != NULL && mp2->arena->mol == mp1)
593                 mp2->arena->mol = mp2;
594         if (mp2->arena != NULL && mp2->arena->xmol == mp1)
595                 mp2->arena->xmol = mp2; */
596 }
597
598 #pragma mark ====== Mutex ======
599
600 void
601 MoleculeLock(Molecule *mol)
602 {
603         if (mol == NULL || mol->mutex == NULL)
604                 return;
605         MoleculeCallback_lockMutex(mol->mutex);
606 }
607
608 void
609 MoleculeUnlock(Molecule *mol)
610 {
611         if (mol == NULL || mol->mutex == NULL)
612                 return;
613         MoleculeCallback_unlockMutex(mol->mutex);
614 }
615
616 #pragma mark ====== Modify count ======
617
618 void
619 MoleculeIncrementModifyCount(Molecule *mp)
620 {
621         if (mp != NULL) {
622                 if (++(mp->modifyCount) == 1)
623                         MoleculeCallback_notifyModification(mp, 0);
624         /*      fprintf(stderr, "MoleculeIncrementModifyCount: %d\n", mp->modifyCount); */
625         }
626 }
627
628 void
629 MoleculeClearModifyCount(Molecule *mp)
630 {
631         if (mp != NULL) {
632                 mp->modifyCount = 0;
633         /*      fprintf(stderr, "MoleculeClearModifyCount: %d\n", mp->modifyCount); */
634         }
635 }
636
637 #pragma mark ====== File handling functions ======
638
639 static const char *
640 guessMoleculeType(const char *fname)
641 {
642         char buf[1024], *p;
643         FILE *fp;
644         const char *retval = NULL;
645         fp = fopen(fname, "rb");
646         if (fp != NULL) {
647                 memset(buf, 0, sizeof buf);
648                 if (fread(buf, 1, sizeof buf - 1, fp) > 0) {
649                         if (strncmp(buf, "PSF", 3) == 0)
650                                 retval = "psf";
651                         else if (((p = strstr(buf, "ATOM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r'))
652                         || ((p = strstr(buf, "HETATM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r')))
653                                 retval = "pdb";
654                         else
655                                 retval = "???";  /*  unknown  */
656                 }
657                 fclose(fp);
658         }
659         return retval;
660 }
661
662 static int
663 guessElement(Atom *ap)
664 {
665         int atomicNumber = -1;
666         if (ap->atomicNumber > 0)
667                 atomicNumber = ap->atomicNumber;
668         else {
669                 atomicNumber = GuessAtomicNumber(ap->element, ap->weight);
670                 if (atomicNumber <= 0 && ap->aname[0] != 0)
671                         atomicNumber = GuessAtomicNumber(ap->aname, ap->weight);
672         }
673         if (atomicNumber >= 0) {
674                 ap->atomicNumber = atomicNumber;
675                 if (ap->weight <= 0)
676                         ap->weight = WeightForAtomicNumber(atomicNumber);
677                 if (ap->element[0] == 0)
678                         ElementToString(atomicNumber, ap->element);
679         }
680         return atomicNumber;
681 }
682
683 static int
684 sReadLineWithInterrupt(char *buf, int size, FILE *stream, int *lineNumber)
685 {
686         static int lastLineNumber = 0;
687         if (lineNumber != NULL) {
688                 if (*lineNumber == 0)
689                         lastLineNumber = 0;
690                 else if (*lineNumber >= lastLineNumber + 1000) {
691                         if (MyAppCallback_checkInterrupt() != 0)
692                                 return -1;  /*  User interrupt  */
693                         lastLineNumber = *lineNumber;
694                 }
695         }
696         return ReadLine(buf, size, stream, lineNumber);
697 }
698
699 static int
700 s_append_asprintf(char **buf, const char *fmt, ...)
701 {
702         int len;
703         char *s;
704         va_list va;
705         va_start(va, fmt);
706         vasprintf(&s, fmt, va);
707         len = (*buf == NULL ? 0 : strlen(*buf));
708         if (s == NULL)
709                 return len;
710         len += strlen(s);
711         if (*buf == NULL) {
712                 *buf = malloc(len + 1);
713                 **buf = 0;
714         } else {
715                 *buf = realloc(*buf, len + 1);
716         }
717         strcat(*buf, s);
718         free(s);
719         return len;
720 }
721
722 int
723 MoleculeLoadFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
724 {
725         int retval;
726         if (ftype == NULL || *ftype == 0) {
727                 const char *cp;
728                 cp = strrchr(fname, '.');
729                 if (cp != NULL)
730                         ftype = cp + 1;
731                 else {
732                         cp = guessMoleculeType(fname);
733                         if (strcmp(cp, "???") != 0)
734                                 ftype = cp;
735                 }
736         }
737         if (strcasecmp(ftype, "psf") == 0) {
738                 retval = MoleculeLoadPsfFile(mp, fname, errbuf);
739         } else if (strcasecmp(ftype, "pdb") == 0) {
740                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
741         } else if (strcasecmp(ftype, "tep") == 0) {
742                 retval = MoleculeLoadTepFile(mp, fname, errbuf);
743         } else if (strcasecmp(ftype, "res") == 0 || strcasecmp(ftype, "ins") == 0) {
744                 retval = MoleculeLoadShelxFile(mp, fname, errbuf);
745         } else if (strcasecmp(ftype, "fchk") == 0 || strcasecmp(ftype, "fch") == 0) {
746                 retval = MoleculeLoadGaussianFchkFile(mp, fname, errbuf);
747         } else {
748                 s_append_asprintf(errbuf, "Unknown format %s", ftype);
749                 return 1;
750         }
751 /*      if (retval != 0) {
752                 retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
753         } */
754         if (retval == 0)
755                 MoleculeSetPath(mp, fname);
756         return retval;
757 }
758
759 int
760 MoleculeLoadMbsfFile(Molecule *mp, const char *fname, char **errbuf)
761 {
762         FILE *fp;
763         char buf[1024];
764         int i, j, k, err, fn, nframes, nwarnings;
765         int lineNumber;
766         int ibuf[12];
767         Int iibuf[4];
768         double dbuf[12];
769         int mview_ibuf[18];
770         double mview_dbuf[10];
771         char cbuf[12][8];
772         const char **pp;
773         char *bufp, *valp, *comp;
774         Int *ip;
775         Double *dp;
776         Vector v;
777         Atom *ap;
778         const int kUndefined = -10000000;
779         err = 0;
780         *errbuf = NULL;
781         nwarnings = 0;
782         if (mp->natoms != 0 || mp->par != NULL || mp->arena != NULL) {
783                 s_append_asprintf(errbuf, "The molecule must be empty");
784                 return 1;
785         }
786         fp = fopen(fname, "rb");
787         if (fp == NULL) {
788                 s_append_asprintf(errbuf, "Cannot open file");
789                 return 1;
790         }
791         for (i = 0; i < 10; i++)
792                 mview_dbuf[i] = kUndefined;
793         for (i = 0; i < 18; i++)
794                 mview_ibuf[i] = kUndefined;
795         /*      flockfile(fp); */
796         lineNumber = 0;
797         fn = 0;
798         nframes = 0;
799         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
800                 if (strncmp(buf, "!:", 2) != 0)
801                         continue;   /*  Skip until section header is found  */
802                 bufp = buf;
803                 strsep(&bufp, " \t\n");
804                 if (strcmp(buf, "!:atoms") == 0) {
805                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
806                                 if (buf[0] == '!')
807                                         continue;
808                                 if (buf[0] == '\n')
809                                         break;
810                                 /* idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge */
811                                 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) {
812                                         s_append_asprintf(errbuf, "line %d: coordinates cannot be read for atom %d", lineNumber, mp->natoms + 1);
813                                         goto err_exit;
814                                 }
815                                 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
816                                 strncpy(ap->segName, cbuf[0], 4);
817                                 ap->resSeq = ibuf[1];
818                                 strncpy(ap->resName, cbuf[1], 4);
819                                 strncpy(ap->aname, cbuf[2], 4);
820                                 ap->type = AtomTypeEncodeToUInt(cbuf[3]);
821                                 ap->charge = dbuf[0];
822                                 ap->weight = dbuf[1];
823                                 strncpy(ap->element, cbuf[4], 2);
824                                 ap->atomicNumber = ibuf[2];
825                                 ap->occupancy = dbuf[2];
826                                 ap->tempFactor = dbuf[3];
827                                 ap->intCharge = ibuf[3];
828                         }
829                         continue;
830                 } else if (strcmp(buf, "!:atoms_symop") == 0) {
831                         i = 0;
832                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
833                                 if (buf[0] == '!')
834                                         continue;
835                                 if (buf[0] == '\n')
836                                         break;
837                                 /* idx symop symbase */
838                                 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
839                                         s_append_asprintf(errbuf, "line %d: symmetry operations cannot be read for atom %d", lineNumber, i + 1);
840                                         goto err_exit;
841                                 }
842                                 if (i >= mp->natoms) {
843                                         s_append_asprintf(errbuf, "line %d: too many atomic symmetry info\n", lineNumber);
844                                         goto err_exit;
845                                 }
846                                 ap = ATOM_AT_INDEX(mp->atoms, i);
847                                 ap->symop.sym = ibuf[1] / 1000000;
848                                 ap->symop.dx = (ibuf[1] % 1000000) / 10000;
849                                 ap->symop.dy = (ibuf[1] % 10000) / 100;
850                                 ap->symop.dz = ibuf[1] % 100;
851                                 ap->symbase = ibuf[2];
852                                 i++;
853                         }
854                         continue;
855                 } else if (strcmp(buf, "!:atoms_fix") == 0) {
856                         i = 0;
857                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
858                                 if (buf[0] == '!')
859                                         continue;
860                                 if (buf[0] == '\n')
861                                         break;
862                                 /* idx fix_force fix_pos */
863                                 if (sscanf(buf, "%d %lf %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]) < 5) {
864                                         s_append_asprintf(errbuf, "line %d: fix atom info cannot be read for atom %d", lineNumber, i + 1);
865                                         goto err_exit;
866                                 }
867                                 if (i >= mp->natoms) {
868                                         s_append_asprintf(errbuf, "line %d: too many fix atom info\n", lineNumber);
869                                         goto err_exit;
870                                 }
871                                 ap = ATOM_AT_INDEX(mp->atoms, i);
872                                 ap->fix_force = dbuf[0];
873                                 ap->fix_pos.x = dbuf[1];
874                                 ap->fix_pos.y = dbuf[2];
875                                 ap->fix_pos.z = dbuf[3];
876                                 i++;
877                         }
878                         continue;
879                 } else if (strcmp(buf, "!:uff_types") == 0) {
880                         i = 0;
881                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
882                                 if (buf[0] == '!')
883                                         continue;
884                                 if (buf[0] == '\n')
885                                         break;
886                                 /* idx uff_type */
887                                 if (sscanf(buf, "%d %6s", &ibuf[0], cbuf[0]) < 2) {
888                                         s_append_asprintf(errbuf, "line %d: uff type info cannot be read for atom %d", lineNumber, i + 1);
889                                         goto err_exit;
890                                 }
891                                 if (i >= mp->natoms) {
892                                         s_append_asprintf(errbuf, "line %d: too many uff type info\n", lineNumber);
893                                         goto err_exit;
894                                 }
895                                 ap = ATOM_AT_INDEX(mp->atoms, i);
896                                 strncpy(ap->uff_type, cbuf[0], 5);
897                                 ap->uff_type[5] = 0;
898                                 i++;
899                         }
900                 } else if (strcmp(buf, "!:mm_exclude") == 0) {
901                         i = 0;
902                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
903                                 if (buf[0] == '!')
904                                         continue;
905                                 if (buf[0] == '\n')
906                                         break;
907                                 /* idx mm_exclude periodic_exclude */
908                                 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
909                                         s_append_asprintf(errbuf, "line %d: mm_exclude flags cannot be read for atom %d", lineNumber, i + 1);
910                                         goto err_exit;
911                                 }
912                                 if (i >= mp->natoms) {
913                                         s_append_asprintf(errbuf, "line %d: too many mm_exclude flags\n", lineNumber);
914                                         goto err_exit;
915                                 }
916                                 ap = ATOM_AT_INDEX(mp->atoms, i);
917                                 ap->mm_exclude = (ibuf[1] != 0);
918                                 ap->periodic_exclude = (ibuf[2] != 0);
919                                 i++;
920                         }
921                         continue;
922                 } else if (strcmp(buf, "!:pi_anchor") == 0) {
923                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
924                                 if (buf[0] == '!')
925                                         continue;
926                                 if (buf[0] == '\n')
927                                         break;
928                                 /* idx count */
929                                 if ((j = sscanf(buf, "%d %d", &ibuf[0], &ibuf[1])) < 2) {
930                                         s_append_asprintf(errbuf, "line %d: bad format for pi_anchor", lineNumber);
931                                         goto err_exit;
932                                 }
933                                 i = ibuf[0];
934                                 ap = ATOM_AT_INDEX(mp->atoms, i);
935                                 if (ap->anchor != NULL) {
936                                         s_append_asprintf(errbuf, "line %d: warning: duplicate pi_anchor entry", lineNumber);
937                                         AtomConnectResize(&ap->anchor->connect, 0);
938                                         free(ap->anchor->coeffs);
939                                         free(ap->anchor);
940                                 }
941                                 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
942                                 if (ibuf[1] < 2 || ibuf[1] >= mp->natoms) {
943                                         s_append_asprintf(errbuf, "line %d: bad number of components for pi_anchor", lineNumber);
944                                         goto err_exit;
945                                 }
946                                 AtomConnectResize(&ap->anchor->connect, ibuf[1]);
947                                 ip = AtomConnectData(&ap->anchor->connect);
948                                 NewArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), ibuf[1]);
949                                 j = ibuf[1];
950                                 for (i = 0; i < j; i++) {
951                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
952                                                 s_append_asprintf(errbuf, "line %d: unexpected end of file while reading pi_anchors", lineNumber);
953                                                 goto err_exit;
954                                         }
955                                         if (sscanf(buf, "%d %lf", &ibuf[0], &dbuf[0]) < 2) {
956                                                 s_append_asprintf(errbuf, "line %d: bad format for pi_anchor", lineNumber);
957                                                 goto err_exit;
958                                         }
959                                         if (ibuf[0] < 0 || ibuf[0] >= mp->natoms) {
960                                                 s_append_asprintf(errbuf, "line %d: atom index out of range", lineNumber);
961                                                 goto err_exit;
962                                         }
963                                         if (dbuf[0] <= 0.0) {
964                                                 s_append_asprintf(errbuf, "line %d: the pi anchor weights should be positive", lineNumber);
965                                                 goto err_exit;
966                                         }
967                                         ip[i] = ibuf[0];
968                                         ap->anchor->coeffs[i] = dbuf[0];
969                                 }
970                         }
971                         continue;
972                 } else if (strcmp(buf, "!:positions") == 0) {
973                         i = 0;
974                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
975                                 if (buf[0] == '!')
976                                         continue;
977                                 if (buf[0] == '\n')
978                                         break;
979                                 /* idx x y z */
980                                 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) {
981                                         s_append_asprintf(errbuf, "line %d: atom position cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
982                                         goto err_exit;
983                                 }
984                                 if (j > 4 && nframes != 0) {
985                                         s_append_asprintf(errbuf, "line %d: atom position sigma can only be given for frame 0", lineNumber);
986                                         goto err_exit;
987                                 }
988                                 if (j > 4 && j != 7) {
989                                         s_append_asprintf(errbuf, "line %d: atom position sigma cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
990                                         goto err_exit;
991                                 }
992                                 if (i >= mp->natoms) {
993                                         s_append_asprintf(errbuf, "line %d: too many atom position records\n", lineNumber);
994                                         goto err_exit;
995                                 }
996                                 v.x = dbuf[0];
997                                 v.y = dbuf[1];
998                                 v.z = dbuf[2];
999                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1000                                 if (nframes > 0) {
1001                                         AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes, &v);
1002                                         if (nframes == 1)
1003                                                 ap->frames[0] = ap->r;
1004                                 }
1005                                 ap->r = v;
1006                                 if (j == 7) {
1007                                         ap->sigma.x = dbuf[3];
1008                                         ap->sigma.y = dbuf[4];
1009                                         ap->sigma.z = dbuf[5];
1010                                 }
1011                                 i++;
1012                         }
1013                         nframes++;
1014                         if (nframes >= 2) {
1015                                 mp->nframes = nframes;
1016                                 mp->cframe = nframes - 1;
1017                         } else {
1018                                 mp->nframes = mp->cframe = 0;
1019                         }
1020                         continue;
1021                 } else if (strcmp(buf, "!:bonds") == 0) {
1022                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1023                                 if (buf[0] == '!')
1024                                         continue;
1025                                 if (buf[0] == '\n')
1026                                         break;
1027                                 /* from1 to1 from2 to2 from3 to3 from4 to4 */ 
1028                                 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]);
1029                                 if (i < 2 || i % 2 != 0) {
1030                                         s_append_asprintf(errbuf, "line %d: bad bond format", lineNumber);
1031                                         goto err_exit;
1032                                 }
1033                                 for (j = 0; j < i; j += 2) {
1034                                         iibuf[0] = ibuf[j];
1035                                         iibuf[1] = ibuf[j + 1];
1036                                         if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[0] == iibuf[1]) {
1037                                                 s_append_asprintf(errbuf, "line %d: warning: bad bond specification (%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1]);
1038                                                 nwarnings++;
1039                                         } else if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[0])->connect), iibuf[1])) {
1040                                                 s_append_asprintf(errbuf, "line %d: warning: bond %d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1]);
1041                                                 nwarnings++;
1042                                         } else {
1043                                                 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, iibuf);
1044                                                 AtomConnectInsertEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[0])->connect), -1, iibuf[1]);
1045                                                 AtomConnectInsertEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[1])->connect), -1, iibuf[0]);
1046                                         }
1047                                 }
1048                         }
1049                         continue;
1050                 } else if (strcmp(buf, "!:bond_orders") == 0) {
1051                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1052                                 if (buf[0] == '!')
1053                                         continue;
1054                                 if (buf[0] == '\n')
1055                                         break;
1056                                 /* b1 b2 b3 b4 */
1057                                 i = sscanf(buf, "%lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]);
1058                                 if (i == 0) {
1059                                         s_append_asprintf(errbuf, "line %d: bad bond order format", lineNumber);
1060                                         goto err_exit;
1061                                 }
1062                                 for (j = 0; j < i; j++) {
1063                                         AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbondOrders, &dbuf[j]);
1064                                 }
1065                         }
1066                         if (mp->nbondOrders > mp->nbonds) {
1067                                 s_append_asprintf(errbuf, "line %d: warning: the number of bond order info (%d) exceeds number of bonds (%d) - ignoring excess info\n", lineNumber, mp->nbondOrders, mp->nbonds);
1068                                 nwarnings++;
1069                                 mp->nbondOrders = mp->nbonds;
1070                         } else if (mp->nbondOrders < mp->nbonds) {
1071                                 s_append_asprintf(errbuf, "line %d: warning: the number of bond order info (%d) is less than number of bonds (%d)\n", lineNumber, mp->nbondOrders, mp->nbonds);
1072                                 nwarnings++;
1073                                 j = mp->nbondOrders;
1074                                 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbonds - 1, NULL);
1075                                 for (i = j; i < mp->nbonds; i++)
1076                                         mp->bondOrders[i] = 0.0;
1077                         }
1078                         continue;
1079                         
1080                 } else if (strcmp(buf, "!:angles") == 0) {
1081                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1082                                 if (buf[0] == '!')
1083                                         continue;
1084                                 if (buf[0] == '\n')
1085                                         break;
1086                                 /* a1 b1 c1 a2 b2 c2 a3 b3 c3 */ 
1087                                 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]);
1088                                 if (i == 0 || i % 3 != 0) {
1089                                         s_append_asprintf(errbuf, "line %d: bad angle format", lineNumber);
1090                                         goto err_exit;
1091                                 }
1092                                 for (j = 0; j < i; j += 3) {
1093                                         iibuf[0] = ibuf[j];
1094                                         iibuf[1] = ibuf[j + 1];
1095                                         iibuf[2] = ibuf[j + 2];
1096                                         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]) {
1097                                                 s_append_asprintf(errbuf, "line %d: warning: bad angle specification (%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1098                                                 nwarnings++;
1099                                         } else if (MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[2]) == 0) {
1100                                                 s_append_asprintf(errbuf, "line %d: warning: angle with non-bonded atoms (%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1101                                                 nwarnings++;                                            
1102                                         } else if (MoleculeLookupAngle(mp, iibuf[0], iibuf[1], iibuf[2]) >= 0) {
1103                                                 s_append_asprintf(errbuf, "line %d: warning: angle %d-%d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1104                                                 nwarnings++;
1105                                         } else {
1106                                                 AssignArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, mp->nangles, iibuf);
1107                                         }
1108                                 }
1109                         }
1110                         continue;
1111                 } else if (strcmp(buf, "!:dihedrals") == 0) {
1112                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1113                                 if (buf[0] == '!')
1114                                         continue;
1115                                 if (buf[0] == '\n')
1116                                         break;
1117                                 /* a1 b1 c1 d1 a2 b2 c2 d2 */ 
1118                                 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]);
1119                                 if (i == 0 || i % 4 != 0) {
1120                                         s_append_asprintf(errbuf, "line %d: bad dihedral format", lineNumber);
1121                                         goto err_exit;
1122                                 }
1123                                 for (j = 0; j < i; j += 4) {
1124                                         iibuf[0] = ibuf[j];
1125                                         iibuf[1] = ibuf[j + 1];
1126                                         iibuf[2] = ibuf[j + 2];
1127                                         iibuf[3] = ibuf[j + 3];
1128                                         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]) {
1129                                                 s_append_asprintf(errbuf, "line %d: warning: bad dihedral specification (%d-%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2], iibuf[3]);
1130                                                 nwarnings++;
1131                                         } else if (MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[2]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[3]) == 0) {
1132                                                 s_append_asprintf(errbuf, "line %d: warning: dihedral with non-bonded atoms (%d-%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2], iibuf[3]);
1133                                                 nwarnings++;                                            
1134                                         } else if (MoleculeLookupDihedral(mp, iibuf[0], iibuf[1], iibuf[2], iibuf[3]) >= 0) {
1135                                                 s_append_asprintf(errbuf, "line %d: warning: dihedral %d-%d-%d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2], iibuf[3]);
1136                                                 nwarnings++;
1137                                         } else {
1138                                                 AssignArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, mp->ndihedrals, iibuf);
1139                                         }
1140                                 }
1141                         }
1142                         continue;
1143                 } else if (strcmp(buf, "!:impropers") == 0) {
1144                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1145                                 if (buf[0] == '!')
1146                                         continue;
1147                                 if (buf[0] == '\n')
1148                                         break;
1149                                 /* a1 b1 c1 d1 a2 b2 c2 d2 */ 
1150                                 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]);
1151                                 if (i == 0 || i % 4 != 0) {
1152                                         s_append_asprintf(errbuf, "line %d: bad improper format", lineNumber);
1153                                         goto err_exit;
1154                                 }
1155                                 for (j = 0; j < i; j += 4) {
1156                                         iibuf[0] = ibuf[j];
1157                                         iibuf[1] = ibuf[j + 1];
1158                                         iibuf[2] = ibuf[j + 2];
1159                                         iibuf[3] = ibuf[j + 3];
1160                                         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]) {
1161                                                 s_append_asprintf(errbuf, "line %d: warning: bad improper specification (%d-%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2], iibuf[3]);
1162                                                 nwarnings++;
1163                                         } else if (MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[1]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[3]) == 0) {
1164                                                 s_append_asprintf(errbuf, "line %d: warning: improper with non-bonded atoms (%d-%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2], iibuf[3]);
1165                                                 nwarnings++;                                            
1166                                         } else if (MoleculeLookupImproper(mp, iibuf[0], iibuf[1], iibuf[2], iibuf[3]) >= 0) {
1167                                                 s_append_asprintf(errbuf, "line %d: warning: improper %d-%d-%d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2], iibuf[3]);
1168                                                 nwarnings++;
1169                                         } else {
1170                                                 AssignArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, mp->nimpropers, iibuf);
1171                                         }
1172                                 }
1173                         }
1174                         continue;
1175                 } else if (strcmp(buf, "!:xtalcell") == 0 && mp->cell == NULL) {
1176                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1177                                 if (buf[0] == '!')
1178                                         continue;
1179                                 if (buf[0] == '\n')
1180                                         break;
1181                                 /* a b c alpha beta gamma [sigmaflag] */ 
1182                                 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) {
1183                                         s_append_asprintf(errbuf, "line %d: bad xtalcell format", lineNumber);
1184                                         goto err_exit;
1185                                 }
1186                                 MoleculeSetCell(mp, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], 0);
1187                                 if (j == 7 && ibuf[0] != 0) {
1188                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1189                                                 s_append_asprintf(errbuf, "line %d: sigma for xtalcell are missing", lineNumber);
1190                                                 goto err_exit;
1191                                         }
1192                                         if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1193                                                 s_append_asprintf(errbuf,"line %d: bad xtalcell sigma format", lineNumber);
1194                                                 goto err_exit;
1195                                         }
1196                                         if (mp->cell != NULL) {
1197                                                 mp->cell->has_sigma = 1;
1198                                                 for (i = 0; i < 6; i++) {
1199                                                         mp->cell->cellsigma[i] = dbuf[i];
1200                                                 }
1201                                         } else {
1202                                                 s_append_asprintf(errbuf, "line %d: cell sigma are given while cell is not given", lineNumber);
1203                                         }
1204                                 }
1205                         }
1206                         continue;
1207                 } else if (strcmp(buf, "!:symmetry_operations") == 0) {
1208                         i = 0;
1209                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1210                                 Transform tr;
1211                                 if (buf[0] == '!')
1212                                         continue;
1213                                 if (buf[0] == '\n')
1214                                         break;
1215                                 /* a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3 */
1216                                 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1217                                         s_append_asprintf(errbuf, "line %d: bad symmetry_operation format", lineNumber);
1218                                         goto err_exit;
1219                                 }
1220                                 if (i < 3) {
1221                                         tr[i] = dbuf[0];
1222                                         tr[i + 3] = dbuf[1];
1223                                         tr[i + 6] = dbuf[2];
1224                                 } else {
1225                                         tr[9] = dbuf[0];
1226                                         tr[10] = dbuf[1];
1227                                         tr[11] = dbuf[2];
1228                                 }
1229                                 i++;
1230                                 if (i == 4) {
1231                                         AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1232                                         i = 0;
1233                                 }
1234                         }
1235                         continue;
1236                 } else if (strcmp(buf, "!:anisotropic_thermal_parameters") == 0) {
1237                         i = 0;
1238                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1239                                 if (buf[0] == '!')
1240                                         continue;
1241                                 if (buf[0] == '\n')
1242                                         break;
1243                                 /* b11 b22 b33 b12 b13 b23 [has_sigma] */
1244                                 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) {
1245                                         s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters cannot be read for atom %d", lineNumber, i + 1);
1246                                         goto err_exit;
1247                                 }
1248                                 if (i >= mp->natoms) {
1249                                         s_append_asprintf(errbuf, "line %d: too many anisotropic thermal parameters\n", lineNumber);
1250                                         goto err_exit;
1251                                 }
1252                                 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) {
1253                                         /*  Skip it  */
1254                                 } else {
1255                                         MoleculeSetAniso(mp, i, 0, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], NULL);
1256                                 }
1257                                 if (j == 7 && ibuf[0] != 0) {
1258                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1259                                                 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma missing", lineNumber);
1260                                                 goto err_exit;
1261                                         }
1262                                         if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1263                                                 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma cannot be read for atom %d", lineNumber, i + 1);
1264                                                 goto err_exit;
1265                                         }
1266                                         ap = ATOM_AT_INDEX(mp->atoms, i);
1267                                         if (ap->aniso == NULL) {
1268                                                 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma are given while the parameters are not given", lineNumber);
1269                                                 goto err_exit;
1270                                         }
1271                                         ap->aniso->has_bsig = 1;
1272                                         for (j = 0; j < 6; j++)
1273                                                 ap->aniso->bsig[j] = dbuf[j];
1274                                 }
1275                                 i++;
1276                         }
1277                         continue;
1278                 } else if (strcmp(buf, "!:periodic_box") == 0) {
1279                         Vector vs[5];
1280                         Byte has_sigma = 0;
1281                         i = 0;
1282                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1283                                 if (buf[0] == '!')
1284                                         continue;
1285                                 if (buf[0] == '\n')
1286                                         break;
1287                                 /* 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] */
1288                                 if (i < 4) {
1289                                         if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1290                                                 s_append_asprintf(errbuf, "line %d: bad periodic_box format", lineNumber);
1291                                                 goto err_exit;
1292                                         }
1293                                         vs[i].x = dbuf[0];
1294                                         vs[i].y = dbuf[1];
1295                                         vs[i].z = dbuf[2];
1296                                         i++;
1297                                         continue;
1298                                 }
1299                                 if ((j = sscanf(buf, "%d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3])) < 3) {
1300                                         s_append_asprintf(errbuf, "line %d: bad periodic_box format", lineNumber);
1301                                         goto err_exit;
1302                                 }
1303                                 if (j == 4 && ibuf[3] != 0)
1304                                         has_sigma = 1;
1305                                 cbuf[0][0] = ibuf[0];
1306                                 cbuf[0][1] = ibuf[1];
1307                                 cbuf[0][2] = ibuf[2];
1308                                 MoleculeSetPeriodicBox(mp, vs, vs + 1, vs + 2, vs + 3, cbuf[0], 0);
1309                                 if (has_sigma) {
1310                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1311                                                 s_append_asprintf(errbuf, "line %d: sigma for cell parameters are missing", lineNumber);
1312                                                 goto err_exit;
1313                                         }
1314                                         if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1315                                                 s_append_asprintf(errbuf, "line %d: bad periodic_box sigma format", lineNumber);
1316                                                 goto err_exit;
1317                                         }
1318                                         if (mp->cell != NULL) {
1319                                                 mp->cell->has_sigma = 1;
1320                                                 for (i = 0; i < 6; i++) {
1321                                                         mp->cell->cellsigma[i] = dbuf[i];
1322                                                 }
1323                                         } else {
1324                                                 s_append_asprintf(errbuf, "line %d: cell sigma are given while cell is not given", lineNumber);
1325                                         }
1326                                 }
1327                                 break;
1328                         }
1329                         continue;
1330                 } else if (strcmp(buf, "!:frame_periodic_boxes") == 0) {
1331                         Vector vs[5];
1332                         i = 0;
1333                 /*      mp->useFlexibleCell = 1;  *//*  The presence of this block causes asserting this flag  */
1334                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1335                                 if (buf[0] == '!')
1336                                         continue;
1337                                 if (buf[0] == '\n')
1338                                         break;
1339                                 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1340                                         s_append_asprintf(errbuf, "line %d: bad frame_periodic_box format", lineNumber);
1341                                         goto err_exit;
1342                                 }
1343                                 vs[i].x = dbuf[0];
1344                                 vs[i].y = dbuf[1];
1345                                 vs[i].z = dbuf[2];
1346                                 i++;
1347                                 if (i == 4) {
1348                                         AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells, vs);
1349                                         i = 0;
1350                                 }
1351                         }
1352                         if (mp->cframe < mp->nframe_cells) {
1353                                 /*  mp->cframe should already have been set when positions are read  */
1354                                 Vector *vp = &mp->frame_cells[mp->cframe * 4];
1355                                 static char defaultFlags[] = {1, 1, 1};
1356                                 char *flags = (mp->cell != NULL ? mp->cell->flags : defaultFlags);
1357                                 MoleculeSetPeriodicBox(mp, vp, vp + 1, vp + 2, vp + 3, flags, 0);
1358                         }
1359                         continue;
1360                 } else if (strcmp(buf, "!:md_parameters") == 0) {
1361                         MDArena *arena;
1362                         if (mp->arena == NULL)
1363                                 mp->arena = md_arena_new(NULL);
1364                         arena = mp->arena;
1365                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1366                                 if (buf[0] == '!')
1367                                         continue;
1368                                 if (buf[0] == '\n')
1369                                         break;
1370                                 bufp = buf;
1371                                 comp = strsep(&bufp, " \t");
1372                                 if (bufp != NULL) {
1373                                         while (*bufp == ' ' || *bufp == '\t')
1374                                                 bufp++;
1375                                         valp = strsep(&bufp, "\n");
1376                                 } else valp = NULL;
1377                                 if (strcmp(comp, "alchem_flags") == 0) {
1378                                         j = (valp == NULL ? 0 : atoi(valp));
1379                                         if (j > 0) {
1380                                                 valp = (char *)malloc(j);
1381                                                 i = 0;
1382                                                 while ((k = fgetc(fp)) >= 0) {
1383                                                         ungetc(k, fp);
1384                                                         if (k < '0' || k > '9') {
1385                                                                 s_append_asprintf(errbuf, "line %d: too few flags in alchem_flags block", lineNumber + 1);
1386                                                                 free(valp);
1387                                                                 goto err_exit;
1388                                                         }
1389                                                         ReadLine(buf, sizeof buf, fp, &lineNumber);
1390                                                         bufp = buf;
1391                                                         while (*bufp != 0) {
1392                                                                 if (*bufp >= '0' && *bufp <= '2') {
1393                                                                         if (i >= j) {
1394                                                                                 s_append_asprintf(errbuf, "line %d: too many flags in alchem_flags block", lineNumber);
1395                                                                                 free(valp);
1396                                                                                 goto err_exit;
1397                                                                         }
1398                                                                         valp[i++] = *bufp - '0';
1399                                                                 } else if (*bufp != ' ' && *bufp != '\t' && *bufp != '\n') {
1400                                                                         s_append_asprintf(errbuf, "line %d: strange character (0x%02x) in alchem_flags block", lineNumber, (int)*bufp);
1401                                                                         free(valp);
1402                                                                         goto err_exit;
1403                                                                 }
1404                                                                 bufp++;
1405                                                         }
1406                                                         if (i == j)
1407                                                                 break;
1408                                                 }
1409                                                 md_set_alchemical_flags(arena, j, valp);
1410                                                 free(valp);
1411                                         }
1412                                         continue;
1413                                 }
1414                                 /*  In the following, the redundant "!= NULL" is to suppress suprious warning  */
1415                                 if ((strcmp(comp, "log_file") == 0 && (pp = &arena->log_result_name) != NULL)
1416                                         || (strcmp(comp, "coord_file") == 0 && (pp = &arena->coord_result_name) != NULL)
1417                                         || (strcmp(comp, "vel_file") == 0 && (pp = &arena->vel_result_name) != NULL)
1418                                         || (strcmp(comp, "force_file") == 0 && (pp = &arena->force_result_name) != NULL)
1419                                         || (strcmp(comp, "debug_file") == 0 && (pp = &arena->debug_result_name) != NULL)) {
1420                                         if (*valp == 0 || strstr(valp, "(null)") == valp)
1421                                                 *pp = NULL;
1422                                         else {
1423                                                 valp = strdup(valp);
1424                                                 if (valp != NULL) {
1425                                                         char *valp1 = strchr(valp, '\n');
1426                                                         if (valp1 != NULL)
1427                                                                 *valp1 = 0;
1428                                                 }
1429                                                 *pp = valp;
1430                                         }
1431                                 } else if ((strcmp(comp, "debug_output_level") == 0 && (ip = &arena->debug_output_level) != NULL)
1432                                                    || (strcmp(comp, "coord_output_freq") == 0 && (ip = &arena->coord_output_freq) != NULL)
1433                                                    || (strcmp(comp, "energy_output_freq") == 0 && (ip = &arena->energy_output_freq) != NULL)
1434                                                    || (strcmp(comp, "coord_frame") == 0 && (ip = &arena->coord_result_frame) != NULL)
1435                                                    || (strcmp(comp, "andersen_freq") == 0 && (ip = &arena->andersen_thermo_freq) != NULL)
1436                                                    || (strcmp(comp, "random_seed") == 0 && (ip = &arena->random_seed) != NULL)
1437                                                    || (strcmp(comp, "use_xplor_shift") == 0 && (ip = &arena->use_xplor_shift) != NULL)
1438                                                    || (strcmp(comp, "relocate_center") == 0 && (ip = &arena->relocate_center) != NULL)
1439                                                    || (strcmp(comp, "surface_potential_freq") == 0 && (ip = &arena->surface_potential_freq) != NULL)
1440                                                    || (strcmp(comp, "use_graphite") == 0 && (ip = &arena->use_graphite) != NULL)) {
1441                                         *ip = (valp == NULL ? 0 : atoi(valp));
1442                                 } else if ((strcmp(comp, "timestep") == 0 && (dp = &arena->timestep) != NULL)
1443                                                    || (strcmp(comp, "cutoff") == 0 && (dp = &arena->cutoff) != NULL)
1444                                                    || (strcmp(comp, "electro_cutoff") == 0 && (dp = &arena->electro_cutoff) != NULL)
1445                                                    || (strcmp(comp, "pairlist_distance") == 0 && (dp = &arena->pairlist_distance) != NULL)
1446                                                    || (strcmp(comp, "switch_distance") == 0 && (dp = &arena->switch_distance) != NULL)
1447                                                    || (strcmp(comp, "temperature") == 0 && (dp = &arena->temperature) != NULL)
1448                                                    || (strcmp(comp, "andersen_coupling") == 0 && (dp = &arena->andersen_thermo_coupling) != NULL)
1449                                                    || (strcmp(comp, "dielectric") == 0 && (dp = &arena->dielectric) != NULL)
1450                                                    || (strcmp(comp, "gradient_convergence") == 0 && (dp = &arena->gradient_convergence) != NULL)
1451                                                    || (strcmp(comp, "coordinate_convergence") == 0 && (dp = &arena->coordinate_convergence) != NULL)
1452                                                    || (strcmp(comp, "scale14_vdw") == 0 && (dp = &arena->scale14_vdw) != NULL)
1453                                                    || (strcmp(comp, "scale14_elect") == 0 && (dp = &arena->scale14_elect) != NULL)
1454                                                    || (strcmp(comp, "surface_probe_radius") == 0 && (dp = &arena->probe_radius) != NULL)
1455                                                    || (strcmp(comp, "surface_tension") == 0 && (dp = &arena->surface_tension) != NULL)
1456                                                    || (strcmp(comp, "alchemical_lambda") == 0 && (dp = &arena->alchem_lambda) != NULL)
1457                                                    || (strcmp(comp, "alchemical_delta_lambda") == 0 && (dp = &arena->alchem_dlambda) != NULL)) {
1458                                         *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1459                                 }
1460                         }
1461                         continue;
1462                 } else if (strcmp(buf, "!:pressure_control_parameters") == 0) {
1463                         MDPressureArena *pressure;
1464                         if (mp->arena == NULL)
1465                                 mp->arena = md_arena_new(mp);
1466                         if (mp->arena->pressure == NULL)
1467                                 mp->arena->pressure = pressure_new();
1468                         pressure = mp->arena->pressure;
1469                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1470                                 if (buf[0] == '!')
1471                                         continue;
1472                                 if (buf[0] == '\n')
1473                                         break;
1474                                 bufp = buf;
1475                                 comp = strsep(&bufp, " \t");
1476                                 if (bufp != NULL) {
1477                                         while (*bufp == ' ' || *bufp == '\t')
1478                                                 bufp++;
1479                                         valp = strsep(&bufp, "\n");
1480                                 } else valp = NULL;
1481                                 if (strcmp(comp, "pressure") == 0) {
1482                                         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) {
1483                                                 s_append_asprintf(errbuf, "line %d: bad format", lineNumber);
1484                                                 goto err_exit;
1485                                         }
1486                                         for (i = 0; i < 9; i++)
1487                                                 pressure->apply[i] = dbuf[i];
1488                                 } else if (strcmp(comp, "pressure_cell_flexibility") == 0) {
1489                                         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) {
1490                                                 s_append_asprintf(errbuf, "line %d: bad format", lineNumber);
1491                                                 goto err_exit;
1492                                         }
1493                                         for (i = 0; i < 8; i++)
1494                                                 pressure->cell_flexibility[i] = dbuf[i];
1495                                 } else if ((strcmp(comp, "pressure_freq") == 0 && (ip = &pressure->freq) != NULL)) {
1496                                         *ip = (valp == NULL ? 0 : atoi(valp));
1497                                 } else if ((strcmp(comp, "pressure_coupling") == 0 && (dp = &pressure->coupling) != NULL)
1498                                                    || (strcmp(comp, "pressure_fluctuate_cell_origin") == 0 && (dp = &pressure->fluctuate_cell_origin) != NULL)
1499                                                    || (strcmp(comp, "pressure_fluctuate_cell_orientation") == 0 && (dp = &pressure->fluctuate_cell_orientation) != NULL)) {
1500                                         *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1501                                 }
1502                         }
1503                         continue;
1504                 } else if (strcmp(buf, "!:velocity") == 0) {
1505                         i = 0;
1506                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1507                                 if (buf[0] == '!')
1508                                         continue;
1509                                 if (buf[0] == '\n')
1510                                         break;
1511                                 /* idx vx vy vz */
1512                                 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1513                                         s_append_asprintf(errbuf, "line %d: atom velocity cannot be read for atom %d", lineNumber, i + 1);
1514                                         goto err_exit;
1515                                 }
1516                                 if (i >= mp->natoms) {
1517                                         s_append_asprintf(errbuf, "line %d: too many atom velocity records\n", lineNumber);
1518                                         goto err_exit;
1519                                 }
1520                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1521                                 ap->v.x = dbuf[0];
1522                                 ap->v.y = dbuf[1];
1523                                 ap->v.z = dbuf[2];
1524                                 i++;
1525                         }
1526                         continue;
1527                 } else if (strcmp(buf, "!:force") == 0) {
1528                         i = 0;
1529                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1530                                 if (buf[0] == '!')
1531                                         continue;
1532                                 if (buf[0] == '\n')
1533                                         break;
1534                                 /* idx fx fy fz */
1535                                 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1536                                         s_append_asprintf(errbuf, "line %d: atom force cannot be read for atom %d", lineNumber, i + 1);
1537                                         goto err_exit;
1538                                 }
1539                                 if (i >= mp->natoms) {
1540                                         s_append_asprintf(errbuf, "line %d: too many atom force records\n", lineNumber);
1541                                         goto err_exit;
1542                                 }
1543                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1544                                 ap->f.x = dbuf[0];
1545                                 ap->f.y = dbuf[1];
1546                                 ap->f.z = dbuf[2];
1547                                 i++;
1548                         }
1549                         continue;
1550                 } else if (strcmp(buf, "!:parameter") == 0 || strcmp(buf, "!:parameters") == 0) {
1551                         Parameter *par = mp->par;
1552                         if (par == NULL) {
1553                                 mp->par = ParameterNew();
1554                                 par = mp->par;
1555                         }
1556                         bufp = NULL;
1557                         i = 0;
1558                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1559                                 if (buf[0] == '!')
1560                                         continue;
1561                                 if (buf[0] == '\n')
1562                                         break;
1563                                 j = ParameterReadFromString(par, buf, &bufp, fname, lineNumber, 0);
1564                                 if (j < 0) {
1565                                         s_append_asprintf(errbuf, "%s", bufp);
1566                                         free(bufp);
1567                                         goto err_exit;
1568                                 }
1569                                 i += j;
1570                         }
1571                         if (bufp != NULL) {
1572                                 s_append_asprintf(errbuf, "%s", bufp);
1573                                 free(bufp);
1574                         }
1575                         continue;
1576                 } else if (strcmp(buf, "!:trackball") == 0) {
1577                         i = 0;
1578                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1579                                 if (buf[0] == '!')
1580                                         continue;
1581                                 if (buf[0] == '\n')
1582                                         break;
1583                                 /* scale; trx try trz; theta_deg x y z */
1584                                 if ((i == 0 && sscanf(buf, "%lf", &mview_dbuf[0]) < 1)
1585                                         || (i == 1 && sscanf(buf, "%lf %lf %lf",
1586                                                                                  &mview_dbuf[1], &mview_dbuf[2], &mview_dbuf[3]) < 3)
1587                                         || (i == 2 && sscanf(buf, "%lf %lf %lf %lf",
1588                                                                                  &mview_dbuf[4], &mview_dbuf[5], &mview_dbuf[6], &mview_dbuf[7]) < 4)) {
1589                                         s_append_asprintf(errbuf, "line %d: bad trackball format", lineNumber);
1590                                         goto err_exit;
1591                                 }
1592                                 i++;
1593                         }
1594                         continue;
1595                 } else if (strcmp(buf, "!:view") == 0) {
1596                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1597                                 if (buf[0] == '!')
1598                                         continue;
1599                                 if (buf[0] == '\n')
1600                                         break;
1601                                 bufp = buf;
1602                                 comp = strsep(&bufp, " \t");
1603                                 if (bufp != NULL) {
1604                                         while (*bufp == ' ' || *bufp == '\t')
1605                                                 bufp++;
1606                                         valp = strsep(&bufp, "\n");
1607                                 } else valp = NULL;
1608                                 /*  In the following, the redundant "!= NULL" is to suppress suprious warning  */
1609                                 if ((strcmp(comp, "show_unit_cell") == 0 && (i = 1))
1610                                         || (strcmp(comp, "show_periodic_box") == 0 && (i = 2))
1611                                         || (strcmp(comp, "show_expanded_atoms") == 0 && (i = 3))
1612                                         || (strcmp(comp, "show_ellipsoids") == 0 && (i = 4))
1613                                         || (strcmp(comp, "show_hydrogens") == 0 && (i = 5))
1614                                         || (strcmp(comp, "show_dummy_atoms") == 0 && (i = 6))
1615                                         || (strcmp(comp, "show_rotation_center") == 0 && (i = 7))
1616                                         || (strcmp(comp, "show_graphite_flag") == 0 && (i = 8))
1617                                         || (strcmp(comp, "show_periodic_image_flag") == 0 && (i = 9))
1618                                         || (strcmp(comp, "show_graphite") == 0 && (i = 10))
1619                                         || (strcmp(comp, "atom_resolution") == 0 && (i = 11))
1620                                         || (strcmp(comp, "bond_resolution") == 0 && (i = 12))) {
1621                                         mview_ibuf[i - 1] = atoi(valp);
1622                                 } else if ((strcmp(comp, "atom_radius") == 0 && (i = 8))
1623                                         || (strcmp(comp, "bond_radius") == 0 && (i = 9))) {
1624                                         mview_dbuf[i] = strtod(valp, NULL);
1625                                 } else if (strcmp(comp, "show_periodic_image") == 0) {
1626                                         sscanf(valp, "%d %d %d %d %d %d",
1627                                                    &mview_ibuf[12], &mview_ibuf[13], &mview_ibuf[14],
1628                                                    &mview_ibuf[15], &mview_ibuf[16], &mview_ibuf[17]);
1629                                 }
1630                         }
1631                         continue;
1632                 }
1633                 /*  Unknown sections are silently ignored  */
1634         }
1635
1636         MoleculeCleanUpResidueTable(mp);
1637         if (mp->arena != NULL)
1638                 md_arena_set_molecule(mp->arena, mp);
1639
1640         fclose(fp);
1641         if (mp->mview != NULL) {
1642                 if (mview_ibuf[0] != kUndefined)
1643                         mp->mview->showUnitCell = mview_ibuf[0];
1644                 if (mview_ibuf[1] != kUndefined)
1645                         mp->mview->showPeriodicBox = mview_ibuf[1];
1646                 if (mview_ibuf[2] != kUndefined)
1647                         mp->mview->showExpandedAtoms = mview_ibuf[2];
1648                 if (mview_ibuf[3] != kUndefined)
1649                         mp->mview->showEllipsoids = mview_ibuf[3];
1650                 if (mview_ibuf[4] != kUndefined)
1651                         mp->mview->showHydrogens = mview_ibuf[4];
1652                 if (mview_ibuf[5] != kUndefined)
1653                         mp->mview->showDummyAtoms = mview_ibuf[5];
1654                 if (mview_ibuf[6] != kUndefined)
1655                         mp->mview->showRotationCenter = mview_ibuf[6];
1656                 if (mview_ibuf[7] != kUndefined)
1657                         mp->mview->showGraphiteFlag = mview_ibuf[7];
1658                 if (mview_ibuf[8] != kUndefined)
1659                         mp->mview->showPeriodicImageFlag = mview_ibuf[8];
1660                 if (mview_ibuf[9] != kUndefined)
1661                         mp->mview->showGraphite = mview_ibuf[9];
1662                 if (mview_ibuf[10] != kUndefined && mview_ibuf[10] >= 6)
1663                         mp->mview->atomResolution = mview_ibuf[10];
1664                 if (mview_ibuf[11] != kUndefined && mview_ibuf[11] >= 4)
1665                         mp->mview->bondResolution = mview_ibuf[11];
1666                 for (i = 0; i < 6; i++) {
1667                         if (mview_ibuf[12 + i] != kUndefined)
1668                                 mp->mview->showPeriodicImage[i] = mview_ibuf[12 + i];
1669                 }
1670                 if (mview_dbuf[8] != kUndefined)
1671                         mp->mview->atomRadius = mview_dbuf[8];
1672                 if (mview_dbuf[9] != kUndefined)
1673                         mp->mview->bondRadius = mview_dbuf[9];          
1674                 if (mp->mview->track != NULL) {
1675                         if (mview_dbuf[0] != kUndefined)
1676                                 TrackballSetScale(mp->mview->track, mview_dbuf[0]);
1677                         if (mview_dbuf[1] != kUndefined)
1678                                 TrackballSetTranslate(mp->mview->track, mview_dbuf + 1);
1679                         if (mview_dbuf[4] != kUndefined)
1680                                 TrackballSetRotate(mp->mview->track, mview_dbuf + 4);
1681                 }
1682         }
1683
1684         return 0;
1685
1686 err_exit:
1687         fclose(fp);
1688         /*  The content of mp may be broken, so make it empty  */
1689         MoleculeClear(mp);
1690         return -1;      
1691 }
1692
1693 int
1694 MoleculeLoadPsfFile(Molecule *mp, const char *fname, char **errbuf)
1695 {
1696         FILE *fp;
1697         char buf[1024];
1698         char *p;
1699         int section = -1;
1700         int i, j, err, fn;
1701         int lineNumber;
1702         Int ibuf[12];
1703         Vector *frames = NULL;
1704         Atom *ap;
1705         err = 0;
1706         *errbuf = NULL;
1707         if (mp == NULL)
1708                 mp = MoleculeNew();
1709         else MoleculeClear(mp);
1710         fp = fopen(fname, "rb");
1711         if (fp == NULL) {
1712                 s_append_asprintf(errbuf, "Cannot open file");
1713                 return 1;
1714         }
1715 /*      flockfile(fp); */
1716         lineNumber = 0;
1717         fn = 0;
1718         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1719                 if (strncmp(buf, "PSF", 3) == 0) {
1720                         section = 0;
1721                         continue;
1722                 } else {
1723                         for (p = buf; *p != 0 && isspace(*p); p++) {}
1724                         if (*p == 0) {
1725                                 section++;
1726                                 continue;
1727                         }
1728                 }
1729                 if (strstr(buf, "!COORD") != NULL) {
1730                         /*  Extended psf file with coordinates  */
1731                         if (fn > 0) {
1732                                 /*  Allocate a temporary storage for frames  */
1733                                 size_t size = sizeof(Vector) * mp->natoms * fn;
1734                                 if (frames == NULL)
1735                                         frames = (Vector *)malloc(size);
1736                                 else
1737                                         frames = (Vector *)realloc(frames, size);
1738                                 if (frames == NULL)
1739                                         goto panic;
1740                         }
1741                         /*  Read coordinates  */
1742                         for (i = 0; i < mp->natoms; i++) {
1743                                 double dval[3];
1744                                 Vector r;
1745                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1746                                         err = 1;
1747                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading coordinates (frame %d)", lineNumber, fn);
1748                                         goto exit;
1749                                 }
1750                                 if (sscanf(buf, "%lg %lg %lg", dval, dval + 1, dval + 2) != 3) {
1751                                         err = 1;
1752                                         s_append_asprintf(errbuf, "line %d: coordinates cannot be read for atom %d", lineNumber, i + 1);
1753                                         goto exit;
1754                                 }
1755                                 r.x = dval[0];
1756                                 r.y = dval[1];
1757                                 r.z = dval[2];
1758                                 if (fn == 0)
1759                                         ATOM_AT_INDEX(mp->atoms, i)->r = r;
1760                                 else
1761                                         frames[mp->natoms * (fn - 1) + i] = r;
1762                         }
1763                         fn++;
1764                         continue;
1765                 }
1766                 
1767                 if (section == 2) {
1768                         /*  Atoms  */
1769                         Int natoms;
1770                         ReadFormat(buf, "I8", &natoms);
1771                         if (natoms == 0)
1772                                 continue;
1773                         if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
1774                                 goto panic;
1775                         mp->nresidues = 0;
1776                         for (i = 0; i < natoms; i++) {
1777                                 struct {
1778                                         char segName[5], resName[4], atomName[5], atomType[3], element[3];
1779                                         Int serial;
1780                                 } w;
1781                                 memset(&w, 0, sizeof(w));
1782                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1783                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1784                                         err = 1;
1785                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading atoms", lineNumber);
1786                                         goto exit;
1787                                 }
1788                                 ReadFormat(buf, "I8 x1 S4 I5 x1 S3 x2 S4 x1 S4 F16 F10",
1789                                         &w.serial, w.segName, &ap->resSeq, w.resName, w.atomName, 
1790                                         w.atomType, &ap->charge, &ap->weight);
1791                                 strncpy(ap->segName, w.segName, 4);
1792                                 strncpy(ap->resName, w.resName, 3);
1793                                 strncpy(ap->aname, w.atomName, 4);
1794                                 ap->type = AtomTypeEncodeToUInt(w.atomType);
1795                                 /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
1796                                 ap->atomicNumber = GuessAtomicNumber(w.atomName, ap->weight);
1797                                 ElementToString(ap->atomicNumber, w.element);
1798                                 strncpy(ap->element, w.element, 2);
1799                         /*      w.element[0] = 0;
1800                                 for (p = w.atomName; *p != 0; p++) {
1801                                         if (isalpha(*p) && *p != '_') {
1802                                                 w.element[0] = toupper(*p);
1803                                                 if (isalpha(p[1]) && p[1] != '_') {
1804                                                         w.element[1] = toupper(p[1]);
1805                                                         w.element[2] = 0;
1806                                                 } else {
1807                                                         w.element[1] = 0;
1808                                                 }
1809                                                 break;
1810                                         }
1811                                 }
1812                                 strncpy(ap->element, w.element, 2);
1813                                 ap->atomicNumber = ElementToInt(w.element); */
1814                                 if (w.resName[0] == 0)
1815                                         strncpy(ap->resName, "XXX", 3);
1816                                 if (ap->resSeq > mp->nresidues)
1817                                         mp->nresidues = ap->resSeq;
1818                         }
1819                         if (mp->residues != NULL)
1820                                 free(mp->residues);
1821                         if (NewArray(&mp->residues, &mp->nresidues, sizeof(char (*)[4]), mp->nresidues + 1) == 0)
1822                                 goto panic;
1823                         for (i = 0; i < mp->natoms; i++) {
1824                                 j = mp->atoms[i].resSeq;
1825                                 if (mp->residues[j][0] == 0)
1826                                         strncpy(mp->residues[j], mp->atoms[i].resName, 4);
1827                         }
1828                         continue;
1829                 } else if (section == 3) {
1830                         /*  Bonds  */
1831                         Int nbonds;
1832                         Int *bp;
1833                         ReadFormat(buf, "I8", &nbonds);
1834                         if (nbonds == 0)
1835                                 continue;
1836                         if (NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, nbonds) == NULL)
1837                                 goto panic;
1838                         bp = mp->bonds;
1839                         for (i = 0; i < nbonds; i += 4) {
1840                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1841                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading bonds", lineNumber);
1842                                         err = 1;
1843                                         goto exit;
1844                                 }
1845                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1846                                         ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1847                                 for (j = 0; j < 4 && i + j < nbonds; j++) {
1848                                         Int b1, b2;
1849                                         Atom *ap;
1850                                         b1 = ibuf[j * 2] - 1;    /* Internal atom number is 0-based */
1851                                         b2 = ibuf[j * 2 + 1] - 1;
1852                                         if (b1 < 0 || b1 >= mp->natoms || b2 < 0 || b2 >= mp->natoms) {
1853                                                 s_append_asprintf(errbuf, "line %d: The bond %d-%d includes non-existent atom", lineNumber, b1+1, b2+1);
1854                                                 err = 1;
1855                                                 goto exit;
1856                                         }
1857                                         *bp++ = b1;
1858                                         *bp++ = b2;
1859                                         ap = ATOM_AT_INDEX(mp->atoms, b1);
1860                                         AtomConnectInsertEntry(&ap->connect, -1, b2);
1861                                         ap = ATOM_AT_INDEX(mp->atoms, b2);
1862                                         AtomConnectInsertEntry(&ap->connect, -1, b1);
1863                                 }
1864                         }
1865                         continue;
1866                 } else if (section == 4) {
1867                         /*  Angles  */
1868                         Int nangles;
1869                         Int *gp;
1870                         ReadFormat(buf, "I8", &nangles);
1871                         if (nangles == 0)
1872                                 continue;
1873                         if (NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, nangles) == NULL)
1874                                 goto panic;
1875                         gp = mp->angles;
1876                         for (i = 0; i < nangles; i += 3) {
1877                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1878                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading angles", lineNumber);
1879                                         err = 1;
1880                                         goto exit;
1881                                 }
1882                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1883                                         ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7, ibuf + 8);
1884                                 for (j = 0; j < 3 && i + j < nangles; j++) {
1885                                         Int a1, a2, a3;
1886                                         a1 = ibuf[j * 3] - 1;   /* Internal atom number is 0-based */
1887                                         a2 = ibuf[j * 3 + 1] - 1;
1888                                         a3 = ibuf[j * 3 + 2] - 1;
1889                                         if (a1 < 0 || a1 >= mp->natoms || a2 < 0 || a2 >= mp->natoms || a3 < 0 || a3 >= mp->natoms) {
1890                                                 s_append_asprintf(errbuf, "line %d: The angle %d-%d-%d includes non-existent atom", lineNumber, a1+1, a2+1, a3+1);
1891                                                 err = 1;
1892                                                 goto exit;
1893                                         }
1894                                         *gp++ = a1;
1895                                         *gp++ = a2;
1896                                         *gp++ = a3;
1897                                 }
1898                         }
1899                         continue;
1900                 } else if (section == 5 || section == 6) {
1901                         /*  Dihedrals and Impropers  */
1902                         Int ndihedrals;
1903                         Int *dp;
1904                         ReadFormat(buf, "I8", &ndihedrals);
1905                         if (ndihedrals == 0)
1906                                 continue;
1907                         if (section == 5) {
1908                                 if (NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, ndihedrals) == NULL)
1909                                         goto panic;
1910                                 dp = mp->dihedrals;
1911                         } else {
1912                                 if (NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, ndihedrals) == NULL)
1913                                         goto panic;
1914                                 dp = mp->impropers;
1915                         }
1916                         for (i = 0; i < ndihedrals; i += 2) {
1917                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1918                                         fclose(fp);
1919                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading %s", lineNumber, (section == 5 ? "dihedral" : "improper"));
1920                                         err = 1;
1921                                         goto exit;
1922                                 }
1923                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3, ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1924                                 for (j = 0; j < 2 && i + j < ndihedrals; j++) {
1925                                         Int d1, d2, d3, d4;
1926                                         d1 = ibuf[j * 4] - 1;   /*  Internal atom number is 0-based  */
1927                                         d2 = ibuf[j * 4 + 1] - 1;
1928                                         d3 = ibuf[j * 4 + 2] - 1;
1929                                         d4 = ibuf[j * 4 + 3] - 1;
1930                                         if (d1 < 0 || d1 >= mp->natoms || d2 < 0 || d2 >= mp->natoms || d3 < 0 || d3 >= mp->natoms || d4 < 0 || d4 >= mp->natoms) {
1931                                                 s_append_asprintf(errbuf, "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);
1932                                                 err = 1;
1933                                                 goto exit;
1934                                         }
1935                                         *dp++ = d1;
1936                                         *dp++ = d2;
1937                                         *dp++ = d3;
1938                                         *dp++ = d4;
1939                                 }
1940                         }
1941                         continue;
1942                 }
1943         }
1944         
1945         /*  Create frames for each atom if necessary  */
1946         if (fn > 1) {
1947                 for (i = 0; i < mp->natoms; i++) {
1948                         ap = ATOM_AT_INDEX(mp->atoms, i);
1949                         ap->frames = (Vector *)malloc(sizeof(Vector) * fn);
1950                         if (ap->frames == NULL)
1951                                 goto panic;
1952                         ap->nframes = fn;
1953                         for (j = 0; j < fn; j++)
1954                                 ap->frames[j] = frames[mp->natoms * j + i];
1955                 }
1956                 free(frames);
1957                 frames = NULL;
1958         }
1959
1960   exit:
1961 /*      funlockfile(fp); */
1962         fclose(fp);
1963         mp->nframes = -1;  /*  Should be recalculated later  */
1964         if (err)
1965                 return 1;
1966         else if (section == -1)
1967                 return -1;
1968         return 0;
1969   panic:
1970         Panic("low memory while reading structure file %s", fname);
1971         return 1; /* not reached */
1972 }
1973
1974 /* ("-x", "y", "-z+0.5") -> (-1,0,0,0,0,1,0,0,0,0,-1,0.5)  */
1975 static int
1976 sMoleculeSymopStringsToTransform(char **symops, Transform tr)
1977 {
1978         int i;
1979         char *symop;
1980         memset(tr, 0, sizeof(Transform));
1981         for (i = 0; i < 3; i++) {
1982                 symop = symops[i];
1983                 if (symop == NULL)
1984                         return 1;
1985                 while (*symop != 0) {
1986                         int sn = 1;
1987                         while (isspace(*symop))
1988                                 symop++;
1989                         if (*symop == 0 || *symop == '\r' || *symop == 'n')
1990                                 break;
1991                         if (*symop == '-') {
1992                                 sn = -1;
1993                                 symop++;
1994                         } else if (*symop == '+') {
1995                                 sn = 1;
1996                                 symop++;
1997                         }
1998                         while (isspace(*symop))
1999                                 symop++;
2000                         if (*symop == '.' || isdigit(*symop)) {
2001                                 /*  Numerical offset  */
2002                                 double d = strtod(symop, &symop);
2003                                 if (*symop == '/') {
2004                                         double dd = strtod(symop + 1, &symop);
2005                                         if (dd > 0)
2006                                                 d /= dd;
2007                                         else
2008                                                 return 1;  /*  Bad format  */
2009                                 }
2010                                 tr[9 + i] = d * sn;
2011                         } else if (*symop == 'x' || *symop == 'X') {
2012                                 tr[i] = sn;
2013                                 symop++;
2014                         } else if (*symop == 'y' || *symop == 'Y') {
2015                                 tr[i + 3] = sn;
2016                                 symop++;
2017                         } else if (*symop == 'z' || *symop == 'Z') {
2018                                 tr[i + 6] = sn;
2019                                 symop++;
2020                         } else return 1;  /*  Bad format  */
2021                 } /* end while (*symop != 0) */
2022         }
2023         return 0;
2024 }
2025
2026 static void
2027 sMoleculeGenerateSymopWithTransform(Molecule *mp, Transform gtr, int num)
2028 {
2029         int i, j;
2030         Transform tr;
2031         if (num <= 0)
2032                 num = mp->nsyms;
2033         for (i = 0; i < num; i++) {
2034                 memmove(tr, mp->syms[i], sizeof(Transform));
2035                 TransformMul(tr, gtr, tr);
2036                 for (j = 9; j < 12; j++) {
2037                         if (tr[j] >= 1.0)
2038                                 tr[j] -= 1.0;
2039                         else if (tr[j] <= 0.0)
2040                                 tr[j] += 1.0;
2041                 }
2042                 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
2043         }
2044 }
2045
2046 static char *
2047 sChomp(char *buf)
2048 {
2049         char *p = buf + strlen(buf) - 1;
2050         if (p >= buf && (*p == '\n' || *p == '\r')) {
2051                 *p = 0;
2052                 if (--p >= buf && (*p == '\n' || *p == '\r')) {
2053                         *p = 0;
2054                 }
2055         }
2056         return buf;
2057 }
2058
2059 int
2060 MoleculeLoadTepFile(Molecule *mp, const char *fname, char **errbuf)
2061 {
2062         FILE *fp;
2063         char buf[1024];
2064         int section = -1;
2065         int lineNumber;
2066         int cellType;
2067         Int ibuf[12];
2068         Double fbuf[12];
2069         Int *bonds, nbonds;
2070         *errbuf = NULL;
2071         if (mp == NULL)
2072                 mp = MoleculeNew();
2073         fp = fopen(fname, "rb");
2074         if (fp == NULL) {
2075                 s_append_asprintf(errbuf, "Cannot open file");
2076                 return 1;
2077         }
2078         lineNumber = 0;
2079         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2080                 if (section == -1) {
2081                         /*  Title  */
2082                         section = 0;
2083                         continue;
2084                 }
2085                 if (section == 0) {
2086                         /*  XtalCell  */
2087                         ReadFormat(buf, "I1F8F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5);
2088                         cellType = ibuf[0];
2089                         MoleculeSetCell(mp, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], 0);
2090                         section = 1;
2091                         continue;
2092                 }
2093                 if (section == 1) {
2094                         /*  Symmetry  */
2095                         Transform tr;
2096                         if (cellType == 0) {
2097                                 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);
2098                                 tr[0] = fbuf[1];
2099                                 tr[3] = fbuf[2];
2100                                 tr[6] = fbuf[3];
2101                                 tr[1] = fbuf[5];
2102                                 tr[4] = fbuf[6];
2103                                 tr[7] = fbuf[7];
2104                                 tr[2] = fbuf[9];
2105                                 tr[5] = fbuf[10];
2106                                 tr[8] = fbuf[11];
2107                                 tr[9] = fbuf[0];
2108                                 tr[10] = fbuf[4];
2109                                 tr[11] = fbuf[8];
2110                         } else {
2111                                 char *symops[3], *brks;
2112                                 sChomp(buf);
2113                                 memset(tr, 0, sizeof(Transform));
2114                                 ReadFormat(buf, "I1", ibuf);
2115                                 symops[0] = strtok_r(buf + 1, ", ", &brks);
2116                                 symops[1] = strtok_r(NULL, ", ", &brks);
2117                                 symops[2] = strtok_r(NULL, ", ", &brks);
2118                                 if (sMoleculeSymopStringsToTransform(symops, tr)) {
2119                                         s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2120                                         return 1;
2121                                 }
2122                         }
2123                         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2124                                 goto panic;
2125                         if (ibuf[0] != 0)
2126                                 section = 2;
2127                         continue;
2128                 }
2129                 if (section == 2) {      /*  Atoms  */
2130                         char name[8];
2131                         Atom *ap;
2132                         int atomType;
2133                         int atomIndex = mp->natoms;
2134                         ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2135                         memset(ap, 0, gSizeOfAtomRecord);
2136                         ReadFormat(buf, "S6x3x9x9F9F9F9F9", name, fbuf, fbuf+1, fbuf+2, fbuf+3);
2137                         strncpy(ap->aname, name, 4);
2138                         ap->r.x = fbuf[0];
2139                         ap->r.y = fbuf[1];
2140                         ap->r.z = fbuf[2];
2141                         MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2142                 /*      ap->atomicNumber = AtomNameToElement(ap->name);
2143                         ElementToString(ap->atomicNumber, ap->element); */
2144                 /*      sAtomSetElement(ap, -1, ap->name); */
2145                         guessElement(ap);
2146                         atomType = fbuf[3];
2147                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2148                                 s_append_asprintf(errbuf, "unexpected end of file");
2149                                 return 1;
2150                         }
2151                         ReadFormat(buf, "I1F8F9F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2152                         atomType = fbuf[6];
2153                         if ((atomType >= 0 && atomType <= 5) || (atomType >= 8 && atomType <= 10)) { 
2154                                 /*  Anisotropic thermal parameters  */
2155                                 MoleculeSetAniso(mp, atomIndex, atomType, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[5], fbuf[4], NULL);
2156                         }
2157                         if (ibuf[0] != 0)
2158                                 section = 3;
2159                         continue;
2160                 }
2161         }
2162         fclose(fp);
2163         MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
2164         if (nbonds > 0) {
2165                 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2166                 free(bonds);
2167         }
2168         mp->nframes = -1;  /*  Should be recalculated later  */
2169         return 0;
2170   panic:
2171         Panic("low memory while reading structure file %s", fname);
2172         return -1; /* not reached */
2173 }
2174
2175 int
2176 MoleculeLoadShelxFile(Molecule *mp, const char *fname, char **errbuf)
2177 {
2178         FILE *fp;
2179         char buf[1024];
2180         char *p1, *p2;
2181         int n;
2182         int lineNumber;
2183         int latticeType;
2184         int currentResSeq = 0;
2185         char currentResName[6];
2186         Transform tr;
2187         int ibuf[12];
2188         float fbuf[12];
2189         Double dbuf[12];
2190         Int nsfacs = 0;
2191         Int nbonds, *bonds;
2192         char (*sfacs)[4] = NULL;
2193
2194         *errbuf = NULL;
2195         if (mp == NULL)
2196                 mp = MoleculeNew();
2197         currentResName[0] = 0;
2198         fp = fopen(fname, "rb");
2199         if (fp == NULL) {
2200                 s_append_asprintf(errbuf, "Cannot open file");
2201                 return 1;
2202         }
2203         lineNumber = 0;
2204         tr[0] = tr[4] = tr[8] = 1;
2205         tr[1] = tr[2] = tr[3] = tr[5] = tr[6] = tr[7] = tr[9] = tr[10] = tr[11] = 0;
2206         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), 0, tr) == 0)
2207                 goto panic;
2208         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2209                 if (strncmp(buf, "CELL", 4) == 0) {
2210                         /*  XtalCell  */
2211                         sscanf(buf + 4, " %f %f %f %f %f %f %f", fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2212                         MoleculeSetCell(mp, fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], fbuf[6], 0);
2213                         continue;
2214                 } else if (strncmp(buf, "SFAC", 4) == 0) {
2215                         sChomp(buf);
2216                         for (p1 = strtok_r(buf + 4, " ", &p2); p1 != NULL; p1 = strtok_r(NULL, " ", &p2)) {
2217                                 char *pp = (char *)AssignArray(&sfacs, &nsfacs, 4, nsfacs, NULL);
2218                                 if (pp == NULL)
2219                                         goto panic;
2220                                 strncpy(pp, p1, 3);
2221                                 pp[3] = 0;
2222                         }
2223                         continue;
2224                 } else if (strncmp(buf, "LATT", 4) == 0) {
2225                         sscanf(buf + 4, " %d", &latticeType);
2226                         continue;
2227                 } else if (strncmp(buf, "SYMM", 4) == 0) {
2228                         char *symops[3], *brks;
2229                         memset(tr, 0, sizeof(Transform));
2230                 //      ReadFormat(buf + 4, "I1", ibuf);
2231                         sChomp(buf);
2232                         symops[0] = strtok_r(buf + 4, ",", &brks);
2233                         symops[1] = strtok_r(NULL, ",", &brks);
2234                         symops[2] = strtok_r(NULL, ",", &brks);
2235                         if (sMoleculeSymopStringsToTransform(symops, tr)) {
2236                                 s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2237                                 return 1;
2238                         }
2239                         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2240                                 goto panic;
2241                         continue;
2242                 } else if (strncmp(buf, "RESI", 4) == 0) {
2243                         for (p1 = buf + 4; isspace(*p1); p1++);
2244                         if (isalpha(*p1)) {
2245                                 for (p2 = p1 + 1; isalnum(*p2); p2++);
2246                                 *p2 = 0;
2247                                 strncpy(currentResName, p1, 4);
2248                                 currentResName[4] = 0;
2249                                 p1 = p2 + 1;
2250                         } else currentResName[0] = 0;
2251                         sscanf(buf + 4, " %d", &currentResSeq);
2252                         continue;
2253                 } else {
2254                         /* Atom name: [A-Za-z]{1,2}[0-9]*  */
2255                         for (p1 = buf; p1 < buf + 2 && (isalpha(*p1) && *p1 != '_'); p1++);
2256                         if (p1 > buf) {
2257                                 while (isdigit(*p1))
2258                                         p1++;
2259                         }
2260                         if (p1 > buf && p1 <= buf + 4 && isspace(*p1)) {
2261                                 /*  Atom  */
2262                                 Atom *ap;
2263                                 char cont[4];
2264                                 int atomIndex = mp->natoms;
2265                                 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2266                                 memset(ap, 0, gSizeOfAtomRecord);
2267                                 strncpy(ap->aname, buf, 4);
2268                                 n = sscanf(p1, " %d %f %f %f %f %f %f %2s", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, cont);
2269                                 if (n == 8 && strcmp(cont, "=") == 0) {
2270                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2271                                                 s_append_asprintf(errbuf, "line %d: unexpected end of file within the atom cards", lineNumber);
2272                                                 return 1;
2273                                         }
2274                                         sscanf(buf, " %f %f %f %f", fbuf+6, fbuf+7, fbuf+8, fbuf+9);
2275                                         n = 10;   /*  Aniso  */
2276                                 } else n = 5; /*  Iso  */
2277                                 ap->r.x = fbuf[0];
2278                                 ap->r.y = fbuf[1];
2279                                 ap->r.z = fbuf[2];
2280                                 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2281                                 ap->occupancy = fbuf[3];
2282                                 if (ap->aname[0] != 'Q' && ibuf[0] >= 1 && ibuf[0] <= nsfacs) {
2283                                         strncpy(ap->element, sfacs[ibuf[0] - 1], 2);
2284                                         ap->element[2] = 0;
2285                                 /*      sAtomSetElement(ap, -1, sfacs[ibuf[0] - 1]); */
2286                                 /*      strncpy(ap->element, sfacs[ibuf[0] - 1], 4);
2287                                         ap->atomicNumber = ElementToInt(ap->element); */
2288                         /*      } else {
2289                                         sAtomSetElement(ap, -1, ap->name); */
2290                                 /*      ap->atomicNumber = AtomNameToElement(ap->name);
2291                                         ElementToString(ap->atomicNumber, ap->element); */
2292                                 }
2293                                 guessElement(ap);
2294                                 if (n == 10 || fbuf[4] >= 0.0) {
2295                                         int i, c, j;
2296                                         /*  Read in the standard deviations  */
2297                                         ReadLine(buf, sizeof buf, fp, &lineNumber);
2298                                         for (i = 0; i < 9; i++) {
2299                                                 j = 3 + i * 8;
2300                                                 c = buf[j + 8];
2301                                                 buf[j + 8] = 0;
2302                                                 dbuf[i] = strtod(buf + j, NULL);
2303                                                 buf[j + 8] = c;
2304                                         }
2305                                         ap->sigma.x = dbuf[0];
2306                                         ap->sigma.y = dbuf[1];
2307                                         ap->sigma.z = dbuf[2];
2308                                 }
2309                                 if (n == 5)
2310                                         ap->tempFactor = fbuf[4] * 78.9568352087147; /* 8*pi*pi */
2311                                 else
2312                                         MoleculeSetAniso(mp, atomIndex, 8, fbuf[4], fbuf[5], fbuf[6], fbuf[9], fbuf[7], fbuf[8], dbuf);
2313                                 ap->resSeq = currentResSeq;
2314                                 strncpy(ap->resName, currentResName, 4);
2315                         }
2316                         continue;
2317                 }
2318         }
2319         fclose(fp);
2320
2321         /*  Add symmetry operations according to the lattice type  */
2322         switch (latticeType < 0 ? -latticeType : latticeType) {
2323                 static Transform tr_i = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0.5};
2324                 static Transform tr_c = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0};
2325                 static Transform tr_a = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.5};
2326                 static Transform tr_b = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5};
2327                 static Transform tr_r1 = {0, 1, 0, -1, -1, 0, 0, 0, 1, 0, 0, 0};
2328                 static Transform tr_r2 = {-1, -1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0};
2329                 case 1:  /* P */
2330                         break;
2331                 case 2:  /* I */
2332                         sMoleculeGenerateSymopWithTransform(mp, tr_i, 0);
2333                         break;
2334                 case 3:  /* Rhombohedral obverse on hexagonal axes  */
2335                         n = mp->nsyms;
2336                         sMoleculeGenerateSymopWithTransform(mp, tr_r1, n);
2337                         sMoleculeGenerateSymopWithTransform(mp, tr_r2, n);
2338                         break;
2339                 case 4:  /* F */
2340                         n = mp->nsyms;
2341                         sMoleculeGenerateSymopWithTransform(mp, tr_a, n);
2342                         sMoleculeGenerateSymopWithTransform(mp, tr_b, n);
2343                         sMoleculeGenerateSymopWithTransform(mp, tr_c, n);
2344                         break;
2345                 case 5:  /* A */
2346                         sMoleculeGenerateSymopWithTransform(mp, tr_a, 0);
2347                         break;
2348                 case 6:  /* B */
2349                         sMoleculeGenerateSymopWithTransform(mp, tr_b, 0);
2350                         break;
2351                 case 7:  /* C */
2352                         sMoleculeGenerateSymopWithTransform(mp, tr_c, 0);
2353                         break;
2354         }
2355                 
2356         if (latticeType > 0) {
2357                 static Transform tr_inv = {-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0};
2358                 sMoleculeGenerateSymopWithTransform(mp, tr_inv, 0);
2359         }
2360         
2361         MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
2362         if (nbonds > 0) {
2363                 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2364                 free(bonds);
2365         }
2366         mp->nframes = -1;  /*  Should be recalculated later  */
2367         return 0;
2368   panic:
2369         Panic("low memory while reading structure file %s", fname);
2370         return -1; /* not reached */
2371 }
2372
2373 /*  Add one gaussian orbital shell information (not undoable)  */
2374 int
2375 MoleculeAddGaussianOrbitalShell(Molecule *mol, Int sym, Int nprims, Int a_idx)
2376 {
2377         BasisSet *bset;
2378         ShellInfo *shellp;
2379         if (mol == NULL)
2380                 return -1;  /*  Molecule is empty  */
2381         bset = mol->bset;
2382         if (bset == NULL) {
2383                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2384                 if (bset == NULL)
2385                         return -2;  /*  Low memory  */
2386         }
2387         shellp = AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), bset->nshells, NULL);
2388         if (shellp == NULL)
2389                 return -2;  /*  Low memory  */
2390         switch (sym) {
2391                 case 0:  shellp->sym = kGTOType_S;  shellp->ncomp = 1; break;
2392                 case 1:  shellp->sym = kGTOType_P;  shellp->ncomp = 3; break;
2393                 case -1: shellp->sym = kGTOType_SP; shellp->ncomp = 4; break;
2394                 case 2:  shellp->sym = kGTOType_D;  shellp->ncomp = 6; break;
2395                 case -2: shellp->sym = kGTOType_D5; shellp->ncomp = 5; break;
2396                 case 3:  shellp->sym = kGTOType_F;  shellp->ncomp = 10; break;
2397                 case -3: shellp->sym = kGTOType_F7; shellp->ncomp = 7; break;
2398                 case 4:  shellp->sym = kGTOType_G;  shellp->ncomp = 15; break;
2399                 case -4: shellp->sym = kGTOType_G9; shellp->ncomp = 9; break;
2400                 default:
2401                         return -3;  /* Unsupported shell type  */
2402         }
2403         shellp->nprim = nprims;
2404         shellp->a_idx = a_idx;
2405         if (bset->shells < shellp) {
2406                 shellp->m_idx = shellp[-1].m_idx + shellp[-1].ncomp;
2407                 shellp->p_idx = shellp[-1].p_idx + shellp[-1].nprim;
2408         } else {
2409                 shellp->m_idx = 0;
2410                 shellp->p_idx = 0;
2411         }
2412         return 0;
2413 }
2414
2415 /*  Add a set of gaussian primitive coefficients (not undoable)  */
2416 int
2417 MoleculeAddGaussianPrimitiveCoefficients(Molecule *mol, Double exponent, Double contraction, Double contraction_sp)
2418 {
2419         BasisSet *bset;
2420         PrimInfo *primp;
2421         if (mol == NULL)
2422                 return -1;  /*  Molecule is empty  */
2423         bset = mol->bset;
2424         if (bset == NULL) {
2425                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2426                 if (bset == NULL)
2427                         return -2;  /*  Low memory  */
2428         }
2429         primp = AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), bset->npriminfos, NULL);
2430         if (primp == NULL)
2431                 return -2;  /*  Low memory  */
2432         primp->A = exponent;
2433         primp->C = contraction;
2434         primp->Csp = contraction_sp;
2435         return 0;
2436 }
2437
2438 /*  Set MO coefficients for idx-th MO  */
2439 int
2440 MoleculeSetMOCoefficients(Molecule *mol, Int idx, Double energy, Int ncomps, Double *coeffs)
2441 {
2442         BasisSet *bset;
2443         int i, n;
2444         if (mol == NULL)
2445                 return -1;  /*  Molecule is empty  */
2446         bset = mol->bset;
2447         if (bset == NULL) {
2448                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2449                 if (bset == NULL)
2450                         return -2;  /*  Low memory  */
2451         }
2452         if (bset->nmos == 0) {
2453                 if (bset->nshells > 0) {
2454                         /*  Shell info is already set: calculate the number of MOs from there  */
2455                         for (i = n = 0; i < bset->nshells; i++)
2456                                 n += bset->shells[i].ncomp;
2457                         bset->ncomps = n;
2458                 } else if (ncomps > 0) {
2459                         bset->ncomps = ncomps;
2460                 }
2461                 if (bset->rflag == 0)
2462                         bset->nmos = bset->ncomps * 2;
2463                 else
2464                         bset->nmos = bset->ncomps;
2465                 if (bset->nmos <= 0)
2466                         return -3;  /*  Bad or inconsistent number of MOs  */
2467                 bset->mo = (Double *)calloc(sizeof(Double), bset->nmos * bset->ncomps);
2468                 bset->moenergies = (Double *)calloc(sizeof(Double), bset->nmos);
2469                 if (bset->mo == NULL || bset->moenergies == NULL) {
2470                         if (bset->mo != NULL)
2471                                 free(bset->mo);
2472                         if (bset->moenergies != NULL)
2473                                 free(bset->moenergies);
2474                         bset->mo = NULL;
2475                         bset->moenergies = NULL;
2476                         bset->nmos = 0;
2477                         return -2;  /*  Low memory  */
2478                 }
2479         }
2480         if (idx < 0 || idx >= bset->nmos)
2481                 return -4;  /*  Bad MO index  */
2482         if (energy != -1000000)
2483                 bset->moenergies[idx] = energy;
2484         if (ncomps < bset->ncomps)
2485                 return -5;  /*  Insufficient number of data provided  */
2486         memmove(bset->mo + (idx * bset->ncomps), coeffs, sizeof(Double) * bset->ncomps);
2487         if (bset->cns != NULL) {
2488                 /*  Clear the cached values  */
2489                 free(bset->cns);
2490                 bset->cns = NULL;
2491                 bset->ncns = 0;
2492         }
2493         return 0;
2494 }
2495
2496 /*  Allocate BasisSet record. rflag: UHF, 0; RHF, 1; ROHF, 2
2497     ne_alpha: number of alpha electrons, ne_beta: number of beta electrons
2498     The natoms and pos are copied from mol.  */
2499 int
2500 MoleculeAllocateBasisSetRecord(Molecule *mol, Int rflag, Int ne_alpha, Int ne_beta)
2501 {
2502         BasisSet *bset;
2503         int i;
2504         Atom *ap;
2505         if (mol == NULL || mol->natoms == 0)
2506                 return -1;  /*  Molecule is empty  */
2507         bset = mol->bset;
2508         if (bset == NULL) {
2509                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2510                 if (bset == NULL)
2511                         return -2;  /*  Low memory  */
2512         }
2513         if (bset->pos != NULL) {
2514                 free(bset->pos);
2515                 bset->pos = NULL;
2516         }
2517         bset->natoms = mol->natoms;
2518         bset->pos = (Vector *)calloc(sizeof(Vector), bset->natoms);
2519         if (bset->pos == NULL)
2520                 return -2;  /*  Low memory  */
2521         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
2522                 bset->pos[i].x = ap->r.x * kAngstrom2Bohr;
2523                 bset->pos[i].y = ap->r.y * kAngstrom2Bohr;
2524                 bset->pos[i].z = ap->r.z * kAngstrom2Bohr;
2525         }
2526         bset->ne_alpha = ne_alpha;
2527         bset->ne_beta = ne_beta;
2528         bset->rflag = rflag;
2529         return 0;
2530 }
2531
2532 static void
2533 sSeparateTokens(char *inString, char **outPtr, int size)
2534 {
2535         char *p;
2536         int i;
2537         for (i = 0; i < size; i++) {
2538                 p = strtok((i == 0 ? inString : NULL), " \r\n");
2539                 if (p == NULL)
2540                         break;
2541                 outPtr[i] = p;
2542         }
2543         while (i < size) {
2544                 outPtr[i++] = NULL;
2545         }
2546 }
2547
2548 static int
2549 sReadNumberArray(void *basep, Int *countp, Int size, Int num, FILE *fp, int *lnp)
2550 {
2551         char buf[256];
2552         Int i, n;
2553         *((void **)basep) = NULL;
2554         *countp = 0;
2555         if (AssignArray(basep, countp, size, num - 1, NULL) == NULL)
2556                 return 4;  /*  Out of memory  */
2557         n = 0;
2558         while (ReadLine(buf, sizeof buf, fp, lnp) > 0) {
2559                 char *tokens[16], *p;
2560                 sSeparateTokens(buf, tokens, 16);
2561                 for (i = 0; i < 16; i++) {
2562                         if (tokens[i] == NULL)
2563                                 break;
2564                         if (size == sizeof(Int)) {
2565                                 (*((Int **)basep))[n] = strtol(tokens[i], &p, 0);
2566                         } else if (size == sizeof(Double)) {
2567                                 (*((Double **)basep))[n] = strtod(tokens[i], &p);
2568                         } else return -1;  /*  Internal error  */
2569                         if (tokens[i] == p || *p != 0)
2570                                 return 1;  /*  Non-digit character  */
2571                         if (++n == num) {
2572                                 if (i < 15 && tokens[i + 1] != NULL)
2573                                         return 2;  /*  Too many data  */
2574                                 return 0;  /*  All data are successfully read  */
2575                         }
2576                 }
2577         }
2578         return 3;  /*  Unexpected EOF  */                       
2579 }
2580
2581 static int
2582 sSetupGaussianCoefficients(BasisSet *bset)
2583 {
2584         ShellInfo *sp;
2585         PrimInfo *pp;
2586         int i, j, k;
2587         Double *dp, d;
2588         
2589         /*  Cache the contraction coefficients for efficient calculation  */
2590         /*  Sum up the number of components for all primitives  */
2591         for (i = k = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2592                 sp->cn_idx = k;
2593                 k += sp->nprim * sp->ncomp;
2594         }
2595         /*  Allocate memory for the cached values  */
2596         if (AssignArray(&bset->cns, &bset->ncns, sizeof(Double), k - 1, NULL) == NULL)
2597                 return 1;
2598         /*  Iterate over all primitives  */
2599         dp = bset->cns;
2600         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2601                 for (j = 0, pp = bset->priminfos + sp->p_idx; j < sp->nprim; j++, pp++) {
2602                         switch (sp->sym) {
2603                                 case kGTOType_S:
2604                                         // (8 alpha^3/pi^3)^0.25 exp(-alpha r^2)
2605                                         *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2606                                         break;
2607                                 case kGTOType_P:
2608                                         // (128 alpha^5/pi^3)^0.25 [x|y|z]exp(-alpha r^2)
2609                                         d = pp->C * pow(pp->A, 1.25) * 1.425410941;
2610                                         *dp++ = d;
2611                                         *dp++ = d;
2612                                         *dp++ = d;
2613                                         break;
2614                                 case kGTOType_SP:
2615                                         *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2616                                         d = pp->Csp * pow(pp->A, 1.25) * 1.425410941;
2617                                         *dp++ = d;
2618                                         *dp++ = d;
2619                                         *dp++ = d;
2620                                         break;
2621                                 case kGTOType_D:
2622                                         //  xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2)
2623                                         //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2624                                         d = pp->C * pow(pp->A, 1.75);
2625                                         dp[0] = dp[1] = dp[2] = d * 1.645922781;
2626                                         dp[3] = dp[4] = dp[5] = d * 2.850821881;
2627                                         dp += 6;
2628                                         break;
2629                                 case kGTOType_D5:
2630                                         //  3zz-rr:   (128 alpha^7/9pi^3)^0.25 (3zz-rr)exp(-alpha r^2)
2631                                         //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2632                                         //  xx-yy:    (128 alpha^7/pi^3)^0.25 (xx-yy)exp(-alpha r^2)
2633                                         d = pp->C * pow(pp->A, 1.75);
2634                                         dp[0] = d * 0.822961390;
2635                                         dp[1] = dp[2] = dp[4] = d * 2.850821881;
2636                                         dp[3] = d * 1.425410941;
2637                                         dp += 5;
2638                                         break;
2639                                 /*  TODO: Support F/F7 and G/G9 type orbitals  */
2640                         }
2641                 }
2642         }
2643         return 0;
2644 }
2645
2646 int
2647 MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char **errbuf)
2648 {
2649         FILE *fp;
2650         char buf[1024];
2651         int lineNumber;
2652         int natoms, nbasis, i, j, k, n, mxbond, retval, ncomps, nprims, nelec;
2653         BasisSet *bset;
2654         ShellInfo *sp;
2655         PrimInfo *pp;
2656         Int nary;
2657         Int *iary;
2658         Double *dary;
2659         Atom *ap;
2660         Vector *vp;
2661         Double w;
2662
2663         *errbuf = NULL;
2664         if (mp == NULL)
2665                 mp = MoleculeNew();
2666         bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2667         if (bset == NULL)
2668                 goto panic;
2669         mp->bset = bset;
2670         fp = fopen(fname, "rb");
2671         if (fp == NULL) {
2672                 s_append_asprintf(errbuf, "Cannot open file");
2673                 return 1;
2674         }
2675         lineNumber = 0;
2676         natoms = nbasis = -1;
2677         mxbond = 0;
2678         ncomps = 0;
2679         nelec = 0;
2680         nprims = 0;
2681         nary = 0;
2682         iary = NULL;
2683         dary = NULL;
2684         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2685                 char *tokens[16];
2686                 char *p = buf + 41;
2687                 if (lineNumber == 2) {
2688                         /*  job info line  */
2689                         if (buf[10] == 'U')
2690                                 bset->rflag = 0;  /*  UHF  */
2691                         else if (buf[11] == 'O')
2692                                 bset->rflag = 2;  /*  ROHF  */
2693                         else bset->rflag = 1; /*  RHF  */
2694                         continue;
2695                 }
2696                 while (p > buf && *p == ' ')
2697                         p--;
2698                 p[1] = 0;
2699                 sSeparateTokens(buf + 42, tokens, 16);
2700                 if (strcmp(buf, "Number of atoms") == 0) {
2701                         if (tokens[1] == NULL || (natoms = atoi(tokens[1])) <= 0) {
2702                                 s_append_asprintf(errbuf, "Line %d: strange number of atoms: %s", lineNumber, tokens[1]);
2703                                 retval = 2;
2704                                 goto cleanup;
2705                         }
2706                         /*  Allocate atom records (all are empty for now)  */
2707                         AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms - 1, NULL);
2708                         /*  Also allocate atom position array for MO calculations  */
2709                         AssignArray(&bset->pos, &bset->natoms, sizeof(Vector), natoms - 1, NULL);
2710                         /*  Also allocate nuclear charge array  */
2711                         bset->nuccharges = (Double *)calloc(sizeof(Double), natoms);
2712                 } else if (strcmp(buf, "Number of electrons") == 0) {
2713                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2714                                 s_append_asprintf(errbuf, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
2715                                 retval = 2;
2716                                 goto cleanup;
2717                         }
2718                         nelec = i;
2719                 } else if (strcmp(buf, "Number of alpha electrons") == 0) {
2720                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2721                                 s_append_asprintf(errbuf, "Line %d: strange number of alpha electrons: %s", lineNumber, tokens[1]);
2722                                 retval = 2;
2723                                 goto cleanup;
2724                         }
2725                         bset->ne_alpha = i;
2726                 } else if (strcmp(buf, "Number of beta electrons") == 0) {
2727                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2728                                 s_append_asprintf(errbuf, "Line %d: strange number of beta electrons: %s", lineNumber, tokens[1]);
2729                                 retval = 2;
2730                                 goto cleanup;
2731                         }
2732                         bset->ne_beta = i;
2733                         if (bset->ne_alpha + bset->ne_beta != nelec) {
2734                                 s_append_asprintf(errbuf, "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);
2735                                 retval = 2;
2736                                 goto cleanup;
2737                         }
2738                 } else if (strcmp(buf, "Number of basis functions") == 0) {
2739                         if (tokens[1] == NULL || (nbasis = atoi(tokens[1])) <= 0) {
2740                                 s_append_asprintf(errbuf, "Line %d: strange number of basis functions: %s", lineNumber, tokens[1]);
2741                                 retval = 2;
2742                                 goto cleanup;
2743                         }
2744                 } else if (strcmp(buf, "Atomic numbers") == 0) {
2745                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2746                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2747                                 retval = 2;
2748                                 goto cleanup;
2749                         }
2750                         if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms, fp, &lineNumber) != 0) {
2751                                 s_append_asprintf(errbuf, "Line %d: cannot read atomic numbers", lineNumber);
2752                                 retval = 2;
2753                                 goto cleanup;
2754                         }
2755                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2756                                 ap->atomicNumber = iary[i];
2757                                 bset->nuccharges[i] = iary[i];
2758                                 ElementToString(ap->atomicNumber, ap->element);
2759                                 memmove(ap->aname, ap->element, 4);
2760                                 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
2761                                         ap->weight = w;
2762                         }
2763                         free(iary);
2764                         iary = NULL;
2765                 } else if (strcmp(buf, "Nuclear charges") == 0) {
2766                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2767                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2768                                 retval = 2;
2769                                 goto cleanup;
2770                         }
2771                         if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms, fp, &lineNumber) != 0) {
2772                                 s_append_asprintf(errbuf, "Line %d: cannot read nuclear charges", lineNumber);
2773                                 retval = 2;
2774                                 goto cleanup;
2775                         }
2776                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2777                                 bset->nuccharges[i] = dary[i];
2778                         }
2779                         free(iary);
2780                         iary = NULL;
2781                 } else if (strcmp(buf, "Current cartesian coordinates") == 0) {
2782                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * 3) {
2783                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of cartesian coordinates: %s", lineNumber, tokens[2]);
2784                                 retval = 2;
2785                                 goto cleanup;
2786                         }
2787                         if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms * 3, fp, &lineNumber) != 0) {
2788                                 s_append_asprintf(errbuf, "Line %d: cannot read cartesian coordinates", lineNumber);
2789                                 retval = 2;
2790                                 goto cleanup;
2791                         }
2792                         for (i = 0, ap = mp->atoms, vp = bset->pos; i < natoms; i++, ap = ATOM_NEXT(ap), vp++) {
2793                                 vp->x = dary[i * 3];
2794                                 vp->y = dary[i * 3 + 1];
2795                                 vp->z = dary[i * 3 + 2];
2796                                 ap->r.x = vp->x * kBohr2Angstrom;
2797                                 ap->r.y = vp->y * kBohr2Angstrom;
2798                                 ap->r.z = vp->z * kBohr2Angstrom;
2799                         }
2800                         free(dary);
2801                         dary = NULL;
2802                 } else if (strcmp(buf, "MxBond") == 0) {
2803                         if (tokens[1] == NULL || (mxbond = atoi(tokens[1])) <= 0) {
2804                                 s_append_asprintf(errbuf, "Line %d: strange number of bonds per atom: %s", lineNumber, tokens[1]);
2805                                 retval = 2;
2806                                 goto cleanup;
2807                         }
2808                 } else if (strcmp(buf, "IBond") == 0) {
2809                         Int *bonds;
2810                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * mxbond) {
2811                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of bonds: %s", lineNumber, tokens[2]);
2812                                 retval = 2;
2813                                 goto cleanup;
2814                         }
2815                         if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms * mxbond, fp, &lineNumber) != 0) {
2816                                 s_append_asprintf(errbuf, "Line %d: cannot read bond information", lineNumber);
2817                                 retval = 2;
2818                                 goto cleanup;
2819                         }
2820                         bonds = (Int *)malloc(sizeof(Int) * (mxbond * 2 + 1));
2821                         for (i = 0; i < natoms; i++) {
2822                                 for (j = k = 0; j < mxbond; j++) {
2823                                         n = iary[i * mxbond + j] - 1;
2824                                         if (n > i) {
2825                                                 /*  Connect atom i and atom n  */
2826                                                 bonds[k++] = i;
2827                                                 bonds[k++] = n;
2828                                         }
2829                                 }
2830                                 if (k > 0) {
2831                                         bonds[k] = kInvalidIndex;
2832                                         MoleculeAddBonds(mp, k / 2, bonds, NULL, 1);
2833                                 }
2834                         }
2835                         free(iary);
2836                         free(bonds);
2837                         iary = NULL;
2838                 } else if (strcmp(buf, "Shell types") == 0) {
2839                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0) {
2840                                 s_append_asprintf(errbuf, "Line %d: wrong number of shell types: %s", lineNumber, tokens[2]);
2841                                 retval = 2;
2842                                 goto cleanup;
2843                         }
2844                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2845                                 s_append_asprintf(errbuf, "Line %d: cannot read shell types", lineNumber);
2846                                 retval = 2;
2847                                 goto cleanup;
2848                         }
2849                         /*  Allocate ShellInfo table and store shell type information  */
2850                         AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), nary - 1, NULL);
2851                         for (i = n = 0, sp = bset->shells; i < nary; i++, sp++) {
2852                                 switch (iary[i]) {
2853                                         case 0:  sp->sym = kGTOType_S;  sp->ncomp = 1; break;
2854                                         case 1:  sp->sym = kGTOType_P;  sp->ncomp = 3; break;
2855                                         case -1: sp->sym = kGTOType_SP; sp->ncomp = 4; break;
2856                                         case 2:  sp->sym = kGTOType_D;  sp->ncomp = 6; break;
2857                                         case -2: sp->sym = kGTOType_D5; sp->ncomp = 5; break;
2858                                         case 3:  sp->sym = kGTOType_F;  sp->ncomp = 10; break;
2859                                         case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break;
2860                                         case 4:  sp->sym = kGTOType_G;  sp->ncomp = 15; break;
2861                                         case -4: sp->sym = kGTOType_G9; sp->ncomp = 9; break;
2862                                         default:
2863                                                 s_append_asprintf(errbuf, "Line %d: unsupported shell type %d", lineNumber, iary[i]);
2864                                                 retval = 2;
2865                                                 goto cleanup;
2866                                 }
2867                                 sp->m_idx = n;
2868                                 n += sp->ncomp;
2869                         }
2870                         bset->ncomps = ncomps = n;
2871                         free(iary);
2872                         iary = NULL;
2873                 } else if (strcmp(buf, "Number of primitives per shell") == 0) {
2874                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2875                                 s_append_asprintf(errbuf, "Line %d: wrong size of the primitive table: %s", lineNumber, tokens[2]);
2876                                 retval = 2;
2877                                 goto cleanup;
2878                         }
2879                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2880                                 s_append_asprintf(errbuf, "Line %d: cannot read primitive table", lineNumber);
2881                                 retval = 2;
2882                                 goto cleanup;
2883                         }
2884                         for (i = n = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2885                                 sp->nprim = iary[i];
2886                                 sp->p_idx = n;
2887                                 n += sp->nprim;
2888                         }
2889                         nprims = n;
2890                         free(iary);
2891                         iary = NULL;
2892                 } else if (strcmp(buf, "Shell to atom map") == 0) {
2893                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2894                                 s_append_asprintf(errbuf, "Line %d: wrong size of the shell-to-atom map: %s", lineNumber, tokens[2]);
2895                                 retval = 2;
2896                                 goto cleanup;
2897                         }
2898                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2899                                 s_append_asprintf(errbuf, "Line %d: cannot read shell-to-atom table", lineNumber);
2900                                 retval = 2;
2901                                 goto cleanup;
2902                         }
2903                         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2904                                 sp->a_idx = iary[i] - 1;
2905                         }
2906                         free(iary);
2907                         iary = NULL;
2908                 } else if (strcmp(buf, "Primitive exponents") == 0) {
2909                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nprims) {
2910                                 s_append_asprintf(errbuf, "Line %d: wrong number of primitive exponents: %s", lineNumber, tokens[2]);
2911                                 retval = 2;
2912                                 goto cleanup;
2913                         }
2914                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2915                                 s_append_asprintf(errbuf, "Line %d: cannot read primitive exponents", lineNumber);
2916                                 retval = 2;
2917                                 goto cleanup;
2918                         }
2919                         /*  Allocate PrimInfo table  */
2920                         AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), nprims - 1, NULL);
2921                         for (i = 0, pp = bset->priminfos; i < nprims; i++, pp++) {
2922                                 pp->A = dary[i];
2923                         }
2924                         free(dary);
2925                         dary = NULL;
2926                 } else if (strcmp(buf, "Contraction coefficients") == 0) {
2927                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2928                                 s_append_asprintf(errbuf, "Line %d: wrong number of contraction coefficients: %s", lineNumber, tokens[2]);
2929                                 retval = 2;
2930                                 goto cleanup;
2931                         }
2932                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2933                                 s_append_asprintf(errbuf, "Line %d: cannot read contraction coefficients", lineNumber);
2934                                 retval = 2;
2935                                 goto cleanup;
2936                         }
2937                         for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2938                                 pp->C = dary[i];
2939                         }
2940                         free(dary);
2941                         dary = NULL;
2942                 } else if (strcmp(buf, "P(S=P) Contraction coefficients") == 0) {
2943                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2944                                 s_append_asprintf(errbuf, "Line %d: wrong number of P(S=P) contraction coefficients: %s", lineNumber, tokens[2]);
2945                                 retval = 2;
2946                                 goto cleanup;
2947                         }
2948                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2949                                 s_append_asprintf(errbuf, "Line %d: cannot read P(S=P) contraction coefficients", lineNumber);
2950                                 retval = 2;
2951                                 goto cleanup;
2952                         }
2953                         for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2954                                 pp->Csp = dary[i];
2955                         }
2956                         free(dary);
2957                         dary = NULL;
2958                 } else if (strcmp(buf, "Alpha Orbital Energies") == 0) {
2959                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2960                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha orbitals: %s", lineNumber, tokens[2]);
2961                                 retval = 2;
2962                                 goto cleanup;
2963                         }
2964                         if (sReadNumberArray(&bset->moenergies, &bset->nmos, sizeof(Double), i, fp, &lineNumber) != 0) {
2965                                 s_append_asprintf(errbuf, "Line %d: cannot read alpha orbital energies", lineNumber);
2966                                 retval = 2;
2967                                 goto cleanup;
2968                         }
2969                 } else if (strcmp(buf, "Alpha MO coefficients") == 0) {
2970                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2971                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha MO coefficients: %s", lineNumber, tokens[2]);
2972                                 retval = 2;
2973                                 goto cleanup;
2974                         }
2975                         if (sReadNumberArray(&bset->mo, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2976                                 s_append_asprintf(errbuf, "Line %d: cannot read MO coefficients", lineNumber);
2977                                 retval = 2;
2978                                 goto cleanup;
2979                         }
2980                 } else if (strcmp(buf, "Beta Orbital Energies") == 0) {
2981                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2982                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta orbitals: %s", lineNumber, tokens[2]);
2983                                 retval = 2;
2984                                 goto cleanup;
2985                         }
2986                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2987                                 s_append_asprintf(errbuf, "Line %d: cannot read beta orbital energies", lineNumber);
2988                                 retval = 2;
2989                                 goto cleanup;
2990                         }
2991                         bset->moenergies = (Double *)realloc(bset->moenergies, sizeof(Double) * 2 * ncomps);
2992                         bset->nmos = ncomps * 2;
2993                         bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);
2994                         memmove(bset->moenergies + ncomps, dary, sizeof(Double) * ncomps);
2995                         memset(bset->mo + ncomps * ncomps, 0, sizeof(Double) * ncomps * ncomps);
2996                         free(dary);
2997                         dary = NULL;
2998                 } else if (strcmp(buf, "Beta MO coefficients") == 0) {
2999                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
3000                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta MO coefficients: %s", lineNumber, tokens[2]);
3001                                 retval = 2;
3002                                 goto cleanup;
3003                         }
3004                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3005                                 s_append_asprintf(errbuf, "Line %d: cannot read alpha MO coefficients", lineNumber);
3006                                 retval = 2;
3007                                 goto cleanup;
3008                         }
3009                         bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);  /*  Should be unnecessary, just in case  */
3010                         memmove(bset->mo + ncomps * ncomps, dary, sizeof(Double) * ncomps * ncomps);
3011                         free(dary);
3012                         dary = NULL;
3013                 } else if (strcmp(buf, "Total SCF Density") == 0) {
3014                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * (ncomps + 1) / 2) {
3015                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of SCF densities: %s", lineNumber, tokens[2]);
3016                                 retval = 2;
3017                                 goto cleanup;
3018                         }
3019                         if (sReadNumberArray(&bset->scfdensities, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3020                                 s_append_asprintf(errbuf, "Line %d: cannot read SCF densities", lineNumber);
3021                                 retval = 2;
3022                                 goto cleanup;
3023                         }
3024                 }
3025         }
3026         if (mp->natoms == 0) {
3027                 s_append_asprintf(errbuf, "Atom information is missing");
3028                 retval = 2;
3029                 goto cleanup;
3030         }
3031         if (bset->shells == NULL || bset->priminfos == NULL) {
3032                 s_append_asprintf(errbuf, "Gaussian primitive information is missing");
3033                 retval = 2;
3034                 goto cleanup;
3035         }
3036         if (bset->mo == NULL) {
3037                 s_append_asprintf(errbuf, "MO coefficients were not found");
3038                 retval = 2;
3039                 goto cleanup;
3040         }
3041         if (sSetupGaussianCoefficients(bset) != 0) {
3042                 s_append_asprintf(errbuf, "Internal error during setup MO calculation");
3043                 retval = 2;
3044                 goto cleanup;
3045         }
3046         mp->nframes = -1;
3047         retval = 0;
3048 cleanup:
3049         fclose(fp);
3050         if (iary != NULL)
3051                 free(iary);
3052         if (dary != NULL)
3053                 free(dary);
3054         if (retval != 0) {
3055                 if (mp->bset != NULL) {
3056                         BasisSetRelease(mp->bset);
3057                         mp->bset = NULL;
3058                 }
3059         }
3060         return retval;
3061 panic:
3062         Panic("low memory while reading fchk file %s", fname);
3063         return -1; /* not reached */    
3064 }
3065
3066 int
3067 MoleculeLoadGamessDatFile(Molecule *mol, const char *fname, char **errbuf)
3068 {
3069         FILE *fp;
3070         int newmol = 0;
3071         char buf[1024];
3072         int lineNumber, i, j, k, len, natoms = 0;
3073         int nframes = 0;
3074         int n1;
3075         int retval = 0;
3076         int ival[8];
3077         double dval[8];
3078         char sval[16];
3079         Vector *vbuf = NULL;
3080         IntGroup *ig;
3081         int optimizing = 0, status = 0;
3082         
3083         *errbuf = NULL;
3084         if (mol == NULL) {
3085                 mol = MoleculeNew();
3086         }
3087         if (mol->natoms == 0)
3088                 newmol = 1;
3089
3090         fp = fopen(fname, "rb");
3091         if (fp == NULL) {
3092                 s_append_asprintf(errbuf, "Cannot open file");
3093                 return 1;
3094         }
3095         
3096         /*  ESP is cleared (not undoable!)  */
3097         if (mol->elpots != NULL) {
3098                 free(mol->elpots);
3099                 mol->elpots = NULL;
3100                 mol->nelpots = 0;
3101         }
3102         
3103         lineNumber = 0;
3104         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3105         redo:
3106                 n1 = 0;
3107                 if (strncmp(buf, " $DATA", 6) == 0) {
3108                         /*  Initial geometry  */
3109                         if (!newmol) {
3110                                 vbuf = (Vector *)calloc(sizeof(Vector), mol->natoms);
3111                         }
3112                         i = 0;
3113                         ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Title  */
3114                         ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Symmetry  */
3115                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3116                                 if (strncmp(buf, " $END", 5) == 0)
3117                                         break;
3118                                 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3119                                         s_append_asprintf(errbuf, "Line %d: bad format in $DATA section", lineNumber);
3120                                         retval = 2;
3121                                         goto exit_loop;
3122                                 }
3123                                 if (newmol) {
3124                                         Atom a;
3125                                         memset(&a, 0, sizeof(a));
3126                                         strncpy(a.aname, sval, 4);
3127                                         a.r.x = dval[1];
3128                                         a.r.y = dval[2];
3129                                         a.r.z = dval[3];
3130                                         a.atomicNumber = (Int)dval[0];
3131                                         strncpy(a.element, ElementToString(a.atomicNumber, sval), 3);
3132                                         a.type = AtomTypeEncodeToUInt(a.element);
3133                                         a.weight = WeightForAtomicNumber(a.atomicNumber);
3134                                         MoleculeCreateAnAtom(mol, &a, mol->natoms);
3135                                 } else {
3136                                         Atom *ap;
3137                                         if (i >= mol->natoms) {
3138                                                 s_append_asprintf(errbuf, "Line %d: too many atoms", lineNumber);
3139                                                 retval = 3;
3140                                                 goto exit_loop;
3141                                         }
3142                                         if ((ap = ATOM_AT_INDEX(mol->atoms, i))->atomicNumber != dval[0]) {
3143                                                 s_append_asprintf(errbuf, "Line %d: atomic number does not match", lineNumber);
3144                                                 retval = 4;
3145                                                 goto exit_loop;
3146                                         }
3147                                         vbuf[i].x = dval[1];
3148                                         vbuf[i].y = dval[2];
3149                                         vbuf[i].z = dval[3];
3150                                 }
3151                                 /*  Skip until a blank line is found  */
3152                                 /*  2013.6.11. Line including "PM3" is also recognized as the end of atom  */
3153                                 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3154                                         for (j = 0; buf[j] == ' '; j++);
3155                                         if (buf[j] == '\n' || strncmp(buf + j, "PM3", 3) == 0)
3156                                                 break;
3157                                 }
3158                                 i++;
3159                         }
3160                         natoms = i;
3161                         if (!newmol) {
3162                                 /*  Set atom positions  */
3163                                 IntGroup *ig;
3164                                 if (natoms < mol->natoms) {
3165                                         s_append_asprintf(errbuf, "Line %d: too few atoms", lineNumber);
3166                                         retval = 5;
3167                                         goto exit_loop;
3168                                 }
3169                                 ig = IntGroupNewWithPoints(0, natoms, -1);
3170                                 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, natoms, vbuf);
3171                                 IntGroupRelease(ig);
3172                         }
3173                         if (vbuf == NULL)
3174                                 vbuf = (Vector *)calloc(sizeof(Vector), natoms);
3175                         nframes = MoleculeGetNumberOfFrames(mol);
3176                         if (status < 0)
3177                                 break;
3178                         continue;
3179                 } else if (strstr(buf, "DATA FROM NSERCH") != NULL || (strstr(buf, "RESULTS FROM SUCCESSFUL") != NULL && (n1 = 1))) {
3180                         /*  Skip until the separator line is read (three or four lines)  */
3181                         i = 0;
3182                         do {
3183                                 if (i++ >= 4) {
3184                                         s_append_asprintf(errbuf, "Line %d: the separator line at the top of the coordinates is not found: bad format?", lineNumber);
3185                                         retval = 6;
3186                                         goto exit_loop;
3187                                 }
3188                                 ReadLine(buf, sizeof buf, fp, &lineNumber);
3189                         } while (strstr(buf, "----------------------------") == NULL);
3190                         for (i = 0; i < natoms; i++) {
3191                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3192                                         s_append_asprintf(errbuf, "Unexpected end of file in reading NSERCH data");
3193                                         retval = 6;
3194                                         goto exit_loop;
3195                                 }
3196                                 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3197                                         s_append_asprintf(errbuf, "Line %d: bad format in NSERCH coordinate data", lineNumber);
3198                                         retval = 6;
3199                                         goto exit_loop;
3200                                 }
3201                                 vbuf[i].x = dval[1];
3202                                 vbuf[i].y = dval[2];
3203                                 vbuf[i].z = dval[3];
3204                         }
3205                         ig = IntGroupNewWithPoints(nframes, 1, -1);
3206                         MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, natoms, vbuf, 0, NULL);
3207                         IntGroupRelease(ig);
3208                         nframes++;
3209                         if (n1 == 0)
3210                                 optimizing = 1;  /*  Flag to skip reading the VEC group  */
3211                         else
3212                                 optimizing = 0;
3213                         continue;
3214                 } else if (strstr(buf, "E(UHF)") != NULL || (strstr(buf, "E(RHF)") != NULL && (n1 = 1)) || (strstr(buf, "E(ROHF)") != NULL && (n1 = 2))) {
3215                         if (mol->bset == NULL) {
3216                                 i = MoleculeAllocateBasisSetRecord(mol, n1, 0, 0);
3217                                 if (i != 0) {
3218                                         s_append_asprintf(errbuf, "Line %d: cannot allocate basis set internal buffer", lineNumber);
3219                                         retval = 8;
3220                                         goto exit_loop;
3221                                 }
3222                         }
3223                 } else if (strncmp(buf, " $VEC", 5) == 0) {
3224                         Double *coeffs;
3225                         /*  Read the vec group  */
3226                         if (mol->bset == NULL || mol->bset->ncomps == 0)
3227                                 continue;  /*  Just ignore  */
3228                         if (optimizing)
3229                                 continue;  /*  Ignore VEC group during optimization  */
3230                         coeffs = (Double *)calloc(sizeof(Double), mol->bset->ncomps);
3231                         if (coeffs == NULL) {
3232                                 s_append_asprintf(errbuf, "Line %d: low memory during $VEC", lineNumber);
3233                                 retval = 9;
3234                                 goto exit_loop;
3235                         }
3236                         i = k = 0;
3237                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3238                                 len = strlen(buf);
3239                                 if (strncmp(buf, " $END", 5) == 0)
3240                                         break;
3241                                 while ((j = 5 + (k % 5) * 15) <= len && buf[j] != 0 && buf[j] != '\n') {
3242                                         strncpy(sval, buf + j, 15);
3243                                         sval[15] = 0;
3244                                         coeffs[k] = strtod(sval, NULL);
3245                                         k++;
3246                                         if ((k % 5) == 0)
3247                                                 break;
3248                                 }
3249                                 if (k < mol->bset->ncomps)
3250                                         continue;
3251                                 j = MoleculeSetMOCoefficients(mol, i, -1000000, k, coeffs);
3252                                 if (j != 0) {
3253                                         s_append_asprintf(errbuf, "Line %d: cannot set coefficients for MO %d", lineNumber, i + 1);
3254                                         free(coeffs);
3255                                         retval = 10;
3256                                         goto exit_loop;
3257                                 }
3258                                 i++;
3259                                 k = 0;
3260                         }
3261                         if (status < 0)
3262                                 break;
3263                         continue;
3264                 } else if ((strstr(buf, "ELECTRIC POTENTIAL") != NULL || strstr(buf, "ELECTROSTATIC POTENTIAL") != NULL) && strstr(buf, "ELPOTT") != NULL) {
3265                         i = 0;
3266                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3267                                 Elpot *ep;
3268                                 if (strstr(buf, "TOTAL NUMBER OF GRID POINTS") != NULL)
3269                                         continue;
3270                                 if (sscanf(buf, "%d %lf %lf %lf %lf", &ival[0], &dval[0], &dval[1], &dval[2], &dval[3]) < 5)
3271                                         break;
3272                                 ep = AssignArray(&mol->elpots, &mol->nelpots, sizeof(Elpot), i, NULL);
3273                                 ep->pos.x = dval[0];
3274                                 ep->pos.y = dval[1];
3275                                 ep->pos.z = dval[2];
3276                                 ep->esp = dval[3];
3277                                 i++;
3278                         }
3279                         if (status > 0)
3280                                 goto redo;  /*  This section has no end line, so the last line should be processed again  */
3281                         else break;    /*  End of file encountered or interrupted */
3282                 }  /*  TODO: read MOLPLT info if present  */
3283         }
3284         if (status < 0) {
3285                 s_append_asprintf(errbuf, "User interrupt at line %d", lineNumber);
3286                 retval = 11;
3287         }
3288 exit_loop:
3289         if (vbuf != NULL)
3290                 free(vbuf);
3291         if (mol->natoms > 0)
3292                 retval = 0;  /*  Return the partially constructed molecule  */
3293         if (newmol && mol->nbonds == 0) {
3294                 /*  Guess bonds  */
3295                 Int nbonds, *bonds;
3296                 MoleculeGuessBonds(mol, 1.2, &nbonds, &bonds);
3297                 if (nbonds > 0) {
3298                         MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
3299                         free(bonds);
3300                 }
3301         }
3302         return 0;
3303 }
3304
3305 int
3306 MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
3307 {
3308         int retval;
3309         if (ftype == NULL || *ftype == 0) {
3310                 const char *cp;
3311                 cp = strrchr(fname, '.');
3312                 if (cp != NULL)
3313                         ftype = cp + 1;
3314                 else {
3315                         cp = guessMoleculeType(fname);
3316                         if (strcmp(cp, "???") != 0)
3317                                 ftype = cp;
3318                 }
3319         }
3320         if (strcasecmp(ftype, "pdb") == 0) {
3321                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3322         }
3323         if (retval != 0) {
3324                 /*  Try all formats once again  */
3325                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3326         }
3327         return retval;
3328 }
3329
3330 int
3331 MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char **errbuf)
3332 {
3333         FILE *fp;
3334         char buf[1024];
3335         char *p;
3336         int lineNumber;
3337         int i, j, new_unit, retval;
3338         Atom *ap;
3339         IntGroup *ig;
3340         Vector *vp = NULL;
3341         Int ibuf[12];
3342         Int entries = 0;
3343         retval = 0;
3344         *errbuf = NULL;
3345         fp = fopen(fname, "rb");
3346         if (fp == NULL) {
3347                 s_append_asprintf(errbuf, "Cannot open file");
3348                 return -1;
3349         }
3350 /*      flockfile(fp); */
3351         if (mp->natoms == 0)
3352                 new_unit = 1;
3353         else {
3354                 /*  Allocate buffer for undo-capable modification  */
3355                 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
3356                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3357                         /*  Retain current position if the atom info is missing in the input file  */
3358                         vp[i] = ap->r;
3359                 }
3360                 new_unit = 0;
3361         }
3362         lineNumber = 0;
3363         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3364                 if (strncmp(buf, "END", 3) == 0)
3365                         break;
3366                 if (strncmp(buf, "HETATM", 6) == 0 || strncmp(buf, "ATOM", 4) == 0) {
3367                         struct {
3368                                 Int serial, intCharge, resSeq;
3369                                 Vector r;
3370                                 Double occ, temp;
3371                                 char segName[5], resName[4], atomName[5], resSeqStr[5], atomType[3], element[3], occStr[6];
3372                         } w;
3373                         memset(&w, 0, sizeof(w));
3374                         ReadFormat(buf, "x6 I5 x1 S4 x1 S3 x1 x1 S4 x1 x3 F8 F8 F8 S6 F6 x6 S4 S2 I2",
3375                                 &w.serial, w.atomName, w.resName, w.resSeqStr, &w.r.x, &w.r.y, &w.r.z,
3376                                 w.occStr, &w.temp, w.segName, w.element, &w.intCharge);
3377                         if (w.atomName[0] == 0) {
3378                                 continue;  /*  Atom name is empty  */
3379                         }
3380                         /*  A workaround for residue number >= 10000 (XPLOR style)  */
3381                         if (w.resSeqStr[0] >= 'A' && w.resSeqStr[0] <= 'Z') {
3382                                 w.resSeq = (w.resSeqStr[0] - 'A' + 10) * 1000 + atoi(w.resSeqStr + 1);
3383                         } else {
3384                                 w.resSeq = atoi(w.resSeqStr);
3385                         }
3386                         if (w.element[0] == 0) {
3387                                 /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
3388                                 for (p = w.atomName; *p != 0; p++) {
3389                                         if (isalpha(*p) && *p != '_') {
3390                                                 w.element[0] = toupper(*p);
3391                                                 if (isalpha(p[1]) && p[1] != '_') {
3392                                                         w.element[1] = toupper(p[1]);
3393                                                         w.element[2] = 0;
3394                                                 } else {
3395                                                         w.element[1] = 0;
3396                                                 }
3397                                                 break;
3398                                         }
3399                                 }
3400                         }
3401                         if (w.occStr[0] == 0)
3402                                 w.occ = 1.0;
3403                         else
3404                                 w.occ = atof(w.occStr);
3405                         if (w.serial <= 0) {
3406                                 s_append_asprintf(errbuf, "line %d: non-positive atom number %d", lineNumber, w.serial);
3407                                 retval = 1;
3408                                 goto abort;
3409                         }
3410                         w.serial--;  /*  The internal atom number is 0-based  */
3411                         if (w.serial >= mp->natoms) {
3412                                 if (new_unit) {
3413                                         /*  Create a new atom entry  */
3414                                         ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, w.serial, NULL);
3415                                 } else {
3416                                         s_append_asprintf(errbuf, "line %d: the atom number %d does not exist in the structure file", lineNumber, w.serial+1);
3417                                         retval = 1;
3418                                         goto abort;
3419                                 }
3420                         }
3421                         if (new_unit) {
3422                                 ap = ATOM_AT_INDEX(mp->atoms, w.serial);
3423                                 ap->r = w.r;
3424                                 ap->occupancy = w.occ;
3425                                 ap->tempFactor = w.temp;
3426                                 if (w.segName[0] == 0)
3427                                         strncpy(w.segName, "MAIN", 4);
3428                                 strncpy(ap->segName, w.segName, 4);
3429                                 ap->resSeq = w.resSeq;
3430                                 strncpy(ap->resName, w.resName, 4);
3431                                 strncpy(ap->aname, w.atomName, 4);
3432                                 strncpy(ap->element, w.element, 2);
3433                                 ap->element[2] = 0;
3434                                 ap->atomicNumber = ElementToInt(ap->element);
3435                                 ap->type = AtomTypeEncodeToUInt(ap->element);
3436                                 ap->weight = WeightForAtomicNumber(ap->atomicNumber);
3437                                 ap->intCharge = w.intCharge;
3438                                 if (ap->resSeq > 0) {
3439                                         if (ap->resSeq < mp->nresidues) {
3440                                                 /*  Update the resName according to residues[]  */
3441                                                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
3442                                         } else {
3443                                                 /*  Register the resName to residues[]  */
3444                                                 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, w.resName);
3445                                         }
3446                                 } else {
3447                                         ap->resSeq = 0;
3448                                         strcpy(ap->resName, "XXX");
3449                                         if (mp->nresidues == 0)
3450                                                 AssignArray(&mp->residues, &mp->nresidues, 4, 0, ap->resName);
3451                                 }
3452                                 i = ElementToInt(ap->element);
3453                                 if (i >= 0)
3454                                         ap->weight = gElementParameters[i].weight;
3455                         } else {
3456                                 /*  Not a new unit: only the atom position is updated  */
3457                                 vp[w.serial] = w.r;
3458                         }
3459                         entries++;
3460                 } else if (strncmp(buf, "CONECT", 6) == 0 && new_unit) {
3461                         i = ReadFormat(buf, "x6 I5I5I5I5I5I5I5I5I5I5I5I5",
3462                                 ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
3463                                 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7,
3464                                 ibuf + 8, ibuf + 9, ibuf + 10, ibuf + 11);
3465                         if (i >= 2) {
3466                                 Int bbuf[25];
3467                                 int bi;
3468                                 for (j = 0; j < i; j++) {
3469                                         if (ibuf[j] < 0 || ibuf[j] > mp->natoms) {
3470                                                 s_append_asprintf(errbuf, "line %d: The CONECT record contains non-existent atom %d", lineNumber, ibuf[j]);
3471                                                 retval = 1;
3472                                                 goto abort;
3473                                         } else if (ibuf[j] == 0)
3474                                                 break;
3475                                 }
3476                                 i = j;
3477                                 if (i < 2)
3478                                         continue;
3479                                 for (j = 1, bi = 0; j < i; j++) {
3480                                         if (ibuf[0] < ibuf[j]) {
3481                                                 if (MoleculeLookupBond(mp, ibuf[0], ibuf[j]) >= 0) {
3482                                                         s_append_asprintf(errbuf, "line %d: warning: duplicate bond %d-%d\n", lineNumber, ibuf[0], ibuf[j]);
3483                                                 } else {
3484                                                         bbuf[bi * 2] = ibuf[0] - 1;
3485                                                         bbuf[bi * 2 + 1] = ibuf[j] - 1;
3486                                                         bi++;
3487                                                 }
3488                                         }
3489                                 }
3490                                 if (bi == 0)
3491                                         continue;
3492                                 bbuf[bi * 2] = -1;
3493                                 retval = MoleculeAddBonds(mp, bi, bbuf, NULL, 1);
3494                                 if (retval < 0) {
3495                                         s_append_asprintf(errbuf, "line %d: bad bond specification", lineNumber);
3496                                         retval = 1;
3497                                         goto abort;
3498                                 }
3499                         }
3500                 }
3501         }
3502 /*      funlockfile(fp); */
3503         fclose(fp);
3504         if (new_unit) {
3505                 /*  Renumber atoms if some atom number is unoccupied  */
3506                 int *old2new, oldidx, newidx;
3507                 old2new = (int *)calloc(sizeof(int), mp->natoms);
3508                 if (old2new == NULL) {
3509                         s_append_asprintf(errbuf, "Out of memory");
3510                         retval = 1;
3511                         goto abort;
3512                 }
3513                 for (oldidx = newidx = 0; oldidx < mp->natoms; oldidx++) {
3514                         ap = ATOM_AT_INDEX(mp->atoms, oldidx);
3515                         if (ap->aname[0] != 0) {
3516                                 old2new[oldidx] = newidx;
3517                                 if (oldidx > newidx)
3518                                         memmove(ATOM_AT_INDEX(mp->atoms, newidx), ap, gSizeOfAtomRecord);
3519                                 newidx++;
3520                         }
3521                 }
3522                 mp->natoms = newidx;
3523                 if (oldidx > newidx) {
3524                         /*  Renumber the connects and bonds  */
3525                         Int *cp;
3526                         for (i = 0; i < mp->natoms; i++) {
3527                                 ap = ATOM_AT_INDEX(mp->atoms, i);
3528                                 cp = AtomConnectData(&ap->connect);
3529                                 for (j = 0; j < ap->connect.count; j++) {
3530                                         cp[j] = old2new[cp[j]];
3531                                 }
3532                         }
3533                         for (i = 0; i < mp->nbonds * 2; i++) {
3534                                 mp->bonds[i] = old2new[mp->bonds[i]];
3535                         }
3536                 }
3537                 retval = MoleculeRebuildTablesFromConnects(mp);
3538                 if (retval != 0) {
3539                         /*  This error may not happen  */
3540                         s_append_asprintf(errbuf, "Cannot build angle/dihedral/improper tables");
3541                         retval = 1;
3542                         goto abort;
3543                 }
3544                 /*  Undo action: delete all atoms  */
3545                 {
3546                         MolAction *act;
3547                         ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3548                         act = MolActionNew(gMolActionUnmergeMolecule, ig);
3549                         act->frame = mp->cframe;
3550                         MolActionCallback_registerUndo(mp, act);
3551                         MolActionRelease(act);
3552                         IntGroupRelease(ig);
3553                 }
3554         } else {
3555                 /*  Set the new atom positions  */
3556                 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3557                 MolActionCreateAndPerform(mp, gMolActionSetAtomPositions, ig, mp->natoms, vp);
3558                 IntGroupRelease(ig);
3559                 free(vp);
3560                 vp = NULL;
3561         }
3562         mp->nframes = -1;  /*  Should be recalculated later  */
3563         if (entries == 0)
3564                 return 1;  /*  No atoms  */
3565         return 0;
3566         abort:
3567         if (fp != NULL) {
3568         /*      funlockfile(fp); */
3569                 fclose(fp);
3570         }
3571         if (vp != NULL)
3572                 free(vp);
3573         if (entries == 0)
3574                 return 1;  /*  Maybe different format?  */
3575         return retval;
3576 }
3577
3578 int
3579 MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char **errbuf)
3580 {
3581         DcdRecord dcd;
3582         SFloat32 *xp, *yp, *zp;
3583         Vector *vp, *cp;
3584         IntGroup *ig;
3585         int n, errcount = 0;
3586         *errbuf = NULL;
3587         if (mp == NULL || mp->natoms == 0) {
3588                 s_append_asprintf(errbuf, "Molecule is empty");
3589                 return 1;
3590         }
3591         n = DcdOpen(fname, &dcd);
3592         if (n != 0) {
3593                 switch (n) {
3594                         case -2: s_append_asprintf(errbuf, "Cannot open file"); break;
3595                         case 1:  s_append_asprintf(errbuf, "Premature EOF encountered"); break;
3596                         case 2:  s_append_asprintf(errbuf, "Bad block length of the first section"); break;
3597                         case 3:  s_append_asprintf(errbuf, "\"CORD\" signature is missing"); break;
3598                         case 4:  s_append_asprintf(errbuf, "Bad termination of the first section"); break;
3599                         case 5:  s_append_asprintf(errbuf, "The title section is not correct"); break;
3600                         case 6:  s_append_asprintf(errbuf, "The atom number section is not correct"); break;
3601                         default: s_append_asprintf(errbuf, "Read error in dcd file"); break;
3602                 }
3603                 errcount++;
3604         } else {
3605                 if (dcd.natoms == 0) {
3606                         s_append_asprintf(errbuf, "No atoms were found in the dcd file");
3607                         errcount++;
3608                 } else if (dcd.nframes == 0) {
3609                         s_append_asprintf(errbuf, "No frames were found in the dcd file");
3610                         errcount++;
3611                 }
3612         }
3613         if (errcount > 0) {
3614                 if (n == 0)
3615                         DcdClose(&dcd);
3616                 return 1;
3617         }
3618
3619         vp = (Vector *)calloc(sizeof(Vector), mp->natoms * dcd.nframes);
3620         if (dcd.nextra)
3621                 cp = (Vector *)calloc(sizeof(Vector), dcd.nframes * 4);
3622         else cp = NULL;
3623         xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3624         yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3625         zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3626         ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mp), dcd.nframes, -1);
3627         if (vp == NULL || xp == NULL || yp == NULL || zp == NULL || ig == NULL) {
3628                 s_append_asprintf(errbuf, "Cannot allocate memory");
3629                 if (vp) free(vp);
3630                 if (cp) free(cp);
3631                 if (xp) free(xp);
3632                 if (yp) free(yp);
3633                 if (zp) free(zp);
3634                 if (ig) IntGroupRelease(ig);
3635                 return 1;
3636         }
3637         for (n = 0; n < dcd.nframes; n++) {
3638                 int i;
3639                 Vector *vpp;
3640                 SFloat32 dcdcell[6];
3641                 if (DcdReadFrame(&dcd, n, xp, yp, zp, dcdcell)) {
3642                         s_append_asprintf(errbuf, "Read error in dcd file");
3643                         goto exit;
3644                 }
3645                 for (i = 0, vpp = &vp[n * mp->natoms]; i < dcd.natoms && i < mp->natoms; i++, vpp++) {
3646                         vpp->x = xp[i];
3647                         vpp->y = yp[i];
3648                         vpp->z = zp[i];
3649                 }
3650                 if (cp != NULL) {
3651                         Double sing;
3652                         vpp = &cp[n * 4];
3653                         /*  dcdcell = {a, gamma, b, beta, alpha, c} */
3654                         /*  angles are described either in cosines (Charmm and NAMD > 2.5) or degrees (NAMD 2.5)  */
3655                         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) {
3656                                 dcdcell[4] = cos(dcdcell[4] * kDeg2Rad);  /*  cos(alpha)  */
3657                                 dcdcell[3] = cos(dcdcell[3] * kDeg2Rad);  /*  cos(beta)  */
3658                                 dcdcell[1] = cos(dcdcell[1] * kDeg2Rad);  /*  cos(gamma)  */
3659                         }
3660                         /*  a axis lies along the cartesian x axis  */
3661                         sing = sqrt(1 - dcdcell[1] * dcdcell[1]);
3662                         vpp[0].x = dcdcell[0];
3663                         vpp[0].y = 0;
3664                         vpp[0].z = 0;
3665                         vpp[1].x = dcdcell[2] * dcdcell[1];
3666                         vpp[1].y = dcdcell[2] * sing;
3667                         vpp[1].z = 0;
3668                         vpp[2].x = dcdcell[5] * dcdcell[3];
3669                         vpp[2].y = dcdcell[5] * (dcdcell[4] - dcdcell[3] * dcdcell[1]) / sing;
3670                         vpp[2].z = sqrt(dcdcell[5] * dcdcell[5] - vpp[2].x * vpp[2].x - vpp[2].y * vpp[2].y);
3671                         vpp[3].x = vpp[3].y = vpp[3].z = 0.0;
3672                         if (mp->cell == NULL) {
3673                                 /*  Create periodicity if not present  */
3674                                 MolActionCreateAndPerform(mp, gMolActionSetBox, &vpp[0], &vpp[1], &vpp[2], &vpp[3], 7, 0);
3675                         }
3676                 }
3677         }
3678         if (MolActionCreateAndPerform(mp, gMolActionInsertFrames, ig, mp->natoms * dcd.nframes, vp, (cp == NULL ? 0 : dcd.nframes * 4), cp) != 0)
3679                 s_append_asprintf(errbuf, "Cannot insert frames");
3680         mp->startStep = dcd.nstart;
3681         mp->stepsPerFrame = dcd.ninterval;
3682         mp->psPerStep = dcd.delta;
3683 exit:
3684         DcdClose(&dcd);
3685         if (cp != NULL)
3686                 free(cp);
3687         free(vp);
3688         free(xp);
3689         free(yp);
3690         free(zp);
3691         IntGroupRelease(ig);
3692         if (errcount == 0)
3693                 return 0;
3694         else return 1;
3695 }
3696
3697 int
3698 MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
3699 {
3700         FILE *fp;
3701         char buf[1024];
3702         int lineNumber;
3703         int i, retval;
3704         Vector v[3], vv;
3705         double d[3];
3706         int n, flag;
3707         char flags[3];
3708         *errbuf = NULL;
3709         fp = fopen(fname, "rb");
3710         if (fp == NULL) {
3711                 s_append_asprintf(errbuf, "Cannot open file");
3712                 return -1;
3713         }
3714         errbuf[0] = 0;
3715         lineNumber = 0;
3716         retval = 0;
3717         flags[0] = flags[1] = flags[2] = 0;
3718         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3719                 if (strncmp(buf, "Bounding box:", 13) == 0) {
3720                         for (i = 0; i < 3; i++) {
3721                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3722                                         s_append_asprintf(errbuf, "line %d: missing %d component of the bounding box", lineNumber, i + 1);
3723                                         retval = 1;
3724                                         goto abort;
3725                                 }
3726                                 n = sscanf(buf, "%lf %lf %lf %d", &d[0], &d[1], &d[2], &flag);
3727                                 if (n < 3) {
3728                                         vv.x = vv.y = vv.z = 0.0;
3729                                         switch (i) {
3730                                                 case 0: vv.x = d[0]; break;
3731                                                 case 1: vv.y = d[0]; break;
3732                                                 case 2: vv.z = d[0]; break;
3733                                         }
3734                                         if (n == 1 || (n == 2 && d[1] != 0.0))
3735                                                 flags[i] = 1;
3736                                 } else {
3737                                         vv.x = d[0];
3738                                         vv.y = d[1];
3739                                         vv.z = d[2];
3740                                         if (n == 4)
3741                                                 flags[i] = (flag != 0);
3742                                         else
3743                                                 flags[i] = (VecLength2(vv) != 0);
3744                                 }
3745                                 v[i] = vv;
3746                         }
3747                         if (mp->cell != NULL)
3748                                 vv = mp->cell->origin;
3749                         else
3750                                 vv.x = vv.y = vv.z = 0.0;
3751                         MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
3752                 } else if (strncmp(buf, "Bounding box origin:", 20) == 0) {
3753                         if (mp->cell != NULL) {
3754                                 v[0] = mp->cell->axes[0];
3755                                 v[1] = mp->cell->axes[1];
3756                                 v[2] = mp->cell->axes[2];
3757                                 memmove(flags, mp->cell->flags, 3);
3758                         } else {
3759                                 v[0].x = 1.0; v[0].y = v[0].z = 0.0;
3760                                 v[1].y = 1.0; v[1].x = v[1].z = 0.0;
3761                                 v[2].z = 1.0; v[2].x = v[2].y = 0.0;
3762                                 flags[0] = flags[1] = flags[2] = 1.0;
3763                         }
3764                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0 || (n = sscanf(buf, "%lf %lf %lf", &d[0], &d[1], &d[2]) < 3)) {
3765                                 s_append_asprintf(errbuf, "line %d: wrong format for the bounding box origin", lineNumber);
3766                                 retval = 1;
3767                                 goto abort;
3768                         }
3769                         vv.x = d[0];
3770                         vv.y = d[1];
3771                         vv.z = d[2];
3772                         MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
3773                 }
3774         }
3775         fclose(fp);
3776         return 0;
3777 abort:
3778         if (fp != NULL)
3779                 fclose(fp);
3780         return retval;
3781 }
3782                         
3783 int
3784 MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
3785 {
3786         int retval;
3787         *errbuf = NULL;
3788         if (ftype == NULL || *ftype == 0) {
3789                 const char *cp;
3790                 cp = strrchr(fname, '.');
3791                 if (cp != NULL)
3792                         ftype = cp + 1;
3793                 else {
3794                         cp = guessMoleculeType(fname);
3795                         if (strcmp(cp, "???") != 0)
3796                                 ftype = cp;
3797                 }
3798         }
3799         if (strcasecmp(ftype, "psf") == 0) {
3800                 retval = MoleculeWriteToPsfFile(mp, fname, errbuf);
3801         } else if (strcasecmp(ftype, "pdb") == 0) {
3802                 retval = MoleculeWriteToPdbFile(mp, fname, errbuf);
3803         } else if (strcasecmp(ftype, "tep") == 0) {
3804                 retval = MoleculeWriteToTepFile(mp, fname, errbuf);
3805         } else {
3806                 s_append_asprintf(errbuf, "The file format should be specified");
3807                 retval = 1;
3808         }
3809         if (retval == 0)
3810                 MoleculeSetPath(mp, fname);
3811         return retval;
3812 }
3813
3814 int
3815 MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char **errbuf)
3816 {
3817         FILE *fp;
3818         Int i, j, k, n1, n2, n3, n_aniso, nframes, nanchors, n_uff;
3819         Atom *ap;
3820         char bufs[6][8];
3821
3822         *errbuf = NULL;
3823         fp = fopen(fname, "wb");
3824         if (fp == NULL) {
3825                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
3826                 return 1;
3827         }
3828         errbuf[0] = 0;
3829
3830         nframes = MoleculeFlushFrames(mp);
3831
3832         fprintf(fp, "!:atoms\n");
3833         fprintf(fp, "! idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge\n");
3834         n1 = n2 = n3 = n_aniso = nanchors = n_uff = 0;
3835         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3836                 strncpy(bufs[0], ap->segName, 4);
3837                 bufs[0][4] = 0;
3838                 strncpy(bufs[1], ap->resName, 4);
3839                 bufs[1][4] = 0;
3840                 strncpy(bufs[2], ap->aname, 4);
3841                 bufs[2][4] = 0;
3842                 AtomTypeDecodeToString(ap->type, bufs[3]);
3843                 bufs[3][6] = 0;
3844                 strncpy(bufs[4], ap->element, 4);
3845                 bufs[4][2] = 0;
3846                 for (j = 0; j < 5; j++) {
3847                         if (bufs[j][0] == 0) {
3848                                 bufs[j][0] = '_';
3849                                 bufs[j][1] = 0;
3850                         }
3851                         for (k = 0; k < 6; k++) {
3852                                 if (bufs[j][k] == 0)
3853                                         break;
3854                                 if (bufs[j][k] > 0 && bufs[j][k] < ' ')
3855                                         bufs[j][k] = '_';
3856                         }
3857                 }
3858                 if (SYMOP_ALIVE(ap->symop))
3859                         n1++;
3860                 if (ap->fix_force != 0)
3861                         n2++;
3862                 if (ap->mm_exclude || ap->periodic_exclude)
3863                         n3++;
3864                 if (ap->aniso != NULL)
3865                         n_aniso++;
3866                 if (ap->anchor != NULL)
3867                         nanchors++;
3868                 if (ap->uff_type[0] != 0)
3869                         n_uff++;
3870                 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);
3871         }
3872         fprintf(fp, "\n");
3873         
3874         if (n_uff > 0) {
3875                 fprintf(fp, "!:uff_type\n");
3876                 fprintf(fp, "! idx uff_type\n");
3877                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3878                         fprintf(fp, "%d %.5s\n", i, ap->uff_type);
3879                 }
3880                 fprintf(fp, "\n");
3881         }
3882         
3883         if (n1 > 0) {
3884                 fprintf(fp, "!:atoms_symop\n");
3885                 fprintf(fp, "! idx symop symbase\n");
3886                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3887                         int n;
3888                         n = ap->symop.sym * 1000000 + ap->symop.dx * 10000 + ap->symop.dy * 100 + ap->symop.dz;
3889                         fprintf(fp, "%d %d %d\n", i, n, ap->symbase);
3890                 }
3891                 fprintf(fp, "\n");
3892         }
3893         
3894         if (n2 > 0) {
3895                 fprintf(fp, "!:atoms_fix\n");
3896                 fprintf(fp, "! idx fix_force fix_pos\n");
3897                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3898                         fprintf(fp, "%d %f %f %f %f\n", i, ap->fix_force, ap->fix_pos.x, ap->fix_pos.y, ap->fix_pos.z);
3899                 }
3900                 fprintf(fp, "\n");
3901         }
3902         
3903         if (n3 > 0) {
3904                 fprintf(fp, "!:mm_exclude\n");
3905                 fprintf(fp, "! idx mm_exclude periodic_exclude\n");
3906                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3907                         fprintf(fp, "%d %d %d\n", i, ap->mm_exclude, ap->periodic_exclude);
3908                 }
3909                 fprintf(fp, "\n");
3910         }
3911         
3912         if (nanchors > 0) {
3913                 fprintf(fp, "!:pi_anchor\n");
3914                 fprintf(fp, "! idx count; n1 weight1; n2 weight2; ...; nN weightN\n");
3915                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3916                         Int *ip;
3917                         if (ap->anchor == NULL)
3918                                 continue;
3919                         k = ap->anchor->connect.count;
3920                         ip = AtomConnectData(&ap->anchor->connect);
3921                         fprintf(fp, "%d %d\n", i, k);
3922                         for (j = 0; j < k; j++) {
3923                                 fprintf(fp, "%d %f\n", ip[j], ap->anchor->coeffs[j]);
3924                         }
3925                 }
3926                 fprintf(fp, "\n");
3927         }
3928                                 
3929         n1 = nframes;
3930         if (n1 > 0)
3931                 n2 = mp->cframe;
3932         else
3933                 n2 = 0;
3934         for (i = 0; (i == n2 || i < n1); i++) {
3935                 fprintf(fp, "!:positions ; frame %d\n", i);
3936                 fprintf(fp, "! idx x y z [sx sy sz]\n");
3937                 for (j = 0, ap = mp->atoms; j < mp->natoms; j++, ap = ATOM_NEXT(ap)) {
3938                         Vector *vp;
3939                         Byte sig_flag = 0;
3940                         if (i != n2 && i < ap->nframes)
3941                                 vp = ap->frames + i;
3942                         else {
3943                                 vp = &(ap->r);
3944                                 if (ap->sigma.x != 0.0 || ap->sigma.y != 0.0 || ap->sigma.z != 0.0)
3945                                         sig_flag = 1;
3946                         }
3947                         fprintf(fp, "%d %.8f %.8f %.8f", j, vp->x, vp->y, vp->z);
3948                         if (sig_flag) {
3949                                 fprintf(fp, " %.8f %.8f %.8f", ap->sigma.x, ap->sigma.y, ap->sigma.z);
3950                         }
3951                         fprintf(fp, "\n");
3952                 }
3953                 fprintf(fp, "\n");
3954         }
3955         
3956         if (mp->nbonds > 0) {
3957                 fprintf(fp, "!:bonds\n");
3958                 fprintf(fp, "! from1 to1 from2 to2 from3 to3 from4 to4\n");
3959                 for (i = 0; i < mp->nbonds; i++) {
3960                         fprintf(fp, "%d %d%c", mp->bonds[i * 2], mp->bonds[i * 2 + 1], (i % 4 == 3 || i == mp->nbonds - 1 ? '\n' : ' '));
3961                 }
3962                 fprintf(fp, "\n");
3963         }
3964
3965         if (mp->nbondOrders > 0) {
3966                 fprintf(fp, "!:bond_orders\n");
3967                 fprintf(fp, "! order1 order2 order3 order4\n");
3968                 for (i = 0; i < mp->nbondOrders; i++) {
3969                         fprintf(fp, "%.6f%c", mp->bondOrders[i], (i % 4 == 3 || i == mp->nbondOrders - 1 ? '\n' : ' '));
3970                 }
3971                 fprintf(fp, "\n");
3972         }
3973         
3974         if (mp->nangles > 0) {
3975                 fprintf(fp, "!:angles\n");
3976                 fprintf(fp, "! a1 b1 c1 a2 b2 c2 a3 b3 c3\n");
3977                 for (i = 0; i < mp->nangles; i++) {
3978                         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' : ' '));
3979                 }
3980                 fprintf(fp, "\n");
3981         }
3982         
3983         if (mp->ndihedrals > 0) {
3984                 fprintf(fp, "!:dihedrals\n");
3985                 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3986                 for (i = 0; i < mp->ndihedrals; i++) {
3987                         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' : ' '));
3988                 }
3989                 fprintf(fp, "\n");
3990         }
3991         
3992         if (mp->nimpropers > 0) {
3993                 fprintf(fp, "!:impropers\n");
3994                 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3995                 for (i = 0; i < mp->nimpropers; i++) {
3996                         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' : ' '));
3997                 }
3998                 fprintf(fp, "\n");
3999         }
4000         
4001         if (mp->cell != NULL) {
4002                 fprintf(fp, "!:xtalcell\n");
4003                 fprintf(fp, "! a b c alpha beta gamma\n");
4004                 fprintf(fp, "! This information is redundant and overridden by the following periodic_box info\n");
4005                 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]);
4006                 fprintf(fp, "\n");
4007
4008                 fprintf(fp, "!:periodic_box\n");
4009                 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");
4010                 for (i = 0; i < 3; i++)
4011                         fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->axes[i].x, mp->cell->axes[i].y, mp->cell->axes[i].z);
4012                 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->origin.x, mp->cell->origin.y, mp->cell->origin.z);
4013                 fprintf(fp, "%d %d %d%s\n", mp->cell->flags[0], mp->cell->flags[1], mp->cell->flags[2], (mp->cell->has_sigma ? " 1" : ""));
4014                 if (mp->cell->has_sigma) {
4015                         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]);
4016                 }
4017                 fprintf(fp, "\n");
4018         }
4019         
4020         if (mp->nframe_cells > 0) {
4021                 fprintf(fp, "!:frame_periodic_boxes\n");
4022                 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz\n");
4023                 for (i = 0; i < mp->nframe_cells * 4; i++) {
4024                         fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->frame_cells[i].x, mp->frame_cells[i].y, mp->frame_cells[i].z);
4025                 }
4026                 fprintf(fp, "\n");
4027         }
4028         
4029         if (mp->nsyms > 0) {
4030                 fprintf(fp, "!:symmetry_operations\n");
4031                 fprintf(fp, "! a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3\n");
4032                 for (i = 0; i < mp->nsyms; i++) {
4033                         Transform *tp = mp->syms + i;
4034                         const unsigned char s_index_order[12] = {0, 3, 6, 1, 4, 7, 2, 5, 8, 9, 10, 11};
4035                         for (j = 0; j < 12; j++)
4036                                 fprintf(fp, "%11.6f%c", (*tp)[s_index_order[j]], (j % 3 == 2 ? '\n' : ' '));
4037                 }
4038                 fprintf(fp, "\n");
4039         }
4040         
4041         if (n_aniso > 0) {
4042                 fprintf(fp, "!:anisotropic_thermal_parameters\n");
4043                 fprintf(fp, "! b11 b22 b33 b12 b13 b23 [sigma; sb11 sb22 sb33 sb12 sb13 sb23]\n");
4044                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4045                         if (ap->aniso != NULL) {
4046                                 Double *bp = ap->aniso->bij;
4047                                 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" : ""));
4048                                 if (ap->aniso->has_bsig) {
4049                                         bp = ap->aniso->bsig;
4050                                         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]);
4051                                 }
4052                         } else {
4053                                 fprintf(fp, "0 0 0 0 0 0\n");
4054                         }
4055                 }
4056                 fprintf(fp, "\n");              
4057         }
4058         
4059         if (mp->arena != NULL) {
4060                 MDArena *arena = mp->arena;
4061                 fprintf(fp, "!:md_parameters\n");
4062                 fprintf(fp, "log_file %s\n", arena->log_result_name);
4063                 fprintf(fp, "coord_file %s\n", arena->coord_result_name);
4064                 fprintf(fp, "vel_file %s\n", arena->vel_result_name);
4065                 fprintf(fp, "force_file %s\n", arena->force_result_name);
4066                 fprintf(fp, "debug_file %s\n", arena->debug_result_name);
4067                 fprintf(fp, "debug_output_level %d\n", arena->debug_output_level);
4068                 fprintf(fp, "step %d\n", arena->step);
4069                 fprintf(fp, "coord_output_freq %d\n", arena->coord_output_freq);
4070                 fprintf(fp, "energy_output_freq %d\n", arena->energy_output_freq);
4071                 fprintf(fp, "coord_frame %d\n", arena->coord_result_frame);
4072                 fprintf(fp, "timestep %g\n", arena->timestep);
4073                 fprintf(fp, "cutoff %g\n", arena->cutoff);
4074                 fprintf(fp, "electro_cutoff %g\n", arena->electro_cutoff);
4075                 fprintf(fp, "pairlist_distance %g\n", arena->pairlist_distance);
4076                 fprintf(fp, "switch_distance %g\n", arena->switch_distance);
4077                 fprintf(fp, "temperature %g\n", arena->temperature);
4078                 fprintf(fp, "andersen_freq %d\n", arena->andersen_thermo_freq);
4079                 fprintf(fp, "andersen_coupling %g\n", arena->andersen_thermo_coupling);
4080                 fprintf(fp, "random_seed %d\n", arena->random_seed);
4081                 fprintf(fp, "dielectric %g\n", arena->dielectric);
4082                 fprintf(fp, "gradient_convergence %g\n", arena->gradient_convergence);
4083                 fprintf(fp, "coordinate_convergence %g\n", arena->coordinate_convergence);
4084                 fprintf(fp, "use_xplor_shift %d\n", arena->use_xplor_shift);
4085                 fprintf(fp, "scale14_vdw %g\n", arena->scale14_vdw);
4086                 fprintf(fp, "scale14_elect %g\n", arena->scale14_elect);
4087                 fprintf(fp, "relocate_center %d\n", arena->relocate_center);
4088                 fprintf(fp, "surface_probe_radius %g\n", arena->probe_radius);
4089                 fprintf(fp, "surface_tension %g\n", arena->surface_tension);
4090                 fprintf(fp, "surface_potential_freq %d\n", arena->surface_potential_freq);
4091                 fprintf(fp, "use_graphite %d\n", arena->use_graphite);
4092                 fprintf(fp, "alchemical_lambda %g\n", arena->alchem_lambda);
4093                 fprintf(fp, "alchemical_delta_lambda %g\n", arena->alchem_dlambda);
4094                 if (arena->nalchem_flags > 0) {
4095                         fprintf(fp, "alchem_flags %d", arena->nalchem_flags);
4096                         for (i = 0; i < arena->nalchem_flags; i++) {
4097                                 if (i % 60 == 0)
4098                                         fputc('\n', fp);
4099                                 else if (i % 10 == 0)
4100                                         fputc(' ', fp);
4101                                 fputc('0' + arena->alchem_flags[i], fp);
4102                         }
4103                         fputc('\n', fp);
4104                 }
4105                 if (arena->pressure != NULL) {
4106                         Double *dp;
4107                         fprintf(fp, "pressure_freq %d\n", arena->pressure->freq);
4108                         fprintf(fp, "pressure_coupling %g\n", arena->pressure->coupling);
4109                         dp = arena->pressure->apply;
4110                         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]);
4111                         dp = arena->pressure->cell_flexibility;
4112                         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]);
4113                         fprintf(fp, "pressure_fluctuate_cell_origin %g\n", arena->pressure->fluctuate_cell_origin);
4114                         fprintf(fp, "pressure_fluctuate_cell_orientation %g\n", arena->pressure->fluctuate_cell_orientation);
4115                 }
4116                 fprintf(fp, "\n");
4117
4118                 if (mp->par != NULL) {
4119                         Parameter *par = mp->par;
4120                         fprintf(fp, "!:parameters\n");
4121                         ParameterAppendToFile(par, fp);
4122                         fprintf(fp, "\n");
4123                 }
4124                 
4125                 fprintf(fp, "!:velocity\n");
4126                 fprintf(fp, "! idx vx vy vz\n");
4127                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4128                         fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->v.x, ap->v.y, ap->v.z);
4129                 }
4130                 fprintf(fp, "\n");
4131
4132                 fprintf(fp, "!:force\n");
4133                 fprintf(fp, "! idx fx fy fz\n");
4134                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4135                         fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->f.x, ap->f.y, ap->f.z);
4136                 }
4137                 fprintf(fp, "\n");
4138         }
4139         
4140         if (mp->mview != NULL) {
4141                 double f[4];
4142                 if (mp->mview->track != NULL) {
4143                         fprintf(fp, "!:trackball\n");
4144                         fprintf(fp, "! scale; trx try trz; theta_deg x y z\n");
4145                         f[0] = TrackballGetScale(mp->mview->track);
4146                         fprintf(fp, "%f\n", f[0]);
4147                         TrackballGetTranslate(mp->mview->track, f);
4148                         fprintf(fp, "%f %f %f\n", f[0], f[1], f[2]);
4149                         TrackballGetRotate(mp->mview->track, f);
4150                         fprintf(fp, "%f %f %f %f\n", f[0], f[1], f[2], f[3]);
4151                         fprintf(fp, "\n");
4152                 }
4153                 fprintf(fp, "!:view\n");
4154                 fprintf(fp, "show_unit_cell %d\n", mp->mview->showUnitCell);
4155                 fprintf(fp, "show_periodic_box %d\n", mp->mview->showPeriodicBox);
4156                 fprintf(fp, "show_expanded_atoms %d\n", mp->mview->showExpandedAtoms);
4157                 fprintf(fp, "show_ellipsoids %d\n", mp->mview->showEllipsoids);
4158                 fprintf(fp, "show_hydrogens %d\n", mp->mview->showHydrogens);
4159                 fprintf(fp, "show_dummy_atoms %d\n", mp->mview->showDummyAtoms);
4160                 fprintf(fp, "show_rotation_center %d\n", mp->mview->showRotationCenter);
4161                 fprintf(fp, "show_graphite_flag %d\n", mp->mview->showGraphiteFlag);
4162                 fprintf(fp, "show_graphite %d\n", mp->mview->showGraphite);
4163                 fprintf(fp, "show_periodic_image_flag %d\n", mp->mview->showPeriodicImageFlag);
4164                 fprintf(fp, "show_periodic_image %d %d %d %d %d %d\n",
4165                                 mp->mview->showPeriodicImage[0], mp->mview->showPeriodicImage[1],
4166                                 mp->mview->showPeriodicImage[2], mp->mview->showPeriodicImage[3],
4167                                 mp->mview->showPeriodicImage[4], mp->mview->showPeriodicImage[5]);
4168                 if (mp->mview->atomRadius != 0.2)
4169                         fprintf(fp, "atom_radius %f\n", mp->mview->atomRadius);
4170                 if (mp->mview->bondRadius != 0.1)
4171                         fprintf(fp, "bond_radius %f\n", mp->mview->bondRadius);
4172                 if (mp->mview->atomResolution != 12)
4173                         fprintf(fp, "atom_resolution %d\n", mp->mview->atomResolution);
4174                 if (mp->mview->bondResolution != 8)
4175                         fprintf(fp, "bond_resolution %d\n", mp->mview->bondResolution);
4176                 fprintf(fp, "\n");
4177         }
4178
4179         fclose(fp);
4180         return 0;
4181 }
4182
4183 int
4184 MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char **errbuf)
4185 {
4186         FILE *fp;
4187         int i;
4188         Atom *ap;
4189         *errbuf = NULL;
4190         fp = fopen(fname, "wb");
4191         if (fp == NULL) {
4192                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4193                 return 1;
4194         }
4195         fprintf(fp, "PSF\n\n");
4196         fprintf(fp, "       1 !NTITLE\n");
4197         fprintf(fp, " REMARKS FILENAME=\n");
4198         fprintf(fp, "\n");
4199         
4200         /*  Atoms  */
4201         fprintf(fp, "%8d !NATOM\n", mp->natoms);
4202         for (i = 0; i < mp->natoms; i++) {
4203                 const char *fmt;
4204                 ap = ATOM_AT_INDEX(mp->atoms, i);
4205                 fprintf(fp, "%8d ", i + 1);
4206                 if (ap->resSeq >= 10000) {
4207                         fmt = "%-3.3s %-5d ";
4208                 } else {
4209                         fmt = "%-4.4s %-4d ";
4210                 }
4211                 fprintf(fp, fmt, ap->segName, ap->resSeq);
4212                 fprintf(fp, "%-3.3s  %-4.4s %-4.4s   %12.6f  %8.4f           0\n",
4213                         ap->resName, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->charge, ap->weight);
4214         }
4215         fprintf(fp, "\n");
4216         
4217         /*  Bonds  */
4218         fprintf(fp, "%8d !NBOND: bonds\n", mp->nbonds);
4219         for (i = 0; i < mp->nbonds * 2; i++) {
4220                 fprintf(fp, "%8d", mp->bonds[i] + 1);
4221                 if (i % 8 == 7)
4222                         fprintf(fp, "\n");
4223         }
4224         if (i % 8 != 0)
4225                 fprintf(fp, "\n");
4226         fprintf(fp, "\n");
4227         
4228         /*  Angles  */
4229         fprintf(fp, "%8d !NTHETA: angles\n", mp->nangles);
4230         for (i = 0; i < mp->nangles * 3; i++) {
4231                 fprintf(fp, "%8d", mp->angles[i] + 1);
4232                 if (i % 9 == 8)
4233                         fprintf(fp, "\n");
4234         }
4235         if (i % 9 != 0)
4236                 fprintf(fp, "\n");
4237         fprintf(fp, "\n");
4238         
4239         /*  Dihedrals  */
4240         fprintf(fp, "%8d !NPHI: dihedrals\n", mp->ndihedrals);
4241         for (i = 0; i < mp->ndihedrals * 4; i++) {
4242                 fprintf(fp, "%8d", mp->dihedrals[i] + 1);
4243                 if (i % 8 == 7)
4244                         fprintf(fp, "\n");
4245         }
4246         if (i % 8 != 0)
4247                 fprintf(fp, "\n");
4248         fprintf(fp, "\n");
4249         
4250         /*  Dihedrals  */
4251         fprintf(fp, "%8d !NIMPHI: impropers\n", mp->nimpropers);
4252         for (i = 0; i < mp->nimpropers * 4; i++) {
4253                 fprintf(fp, "%8d", mp->impropers[i] + 1);
4254                 if (i % 8 == 7)
4255                         fprintf(fp, "\n");
4256         }
4257         if (i % 8 != 0)
4258                 fprintf(fp, "\n");
4259         fprintf(fp, "\n");
4260         
4261         fprintf(fp, "%8d !NDON: donors\n\n", 0);
4262         fprintf(fp, "%8d !NACC: acceptors\n\n", 0);
4263         fprintf(fp, "%8d !NNB: non-bonding exclusions\n\n", 0);
4264         for (i = 0; i < mp->natoms; i++) {
4265                 fprintf(fp, "%8d", 0);
4266                 if (i % 8 == 7)
4267                         fprintf(fp, "\n");
4268         }
4269         if (i % 8 != 0)
4270                 fprintf(fp, "\n");
4271         fprintf(fp, "\n");
4272         fprintf(fp, "%8d !NGRP: groups\n", 1);
4273         fprintf(fp, "       0       0       0\n");
4274         fprintf(fp, "\n");
4275         
4276         i = strlen(fname);
4277         if (i > 5 && strcmp(fname + i - 5, ".psfx") == 0) {
4278                 /*  Extended psf (with coordinates and other info)  */
4279                 fprintf(fp, "%8d !COORD: coordinates\n", mp->natoms);
4280                 for (i = 0; i < mp->natoms; i++) {
4281                         Vector r;
4282                         ap = ATOM_AT_INDEX(mp->atoms, i);
4283                         r = ap->r;
4284                         fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->aname);
4285                 }
4286                 fprintf(fp, "\n");
4287         }
4288                 
4289         fclose(fp);
4290         return 0;
4291 }
4292
4293 int
4294 MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char **errbuf)
4295 {
4296         FILE *fp;
4297         int i, j;
4298         Atom *ap;
4299         *errbuf = NULL;
4300         fp = fopen(fname, "wb");
4301         if (fp == NULL) {
4302                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4303                 return 1;
4304         }
4305         for (i = 0; i < mp->natoms; i++) {
4306                 char buf[6];
4307                 ap = ATOM_AT_INDEX(mp->atoms, i);
4308                 if (ap->resSeq >= 10000) {
4309                         snprintf(buf, sizeof buf, "%c%03d", 'A' + (ap->resSeq - 10000) / 1000, ap->resSeq % 1000);
4310                 } else {
4311                         snprintf(buf, sizeof buf, "%4d", ap->resSeq);
4312                 }
4313                 fprintf(fp, "ATOM  %5d %-4.4s%1.1s%-3.3s %1.1s%4.4s%1.1s   "
4314                                         "%8.3f%8.3f%8.3f %5.2f %5.2f      "
4315                                         "%-4.4s%-2.2s%-2d\n",
4316                         i + 1, ap->aname, " ", ap->resName, " ", buf, " ",
4317                         ap->r.x, ap->r.y, ap->r.z, ap->occupancy, ap->tempFactor,
4318                         ap->segName, ap->element, ap->intCharge);
4319         }
4320         for (i = 0; i < mp->natoms; i++) {
4321                 Int *cp;
4322                 ap = ATOM_AT_INDEX(mp->atoms, i);
4323                 cp = AtomConnectData(&ap->connect);
4324                 for (j = 0; j < ap->connect.count; j++) {
4325                         if (j % 4 == 0) {
4326                                 if (j > 0)
4327                                         fprintf(fp, "\n");
4328                                 fprintf(fp, "CONECT%5d", i + 1);
4329                         }
4330                         fprintf(fp, "%5d", cp[j] + 1);
4331                 }
4332                 if (j > 0)
4333                         fprintf(fp, "\n");
4334         }
4335         fprintf(fp, "END\n");
4336         fclose(fp);
4337         return 0;
4338 }
4339
4340 int
4341 MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char **errbuf)
4342 {
4343         DcdRecord dcd;
4344         SFloat32 *xp, *yp, *zp;
4345         int n;
4346         *errbuf = NULL;
4347         if (mp == NULL || mp->natoms == 0) {
4348                 s_append_asprintf(errbuf, "Molecule is empty");
4349                 return 1;
4350         }
4351         memset(&dcd, 0, sizeof(dcd));
4352         dcd.natoms = mp->natoms;
4353         dcd.nframes = MoleculeGetNumberOfFrames(mp);
4354         if (dcd.nframes == 0) {
4355                 s_append_asprintf(errbuf, "no frame is present");
4356                 return 1;
4357         }
4358         dcd.nstart = mp->startStep;
4359         dcd.ninterval = mp->stepsPerFrame;
4360         if (dcd.ninterval == 0)
4361                 dcd.ninterval = 1;
4362         dcd.nend = dcd.nstart + (dcd.nframes - 1) * dcd.ninterval;
4363         if (mp->cell != NULL)
4364                 dcd.nextra = 1;
4365         dcd.delta = mp->psPerStep;
4366         if (dcd.delta == 0.0)
4367                 dcd.delta = 1.0;
4368         dcd.ncharmver = 24;
4369         n = DcdCreate(fname, &dcd);
4370         if (n != 0) {
4371                 if (n < 0)
4372                         s_append_asprintf(errbuf, "Cannot create dcd file");
4373                 else
4374                         s_append_asprintf(errbuf, "Cannot write dcd header");
4375                 DcdClose(&dcd);
4376                 return 1;
4377         }
4378         
4379         xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4380         yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4381         zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4382         if (xp == NULL || yp == NULL || zp == NULL) {
4383                 s_append_asprintf(errbuf, "Cannot allocate memory");
4384                 if (xp) free(xp);
4385                 if (yp) free(yp);
4386                 if (zp) free(zp);
4387                 DcdClose(&dcd);
4388                 return 1;
4389         }
4390         for (n = 0; n < dcd.nframes; n++) {
4391                 int i;
4392                 Atom *ap;
4393                 for (i = 0, ap = mp->atoms; i < dcd.natoms && mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4394                         Vector r;
4395                         if (ap->frames == NULL || n >= ap->nframes)
4396                                 r = ap->r;
4397                         else
4398                                 r = ap->frames[n];
4399                         xp[i] = r.x;
4400                         yp[i] = r.y;
4401                         zp[i] = r.z;
4402                 }
4403                 if (i < dcd.natoms) {
4404                         size_t sz = (dcd.natoms - i) * sizeof(SFloat32);
4405                         memset(xp + i, 0, sz);
4406                         memset(yp + i, 0, sz);
4407                         memset(zp + i, 0, sz);
4408                 }
4409                 if (n < mp->nframe_cells && mp->frame_cells != NULL) {
4410                         Vector *cp = &(mp->frame_cells[n * 4]);
4411                         dcd.globalcell[0] = VecLength(cp[0]);
4412                         dcd.globalcell[2] = VecLength(cp[1]);
4413                         dcd.globalcell[5] = VecLength(cp[2]);
4414                         dcd.globalcell[1] = VecDot(cp[0], cp[1]) / (dcd.globalcell[0] * dcd.globalcell[2]);
4415                         dcd.globalcell[3] = VecDot(cp[0], cp[2]) / (dcd.globalcell[0] * dcd.globalcell[5]);
4416                         dcd.globalcell[4] = VecDot(cp[1], cp[2]) / (dcd.globalcell[2] * dcd.globalcell[5]);                     
4417                 }                       
4418                 if (DcdWriteFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
4419                         s_append_asprintf(errbuf, "Write error in dcd file");
4420                         goto exit;
4421                 }
4422         }
4423         
4424 exit:
4425         DcdClose(&dcd);
4426         free(xp);
4427         free(yp);
4428         free(zp);
4429         if (errbuf[0] == 0)
4430                 return 0;
4431         else return 1;
4432 }
4433
4434 int
4435 MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
4436 {
4437         FILE *fp;
4438         int i;
4439         Vector v;
4440         *errbuf = NULL;
4441         fp = fopen(fname, "wb");
4442         if (fp == NULL) {
4443                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4444                 return 1;
4445         }
4446         if (mp->cell != NULL) {
4447                 fprintf(fp, "Bounding box:\n");
4448                 for (i = 0; i < 3; i++) {
4449                         v = mp->cell->axes[i];
4450                         fprintf(fp, "%.3f %.3f %.3f %d\n", v.x, v.y, v.z, mp->cell->flags[i]);
4451                 }
4452                 fprintf(fp, "Bounding box origin:\n");
4453                 v = mp->cell->origin;
4454                 fprintf(fp, "%.3f %.3f %.3f\n", v.x, v.y, v.z);
4455         }
4456         fclose(fp);
4457         return 0;
4458 }
4459                 
4460  static int
4461 sCompareByElement(const void *ap, const void *bp)
4462 {
4463         return ((*(Atom **)bp)->atomicNumber - (*(Atom **)ap)->atomicNumber);
4464 }
4465
4466 static int
4467 sMakeAdc(int n, int base, Symop symop)
4468 {
4469         int an, sym;
4470         if (SYMOP_ALIVE(symop)) {
4471                 an = base;
4472                 sym = (symop.dx + 5) * 10000 + (symop.dy + 5) * 1000 + (symop.dz + 5) * 100 + symop.sym + 1;
4473         } else {
4474                 an = n;
4475                 sym = 55501;
4476         }
4477         return (an + 1) * 100000 + sym;
4478 }
4479
4480 static int
4481 sCompareAdc(const void *ap, const void *bp)
4482 {
4483         int n = *((Int *)ap) % 100000 - *((Int *)bp) % 100000;
4484         if (n == 0)
4485                 n = *((Int *)ap) / 100000 - *((Int *)bp) / 100000;
4486         return n;
4487 }
4488
4489 static void
4490 sOutputAtomListInstructions(FILE *fp, int natoms, Atom *atoms)
4491 {
4492         int i, j, k, an, sym;
4493         Atom *ap;
4494         Int *adc;
4495         adc = (Int *)malloc(sizeof(Int) * natoms);
4496         if (adc == NULL)
4497                 return;
4498         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4499                 if (ap->exflags & kAtomHiddenFlag)
4500                         continue;
4501                 adc[i] = sMakeAdc(i, ap->symbase, ap->symop);
4502         }
4503         mergesort(adc, natoms, sizeof(Int), sCompareAdc);
4504         
4505         /*  Create the atom list  */
4506         an = sym = -1;
4507         for (i = j = k = 0; i < natoms; i++) {
4508                 int an1 = adc[i] / 100000;
4509                 int sym1 = adc[i] % 100000;
4510                 if (sym == sym1 && an1 == an + 1) {
4511                         /*  Continuous  */
4512                         an = an1;
4513                         k++;
4514                         continue;
4515                 }
4516                 if (k > 0)
4517                         /*  Output the last atom with a minus sign  */
4518                         adc[j++] = -(an * 100000 + sym);
4519                 /*  Output this atom  */
4520                 adc[j++] = adc[i];
4521                 an = an1;
4522                 sym = sym1;
4523                 k = 0;
4524         }
4525         if (k > 0)
4526                 adc[j++] = -(an * 100000 + sym);
4527         
4528         /*  Create the instruction cards  */
4529         for (i = k = 0; i < j; i++) {
4530                 if (k == 0)
4531                         fprintf(fp, "      401");
4532                 fprintf(fp, "%9d", adc[i]);
4533                 k++;
4534                 if (i == j - 1 || k == 6 || (k == 5 && i < j - 2 && adc[i + 2] < 0)) {
4535                         fprintf(fp, "\n");
4536                         k = 0;
4537                 }
4538         }
4539         free(adc);
4540 }
4541
4542 static int
4543 sEllipsoidType(int an)
4544 {
4545         return (an >= 18 ? 3 : (an >= 2 && an != 6 ? 2 : (an > 0 ? 1 : 0)));
4546 }
4547
4548 static void
4549 sOutputAtomTypeInstructions(FILE *fp, int natoms, Atom *atoms)
4550 {
4551         int i;
4552         Atom *ap;
4553         int etype, elast, istart, ilast, n1, n2;
4554         elast = istart = ilast = -1;
4555         for (i = 0, ap = atoms; i <= natoms; i++, ap++) {
4556                 if (i < natoms) {
4557                         if (SYMOP_ALIVE(ap->symop))
4558                                 continue;
4559                         if (ap->exflags & kAtomHiddenFlag)
4560                                 continue;
4561                         etype = sEllipsoidType(ap->atomicNumber);
4562                         if (elast < 0) {
4563                                 istart = ilast = i;
4564                                 elast = etype;
4565                                 continue;
4566                         } else if (elast == etype && ilast == i - 1) {
4567                                 ilast++;
4568                                 continue;
4569                         }
4570                 }
4571                 /*  Output the instruction card for the 'last' block of atoms  */
4572                 switch (etype) {
4573                         case 2:
4574                                 n1 = 4; n2 = 0; break;
4575                         case 3:
4576                                 n1 = 4; n2 = 5; break;
4577                         default:
4578                                 n1 = 1; n2 = 0; break;
4579                 }
4580                 fprintf(fp, "  1   715 %8d        0 %8d        0    0.100    0.000    0.000\n", n1, n2);
4581                 fprintf(fp, "                           %9d%9d\n", istart + 1, ilast + 1);
4582                 elast = etype;
4583                 ilast = istart = i;
4584         }
4585 }
4586
4587 static int
4588 sCompareBondType(const void *ap, const void *bp)
4589 {
4590         /*  Descending order  */
4591         return *((int *)bp) - *((int *)ap);
4592 }
4593
4594 static void
4595 sOutputBondInstructions(FILE *fp, int natoms, Atom *atoms, int overlap_correction)
4596 {
4597         Atom *ap, *ap2;
4598         char buf[96];
4599         int i, j, n[5], an, count, n1, n2, k;
4600         Int *cp;
4601         Int nexbonds;
4602         Int *exbonds;
4603         static const float sBondRad[4] = {0.060, 0.060, 0.060, 0.040};
4604         static const int sBondShade[4] = {5, 3, 1, 1};
4605
4606         n[0] = n[1] = n[2] = n[3] = 0;  /*  Start index of 3rd row atoms (and higher), 2nd row, 1st row, and H */
4607         n[4] = natoms;
4608         for (i = natoms - 1, ap = atoms + i; i >= 0; i--, ap--) {
4609                 an = ap->atomicNumber;
4610                 if (an < 2)
4611                         n[3] = i;
4612                 if (an < 10)
4613                         n[2] = i;
4614                 if (an < 18)
4615                         n[1] = i;
4616         }
4617         nexbonds = 0;
4618         exbonds = NULL;
4619         count = 0;
4620
4621         if (overlap_correction)
4622                 strcpy(buf, "  2  1001    0.000\n");
4623         else
4624                 strcpy(buf, "  2   812\n");
4625         
4626         for (i = 0; i < 4; i++) {
4627                 for (j = i; j < 4; j++) {
4628                         /*  Examine bonds between "group i" and "group j"  */
4629                         Vector dr;
4630                         double d;
4631                         double min_bond = 10000.0;     /*  Minimum distance between bound atoms  */
4632                         double min_nonbond = 10000.0;  /*  Minimum distance between non-bound atoms  */
4633                         double max_bond = -10000.0;    /*  Maximum distance between bound atoms  */
4634                         int count_exbond = 0;          /*  Number of explicit bonds in this group  */
4635                         for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
4636                                 for (n2 = n[j], ap2 = atoms + n2; n2 < n[j + 1]; n2++, ap2++) {
4637                                         if (n1 == n2)
4638                                                 continue;
4639                                         VecSub(dr, ap->r, ap2->r);
4640                                         d = VecLength(dr);
4641                                         cp = AtomConnectData(&ap->connect);
4642                                         for (k = ap->connect.count - 1; k >= 0; k--) {
4643                                                 if (cp[k] == n2)
4644                                                         break;
4645                                         }
4646                                         if (k >= 0) {
4647                                                 /*  n1 and n2 are bound  */
4648                                                 if (d < min_bond)
4649                                                         min_bond = d;
4650                                                 if (d > max_bond)
4651                                                         max_bond = d;
4652                                         } else {
4653                                                 /*  n1 and n2 are not bound  */
4654                                                 if (d < min_nonbond)
4655                                                         min_nonbond = d;
4656                                         }
4657                                 }
4658                         }
4659                         if (min_bond == 10000.0)
4660                                 continue;  /*  No bonds between these groups  */
4661                         min_bond *= 0.9;
4662                         if (max_bond + 0.002 < min_nonbond)
4663                                 max_bond += 0.002;
4664                         else {
4665                                 max_bond = min_nonbond - 0.002;
4666                                 /*  Some bonds may be omitted, so scan all bonds again  */
4667                                 for (n1 = n[i], ap = ATOM_AT_INDEX(atoms, n1); n1 < n[i + 1]; n1++, ap = ATOM_NEXT(ap)) {
4668                                         cp = AtomConnectData(&ap->connect);
4669                                         for (k = ap->connect.count - 1; k >= 0; k--) {
4670                                                 n2 = cp[k];
4671                                                 if (n2 < n[j] || n2 >= n[j + 1])
4672                                                         continue;
4673                                                 ap2 = atoms + n2;
4674                                                 VecSub(dr, ap->r, ap2->r);
4675                                                 d = VecLength(dr);
4676                                                 if (d > max_bond) {
4677                                                         /*  This bond should be explicitly defined  */
4678                                                         Int adc1, adc2;
4679                                                         if (count_exbond == 0) {
4680                                                                 adc1 = -(i + 1);  /*  Bond type  */
4681                                                                 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4682                                                         }
4683                                                         adc1 = sMakeAdc(n1, ap->symbase, ap->symop);
4684                                                         adc2 = sMakeAdc(n2, ap2->symbase, ap2->symop);
4685                                                         AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4686                                                         AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc2);
4687                                                         count_exbond++;
4688                                                 }
4689                                         }
4690                                 }
4691                         }
4692                         /*  Output the last instruction card  */
4693                         fputs(buf, fp);
4694                         /*  Make a new trailer card  */
4695                         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]);
4696                         count++;
4697                 }
4698         }
4699         if (count > 0) {
4700                 /*  Output the last trailer card  */
4701                 buf[2] = ' ';
4702                 fputs(buf, fp);
4703         }
4704         if (nexbonds > 0) {
4705                 if (count == 0 && overlap_correction) {
4706                         /*  1001 card is not yet written, so write it  */
4707                         buf[2] = ' ';
4708                         fputs(buf, fp);
4709                 }
4710                 snprintf(buf, sizeof(buf), "  1   %3d", (overlap_correction ? 821 : 811));
4711                 k = -exbonds[0] - 1;  /*  Bond type for the first block  */
4712                 i = 1;  /*  Index for exbonds[]  */
4713                 j = 0;  /*  Count in this block  */
4714                 while (i <= nexbonds) {
4715                         if (j >= 29 || i == nexbonds || exbonds[i] < 0) {
4716                                 /*  End of block  */
4717                                 buf[2] = '2';
4718                                 fputs(buf, fp);
4719                                 /*  The trailer card  */
4720                                 fprintf(fp, "                     %3d            %6.3f\n", sBondShade[k], sBondRad[k]);
4721                                 if (i == nexbonds)
4722                                         break;
4723                                 if (exbonds[i] < 0)
4724                                         k = -exbonds[i++] - 1;  /*  The new bond type  */
4725                                 j = 0;
4726                         } else if (j > 0 && j % 3 == 0) {
4727                                 buf[2] = '1';
4728                                 fputs(buf, fp);
4729                         }
4730                         n1 = exbonds[i++];
4731                         n2 = exbonds[i++];
4732                         snprintf(buf + 9 + (j % 3) * 18, sizeof(buf) - 9 - (j % 3) * 18, "%9d%9d\n", n1, n2);
4733                         j++;
4734                 }
4735                 free(exbonds);
4736         }
4737 }
4738
4739 int
4740 MoleculeWriteToTepFile(Molecule *mp, const char *fname, char **errbuf)
4741 {
4742         FILE *fp;
4743         int i, j, natoms, *ip;
4744         Int *cp;
4745         Atom *ap, *atoms, **app;
4746         Double *dp;
4747         static Double sUnit[] = {1, 1, 1, 90, 90, 90};
4748         
4749         *errbuf = NULL;
4750
4751         /*  Create sorted array of atoms  */
4752         natoms = mp->natoms;
4753         atoms = (Atom *)calloc(sizeof(Atom), natoms);
4754         app = (Atom **)calloc(sizeof(Atom *), natoms);
4755         ip = (int *)calloc(sizeof(int), natoms);
4756         if (atoms == NULL || app == NULL || ip == NULL) {
4757                 s_append_asprintf(errbuf, "Cannot allocate memory");
4758                 return 1;
4759         }
4760         /*  Sort the atom pointer by atomic number  */
4761         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
4762                 app[i] = ap;
4763         mergesort(app, natoms, sizeof(Atom *), sCompareByElement);
4764         for (i = 0; i < natoms; i++) {
4765                 /*  ip[old_index] is new_index  */
4766                 ip[app[i] - mp->atoms] = i;
4767         }
4768         /*  Copy the atom record to atoms[]  */
4769         /*  The 'v' member contains crystallographic coordinates  */
4770         /*  The connection table and symbase are renumbered  */
4771         /*  Hidden flags are modified to reflect the visibility in the MainView  */
4772         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4773                 AtomDuplicateNoFrame(ap, app[i]);
4774         /*      memmove(ap, app[i], gSizeOfAtomRecord); */
4775                 MoleculeCartesianToXtal(mp, &(ap->v), &(ap->r));
4776                 cp = AtomConnectData(&ap->connect);
4777                 for (j = ap->connect.count - 1; j >= 0; j--) {
4778                         cp[j] = ip[cp[j]];
4779                 }
4780                 if (SYMOP_ALIVE(ap->symop))
4781                         ap->symbase = ip[ap->symbase];
4782                 if (MainView_isAtomHidden(mp->mview, i)) {
4783                         ap->exflags |= kAtomHiddenFlag;
4784                 } else {
4785                         ap->exflags &= ~kAtomHiddenFlag;
4786                 }
4787         }
4788         free(ip);
4789         free(app);
4790         
4791         fp = fopen(fname, "wb");
4792         if (fp == NULL) {
4793                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4794                 return 1;
4795         }
4796
4797         /*  Title line  */
4798         fprintf(fp, "Generated by Molby\n");
4799         
4800         /*  XtalCell  */
4801         if (mp->cell != NULL) {
4802                 dp = mp->cell->cell;
4803         } else {
4804                 dp = sUnit;
4805         }
4806         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]);
4807         
4808         /*  Symmetry operations  */
4809         if (mp->nsyms > 0) {
4810                 for (i = 0; i < mp->nsyms; i++) {
4811                         dp = mp->syms[i];
4812                         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]);
4813                 }
4814         } else {
4815                 fprintf(fp, "1             0  1  0  0              0  0  1  0              0  0  0  1\n");
4816         }
4817         
4818         /*  Atoms  */
4819         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4820                 /*  The 'v' field contains crystallographic coordinates  */
4821                 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);
4822                 if (ap->aniso != NULL) {
4823                         dp = ap->aniso->bij;
4824                         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);
4825                 } else {
4826                         Double temp = ap->tempFactor;
4827                         if (temp <= 0)
4828                                 temp = 1.2;
4829                         fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", temp, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4830                 }
4831         }
4832         /*  Special points  */
4833         {
4834                 Vector camera, lookat, up, xvec, yvec, zvec;
4835                 MainView_getCamera(mp->mview, &camera, &lookat, &up);
4836                 VecSub(zvec, lookat, camera);
4837                 VecCross(xvec, zvec, up);
4838                 NormalizeVec(&xvec, &xvec);
4839                 NormalizeVec(&yvec, &up);
4840                 VecInc(xvec, lookat);
4841                 VecInc(yvec, lookat);
4842                 MoleculeCartesianToXtal(mp, &lookat, &lookat);
4843                 MoleculeCartesianToXtal(mp, &xvec, &xvec);
4844                 MoleculeCartesianToXtal(mp, &yvec, &yvec);
4845                 fprintf(fp, " ORGN                      %9g%9g%9g        0\n", 0.0, 0.0, 0.0);
4846                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4847                 fprintf(fp, " CNTR                      %9g%9g%9g        0\n", lookat.x, lookat.y, lookat.z);
4848                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4849                 fprintf(fp, " X                         %9g%9g%9g        0\n", xvec.x, xvec.y, xvec.z);
4850                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4851                 fprintf(fp, " Y                         %9g%9g%9g        0\n", yvec.x, yvec.y, yvec.z);
4852                 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);
4853         }
4854         
4855         /*  Instructions  */
4856         fprintf(fp, "      201\n");
4857         fprintf(fp, "      205       12\n");
4858         fprintf(fp, "      301      6.6      6.6        0      0.8\n");
4859         sOutputAtomListInstructions(fp, natoms, atoms);
4860         fprintf(fp, "      501%4d55501%4d55501%4d55501%4d55501%4d55501                 1\n", natoms + 2, natoms + 2, natoms + 3, natoms + 2, natoms + 4);
4861         fprintf(fp, "      502        1      0.0        2      0.0        3      0.0\n");
4862         fprintf(fp, "      604                               1.538\n");
4863
4864         sOutputBondInstructions(fp, natoms, atoms, 1);
4865         sOutputAtomTypeInstructions(fp, natoms, atoms);
4866         sOutputBondInstructions(fp, natoms, atoms, 0);
4867
4868         for (i = 0; i < natoms; i++) {
4869                 AtomClean(atoms + i);
4870         }
4871         free(atoms);
4872
4873         fprintf(fp, "      202\n");
4874         fprintf(fp, "  0    -1\n");
4875         fclose(fp);
4876         return 0;
4877 }
4878
4879 void
4880 MoleculeDump(Molecule *mol)
4881 {
4882         int i, j;
4883         Int *cp;
4884         Atom *ap;
4885         for (i = 0; i < mol->natoms; i++) {
4886                 char buf1[8];
4887                 ap = ATOM_AT_INDEX(mol->atoms, i);
4888                 snprintf(buf1, sizeof buf1, "%3.4s.%d", ap->resName, ap->resSeq);
4889                 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);
4890                 cp = AtomConnectData(&ap->connect);
4891                 for (j = 0; j < ap->connect.count; j++) {
4892                         fprintf(stderr, "%s%d", (j > 0 ? "," : ""), cp[j]);
4893                 }
4894                 fprintf(stderr, "]\n");
4895         }
4896 }
4897
4898 #pragma mark ====== MD support (including modification of Molecule) ======
4899
4900 /*  Call md_prepare for the MDArena. If MDArena has not been created, a new arena is created.
4901         If something goes wrong, returns 1 (for missing parameters) or -1 (more serious error).
4902     If retmsg is not NULL, a message describing the problem is returned there. This message
4903     must be free'd by the caller.  */
4904 int
4905 MoleculePrepareMDArena(Molecule *mol, int check_only, char **retmsg)
4906 {
4907         const char *msg;
4908         Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
4909         Int missing = 0;
4910         IntGroup *ig1, *ig2, *ig3;
4911         MDArena *arena = mol->arena;
4912
4913         if (arena == NULL) {
4914                 md_arena_new(mol);
4915                 arena = mol->arena;
4916         } else if (arena->xmol != mol)
4917                 md_arena_set_molecule(arena, mol);
4918
4919         arena->is_initialized = 0;
4920         
4921         /*  Rebuild the tables  */
4922         ig1 = ig2 = ig3 = NULL;
4923         nangles = MoleculeFindMissingAngles(mol, &angles);
4924         ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
4925         nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
4926         if (nangles > 0) {
4927                 ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
4928                 MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
4929                 free(angles);
4930                 IntGroupRelease(ig1);
4931         }
4932         if (ndihedrals > 0) {
4933                 ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
4934                 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
4935                 free(dihedrals);
4936                 IntGroupRelease(ig2);
4937         }
4938         if (nimpropers > 0) {
4939                 ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
4940                 MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
4941                 free(impropers);
4942                 IntGroupRelease(ig3);
4943         }
4944         
4945         {
4946                 /*  Update the path information of the molecule before MD setup  */
4947                 char *buf = (char *)malloc(4096);
4948                 MoleculeCallback_pathName(mol, buf, sizeof buf);
4949                 MoleculeSetPath(mol, buf);
4950                 free(buf);
4951         }
4952                 
4953         /*  Prepare parameters and internal information  */
4954         msg = md_prepare(arena, check_only);
4955         
4956         /*  Some parameters are missing?  */
4957         if (msg != NULL) {
4958                 if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
4959                         missing = 1;
4960                 else {
4961                         if (retmsg != NULL)
4962                                 asprintf(retmsg, "cannot initialize for MD: %s", msg);
4963                         return -1;
4964                 }
4965         }
4966         
4967         /*  The local parameter list is updated  */
4968         {
4969                 Int parType, idx;
4970                 if (mol->par == NULL)
4971                         mol->par = ParameterNew();
4972                 for (parType = kFirstParType; parType <= kLastParType; parType++) {
4973                         /*  Delete global and undefined parameters  */
4974                         UnionPar *up, *upbuf;
4975                         Int nparams, count;
4976                         ig1 = IntGroupNew();
4977                         for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
4978                                 if (up->bond.src != 0)
4979                                         IntGroupAdd(ig1, idx, 1);
4980                         }
4981                         if (IntGroupGetCount(ig1) > 0)
4982                                 MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
4983                         IntGroupRelease(ig1);
4984                         /*  Copy global and undefined parameters from arena and insert to mol->par  */
4985                         nparams = ParameterGetCountForType(arena->par, parType);
4986                         if (nparams == 0)
4987                                 continue;
4988                         upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
4989                         ig1 = IntGroupNew();
4990                         ig2 = IntGroupNew();
4991                         for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
4992                                 if (up->bond.src > 0)
4993                                         IntGroupAdd(ig1, idx, 1); /* Global parameter */
4994                                 else if (up->bond.src < 0)
4995                                         IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
4996                         }
4997                         if ((count = IntGroupGetCount(ig1)) > 0) {
4998                                 /*  Insert global parameters (at the top)  */
4999                                 ParameterCopy(arena->par, parType, upbuf, ig1);
5000                                 ig3 = IntGroupNewWithPoints(0, count, -1);
5001                                 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5002                                 IntGroupRelease(ig3);
5003                         }
5004                         if ((count = IntGroupGetCount(ig2)) > 0) {
5005                                 /*  Insert undefined parameters (at the bottom)  */
5006                                 ParameterCopy(arena->par, parType, upbuf, ig2);
5007                                 idx = ParameterGetCountForType(mol->par, parType);
5008                                 ig3 = IntGroupNewWithPoints(idx, count, -1);
5009                                 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5010                                 IntGroupRelease(ig3);
5011                         }
5012                         IntGroupRelease(ig2);
5013                         IntGroupRelease(ig1);
5014                         free(upbuf);
5015                 }
5016                 mol->needsMDRebuild = 0;  /*  We know the "modified" parameters are consistent with the MDArena  */
5017         }
5018         
5019         if (missing) {
5020                 if (retmsg != NULL)
5021                         *retmsg = strdup(msg);
5022                 return 1;
5023         } else return 0;
5024 }
5025
5026 #pragma mark ====== Serialize ======
5027
5028 Molecule *
5029 MoleculeDeserialize(const char *data, Int length, Int *timep)
5030 {
5031         Molecule *mp;
5032         Parameter *par;
5033         Atom *ap;
5034 /*      int result; */
5035
5036         mp = MoleculeNew();
5037         if (mp == NULL)
5038                 goto out_of_memory;
5039         par = ParameterNew();
5040         if (par == NULL)
5041                 goto out_of_memory;
5042
5043         while (length >= 12) {
5044                 const char *ptr = data + 8 + sizeof(Int);
5045                 int len = *((const Int *)(data + 8));
5046                 int i, j, n;
5047                 if (strcmp(data, "ATOM") == 0) {
5048                         n = len / gSizeOfAtomRecord;
5049                         NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, n);
5050                         memmove(mp->atoms, ptr, len);
5051                 } else if (strcmp(data, "ANISO") == 0) {
5052                         n = len / (sizeof(Int) + sizeof(Aniso));
5053                         for (i = 0; i < n; i++) {
5054                                 j = *((const Int *)ptr);
5055                                 if (j < 0 || j >= mp->natoms)
5056                                         goto bad_format;
5057                                 ap = ATOM_AT_INDEX(mp->atoms, j);
5058                                 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
5059                                 if (ap->aniso == NULL)
5060                                         goto out_of_memory;
5061                                 *(ap->aniso) = *((Aniso *)(ptr + sizeof(Int)));
5062                                 ptr += sizeof(Int) + sizeof(Aniso);
5063                         }
5064                 } else if (strcmp(data, "FRAME") == 0) {
5065                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5066                                 if (ap->nframes == 0)
5067                                         continue;
5068                                 ap->frames = (Vector *)malloc(sizeof(Vector) * ap->nframes);
5069                                 if (ap->frames == NULL)
5070                                         goto out_of_memory;
5071                                 memmove(ap->frames, ptr, sizeof(Vector) * ap->nframes);
5072                                 ptr += sizeof(Vector) * ap->nframes;
5073                         }
5074                 } else if (strcmp(data, "EXTCON") == 0) {
5075                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5076                                 if (ap->connect.count <= ATOM_CONNECT_LIMIT)
5077                                         continue;
5078                                 n = ap->connect.count;
5079                                 ap->connect.count = 0;
5080                                 ap->connect.u.ptr = NULL;
5081                                 NewArray(&(ap->connect.u.ptr), &(ap->connect.count), sizeof(Int), n);
5082                                 memmove(ap->connect.u.ptr, ptr, sizeof(Int) * n);
5083                                 ptr += sizeof(Int) * n;
5084                         }
5085                 } else if (strcmp(data, "BOND") == 0) {
5086                         n = len / (sizeof(Int) * 2);
5087                         NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, n);
5088                         memmove(mp->bonds, ptr, len);
5089                 } else if (strcmp(data, "ANGLE") == 0) {
5090                         n = len / (sizeof(Int) * 3);
5091                         NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, n);
5092                         memmove(mp->angles, ptr, len);
5093                 } else if (strcmp(data, "DIHED") == 0) {
5094                         n = len / (sizeof(Int) * 4);
5095                         NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, n);
5096                         memmove(mp->dihedrals, ptr, len);
5097                 } else if (strcmp(data, "IMPROP") == 0) {
5098                         n = len / (sizeof(Int) * 4);
5099                         NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, n);
5100                         memmove(mp->impropers, ptr, len);
5101                 } else if (strcmp(data, "RESIDUE") == 0) {
5102                         n = len / 4;
5103                         NewArray(&mp->residues, &mp->nresidues, 4, n);
5104                         memmove(mp->residues, ptr, len);
5105                 } else if (strcmp(data, "CELL") == 0) {
5106                         mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
5107                         if (mp->cell == NULL)
5108                                 goto out_of_memory;
5109                         memmove(mp->cell, ptr, sizeof(XtalCell));
5110                 } else if (strcmp(data, "SYMOP") == 0) {
5111                         n = len / sizeof(Transform);
5112                         NewArray(&mp->syms, &mp->nsyms, sizeof(Transform), n);
5113                         memmove(mp->syms, ptr, len);
5114                 } else if (strcmp(data, "ANCHOR") == 0) {
5115                         const char *ptr2 = ptr + len;
5116                         while (ptr < ptr2) {
5117                                 PiAnchor an;
5118                                 memset(&an, 0, sizeof(an));
5119                                 i = *((Int *)ptr);
5120                                 if (i >= 0 && i < mp->natoms) {
5121                                         n = *((Int *)(ptr + sizeof(Int)));
5122                                         AtomConnectResize(&(an.connect), n);
5123                                         memmove(AtomConnectData(&(an.connect)), ptr + sizeof(Int) * 2, sizeof(Int) * n);
5124                                         NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), n);
5125                                         memmove(an.coeffs, ptr + sizeof(Int) * (2 + n), sizeof(Double) * n);
5126                                         ap = ATOM_AT_INDEX(mp->atoms, i);
5127                                         ap->anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
5128                                         memmove(ap->anchor, &an, sizeof(PiAnchor));
5129                                 }
5130                                 ptr += sizeof(Int) * (2 + n) + sizeof(Double) * n;
5131                         }
5132                 } else if (strcmp(data, "TIME") == 0) {
5133                         if (timep != NULL)
5134                                 *timep = *((Int *)ptr);
5135                 } else if (strcmp(data, "BONDPAR") == 0) {
5136                         mp->par = par;
5137                         n = len / sizeof(BondPar);
5138                         NewArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), n);
5139                         memmove(par->bondPars, ptr, len);
5140                 } else if (strcmp(data, "ANGPAR") == 0) {
5141                         mp->par = par;
5142                         n = len / sizeof(AnglePar);
5143                         NewArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), n);
5144                         memmove(par->anglePars, ptr, len);
5145                 } else if (strcmp(data, "DIHEPAR") == 0) {
5146                         mp->par = par;
5147                         n = len / sizeof(TorsionPar);
5148                         NewArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), n);
5149                         memmove(par->dihedralPars, ptr, len);
5150                 } else if (strcmp(data, "IMPRPAR") == 0) {
5151                         mp->par = par;
5152                         n = len / sizeof(TorsionPar);
5153                         NewArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), n);
5154                         memmove(par->improperPars, ptr, len);
5155                 } else if (strcmp(data, "VDWPAR") == 0) {
5156                         mp->par = par;
5157                         n = len / sizeof(VdwPar);
5158                         NewArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), n);
5159                         memmove(par->vdwPars, ptr, len);
5160                 } else if (strcmp(data, "VDWPPAR") == 0) {
5161                         mp->par = par;
5162                         n = len / sizeof(VdwPairPar);
5163                         NewArray(&par->vdwpPars, &par->nvdwpPars, sizeof(VdwPairPar), n);
5164                         memmove(par->vdwpPars, ptr, len);
5165                 } else if (strcmp(data, "VCUTPAR") == 0) {
5166                         mp->par = par;
5167                         n = len / sizeof(VdwCutoffPar);
5168                         NewArray(&par->vdwCutoffPars, &par->nvdwCutoffPars, sizeof(VdwCutoffPar), n);
5169                         memmove(par->vdwCutoffPars, ptr, len);
5170                 }
5171                 len += 8 + sizeof(Int);
5172                 data += len;
5173                 length -= len;
5174         }
5175         if (mp->par == NULL)
5176                 ParameterRelease(par);
5177 /*      result = MoleculeRebuildTablesFromConnects(mp);
5178         if (result != 0)
5179                 goto bad_format; */
5180         return mp;
5181         
5182   out_of_memory:
5183         Panic("Low memory while deserializing molecule data");
5184         return NULL; /* Not reached */
5185
5186   bad_format:
5187         Panic("internal error: bad format during deserializing molecule data");
5188         return NULL; /* Not reached */
5189 }
5190
5191 char *
5192 MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep)
5193 {
5194         char *ptr, *p;
5195         int len, len_all, i, naniso, nframes, nconnects, nanchors;
5196         Atom *ap;
5197
5198         /*  Array of atoms  */
5199         len = 8 + sizeof(Int) + gSizeOfAtomRecord * mp->natoms;
5200         ptr = (char *)malloc(len);
5201         if (ptr == NULL)
5202                 goto out_of_memory;
5203         memmove(ptr, "ATOM\0\0\0\0", 8);
5204         *((Int *)(ptr + 8)) = gSizeOfAtomRecord * mp->natoms;
5205         p = ptr + 8 + sizeof(Int);
5206         memmove(p, mp->atoms, gSizeOfAtomRecord * mp->natoms);
5207         naniso = nframes = nconnects = nanchors = 0;
5208         for (i = 0; i < mp->natoms; i++) {
5209                 ap = ATOM_AT_INDEX(p, i);
5210                 if (ap->aniso != NULL) {
5211                         naniso++;
5212                         ap->aniso = NULL;
5213                 }
5214                 if (ap->frames != NULL) {
5215                         nframes += ap->nframes;
5216                         ap->frames = NULL;
5217                 }
5218                 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5219                         nconnects += ap->connect.count;
5220                         ap->connect.u.ptr = NULL;
5221                 }
5222                 if (ap->anchor != NULL) {
5223                         nanchors++;
5224                         ap->anchor = NULL;
5225                 }
5226         }
5227         len_all = len;
5228
5229         /*  Array of aniso  */
5230         if (naniso > 0) {
5231                 len = 8 + sizeof(Int) + (sizeof(Int) + sizeof(Aniso)) * naniso;
5232                 ptr = (char *)realloc(ptr, len_all + len);
5233                 if (ptr == NULL)
5234                         goto out_of_memory;
5235                 p = ptr + len_all;
5236                 memmove(p, "ANISO\0\0\0", 8);
5237                 *((Int *)(p + 8)) = (sizeof(Int) + sizeof(Aniso)) * naniso;
5238                 p += 8 + sizeof(Int);
5239                 for (i = 0; i < mp->natoms; i++) {
5240                         ap = ATOM_AT_INDEX(mp->atoms, i);
5241                         if (ap->aniso != NULL) {
5242                                 *((Int *)p) = i;
5243                                 *((Aniso *)(p + sizeof(Int))) = *(ap->aniso);
5244                                 p += sizeof(Int) + sizeof(Aniso);
5245                         }
5246                 }
5247                 len_all += len;
5248         }
5249         
5250         /*  Array of frames  */
5251         if (nframes > 0) {
5252                 len = 8 + sizeof(Int) + sizeof(Vector) * nframes;
5253                 ptr = (char *)realloc(ptr, len_all + len);
5254                 if (ptr == NULL)
5255                         goto out_of_memory;
5256                 p = ptr + len_all;
5257                 memmove(p, "FRAME\0\0\0", 8);
5258                 *((Int *)(p + 8)) = sizeof(Vector) * nframes;
5259                 p += 8 + sizeof(Int);
5260                 for (i = 0; i < mp->natoms; i++) {
5261                         ap = ATOM_AT_INDEX(mp->atoms, i);
5262                         if (ap->frames != NULL) {
5263                                 memmove(p, ap->frames, sizeof(Vector) * ap->nframes);
5264                                 p += sizeof(Vector) * ap->nframes;
5265                         }
5266                 }
5267                 len_all += len;
5268         }
5269         
5270         /*  Array of connects  */
5271         if (nconnects > 0) {
5272                 len = 8 + sizeof(Int) + sizeof(Int) * nconnects;
5273                 ptr = (char *)realloc(ptr, len_all + len);
5274                 if (ptr == NULL)
5275                         goto out_of_memory;
5276                 p = ptr + len_all;
5277                 memmove(p, "EXTCON\0\0", 8);
5278                 *((Int *)(p + 8)) = sizeof(Int) * nconnects;
5279                 p += 8 + sizeof(Int);
5280                 for (i = 0; i < mp->natoms; i++) {
5281                         ap = ATOM_AT_INDEX(mp->atoms, i);
5282                         if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5283                                 memmove(p, ap->connect.u.ptr, sizeof(Int) * ap->connect.count);
5284                                 p += sizeof(Int) * ap->connect.count;
5285                         }
5286                 }
5287                 len_all += len;
5288         }
5289         
5290         /*  Bonds, angles, dihedrals, impropers  */
5291         if (mp->nbonds > 0) {
5292                 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nbonds;
5293                 ptr = (char *)realloc(ptr, len_all + len);
5294                 if (ptr == NULL)
5295                         goto out_of_memory;
5296                 p = ptr + len_all;
5297                 memmove(p, "BOND\0\0\0\0", 8);
5298                 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nbonds;
5299                 p += 8 + sizeof(Int);
5300                 memmove(p, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
5301                 len_all += len;
5302         }
5303         if (mp->nangles > 0) {
5304                 len = 8 + sizeof(Int) + sizeof(Int) * 3 * mp->nangles;
5305                 ptr = (char *)realloc(ptr, len_all + len);
5306                 if (ptr == NULL)
5307                         goto out_of_memory;
5308                 p = ptr + len_all;
5309                 memmove(p, "ANGLE\0\0\0", 8);
5310                 *((Int *)(p + 8)) = sizeof(Int) * 3 * mp->nangles;
5311                 p += 8 + sizeof(Int);
5312                 memmove(p, mp->angles, sizeof(Int) * 3 * mp->nangles);
5313                 len_all += len;
5314         }
5315         if (mp->ndihedrals > 0) {
5316                 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->ndihedrals;
5317                 ptr = (char *)realloc(ptr, len_all + len);
5318                 if (ptr == NULL)
5319                         goto out_of_memory;
5320                 p = ptr + len_all;
5321                 memmove(p, "DIHED\0\0\0", 8);
5322                 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->ndihedrals;
5323                 p += 8 + sizeof(Int);
5324                 memmove(p, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
5325                 len_all += len;
5326         }
5327         if (mp->nimpropers > 0) {
5328                 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->nimpropers;
5329                 ptr = (char *)realloc(ptr, len_all + len);
5330                 if (ptr == NULL)
5331                         goto out_of_memory;
5332                 p = ptr + len_all;
5333                 memmove(p, "IMPROP\0\0", 8);
5334                 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->nimpropers;
5335                 p += 8 + sizeof(Int);
5336                 memmove(p, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
5337                 len_all += len;
5338         }
5339         
5340         /*  Array of residues  */
5341         if (mp->nresidues > 0) {
5342                 len = 8 + sizeof(Int) + 4 * mp->nresidues;
5343                 ptr = (char *)realloc(ptr, len_all + len);
5344                 if (ptr == NULL)
5345                         goto out_of_memory;
5346                 p = ptr + len_all;
5347                 memmove(p, "RESIDUE\0", 8);
5348                 *((Int *)(p + 8)) = 4 * mp->nresidues;
5349                 p += 8 + sizeof(Int);
5350                 memmove(p, mp->residues, 4 * mp->nresidues);
5351                 len_all += len;
5352         }
5353
5354         /*  Unit cell  */
5355         if (mp->cell != NULL) {
5356                 len = 8 + sizeof(Int) + sizeof(XtalCell);
5357                 ptr = (char *)realloc(ptr, len_all + len);
5358                 if (ptr == NULL)
5359                         goto out_of_memory;
5360                 p = ptr + len_all;
5361                 memmove(p, "CELL\0\0\0\0", 8);
5362                 *((Int *)(p + 8)) = sizeof(XtalCell);
5363                 p += 8 + sizeof(Int);
5364                 memmove(p, mp->cell, sizeof(XtalCell));
5365                 len_all += len;
5366         }
5367         
5368         /*  Symmetry operations  */
5369         if (mp->nsyms > 0) {
5370                 len = 8 + sizeof(Int) + sizeof(Transform) * mp->nsyms;
5371                 ptr = (char *)realloc(ptr, len_all + len);
5372                 if (ptr == NULL)
5373                         goto out_of_memory;
5374                 p = ptr + len_all;
5375                 memmove(p, "SYMOP\0\0\0", 8);
5376                 *((Int *)(p + 8)) = sizeof(Transform) * mp->nsyms;
5377                 p += 8 + sizeof(Int);
5378                 memmove(p, mp->syms, sizeof(Transform) * mp->nsyms);
5379                 len_all += len;
5380         }
5381         
5382         /*  Pi-anchors  */
5383         if (nanchors > 0) {
5384                 /*  Estimate the necessary storage first  */
5385                 /*  One entry consists of { atom_index (Int), number_of_connects (Int), connects (Int's), weights (Double's) }  */
5386                 len = 8 + sizeof(Int);
5387                 for (i = 0; i < mp->natoms; i++) {
5388                         ap = ATOM_AT_INDEX(mp->atoms, i);
5389                         if (ap->anchor != NULL)
5390                                 len += sizeof(Int) * 2 + (sizeof(Int) + sizeof(Double)) * ap->anchor->connect.count;
5391                 }
5392                 ptr = (char *)realloc(ptr, len_all + len);
5393                 if (ptr == NULL)
5394                         goto out_of_memory;
5395                 p = ptr + len_all;
5396                 memmove(p, "ANCHOR\0\0", 8);
5397                 *((Int *)(p + 8)) = len - (8 + sizeof(Int));
5398                 p += 8 + sizeof(Int);
5399                 for (i = 0; i < mp->natoms; i++) {
5400                         Int count, *ip;
5401                         ap = ATOM_AT_INDEX(mp->atoms, i);
5402                         if (ap->anchor != NULL) {
5403                                 count = ap->anchor->connect.count;
5404                                 *((Int *)p) = i;
5405                                 *((Int *)(p + sizeof(Int))) = count;
5406                                 p += sizeof(Int) * 2;
5407                                 ip = AtomConnectData(&(ap->anchor->connect));
5408                                 memmove(p, ip, sizeof(Int) * count);
5409                                 p += sizeof(Int) * count;
5410                                 memmove(p, ap->anchor->coeffs, sizeof(Double) * count);
5411                                 p += sizeof(Double) * count;
5412                         }
5413                 }
5414                 len_all += len;
5415         }
5416         
5417         /*  Parameters  */
5418         if (mp->par != NULL) {
5419                 int type;
5420                 for (type = kFirstParType; type <= kLastParType; type++) {
5421                         const char *parname;
5422                         Int parsize, parcount;
5423                         void *parptr;
5424                         switch (type) {
5425                                 case kBondParType:
5426                                         parname = "BONDPAR\0";
5427                                         parsize = sizeof(BondPar);
5428                                         parcount = mp->par->nbondPars;
5429                                         parptr = mp->par->bondPars;
5430                                         break;
5431                                 case kAngleParType:
5432                                         parname = "ANGPAR\0\0";
5433                                         parsize = sizeof(AnglePar);
5434                                         parcount = mp->par->nanglePars;
5435                                         parptr = mp->par->anglePars;
5436                                         break;
5437                                 case kDihedralParType:
5438                                         parname = "DIHEPAR\0";
5439                                         parsize = sizeof(TorsionPar);
5440                                         parcount = mp->par->ndihedralPars;
5441                                         parptr = mp->par->dihedralPars;
5442                                         break;
5443                                 case kImproperParType:
5444                                         parname = "IMPRPAR\0";
5445                                         parsize = sizeof(TorsionPar);
5446                                         parcount = mp->par->nimproperPars;
5447                                         parptr = mp->par->improperPars;
5448                                         break;
5449                                 case kVdwParType:
5450                                         parname = "VDWPAR\0\0";
5451                                         parsize = sizeof(VdwPar);
5452                                         parcount = mp->par->nvdwPars;
5453                                         parptr = mp->par->vdwPars;
5454                                         break;
5455                                 case kVdwPairParType:
5456                                         parname = "VDWPPAR\0";
5457                                         parsize = sizeof(VdwPairPar);
5458                                         parcount = mp->par->nvdwpPars;
5459                                         parptr = mp->par->vdwpPars;
5460                                         break;
5461                                 case kVdwCutoffParType:
5462                                         parname = "VCUTPAR\0";
5463                                         parsize = sizeof(VdwCutoffPar);
5464                                         parcount = mp->par->nvdwCutoffPars;
5465                                         parptr = mp->par->vdwCutoffPars;
5466                                         break;
5467                                 default:
5468                                         continue;
5469                         }
5470                         if (parcount > 0) {
5471                                 len = 8 + sizeof(Int) + parsize * parcount;
5472                                 ptr = (char *)realloc(ptr, len_all + len);
5473                                 if (ptr == NULL)
5474                                         goto out_of_memory;
5475                                 p = ptr + len_all;
5476                                 memmove(p, parname, 8);
5477                                 *((Int *)(p + 8)) = parsize * parcount;
5478                                 p += 8 + sizeof(Int);
5479                                 memmove(p, parptr, parsize * parcount);
5480                                 len_all += len;
5481                         }
5482                 }
5483         }
5484         
5485         /*  Time stamp  */
5486         {
5487                 time_t tm = time(NULL);
5488                 len = 8 + sizeof(Int) + sizeof(Int);
5489                 ptr = (char *)realloc(ptr, len_all + len);
5490                 if (ptr == NULL)
5491                         goto out_of_memory;
5492                 p = ptr + len_all;
5493                 memmove(p, "TIME\0\0\0\0", 8);
5494                 *((Int *)(p + 8)) = sizeof(Int);
5495                 p += 8 + sizeof(Int);
5496                 *((Int *)p) = (Int)tm;
5497                 len_all += len;
5498                 if (timep != NULL)
5499                         *timep = (Int)tm;
5500         }
5501         
5502         if (outLength != NULL)
5503                 *outLength = len_all;
5504         return ptr;
5505
5506   out_of_memory:
5507     Panic("Low memory while serializing a molecule data");
5508         return NULL; /* Not reached */  
5509 }
5510
5511 #pragma mark ====== Search for bonds, angles, dihedrals, impropers ======
5512
5513 static IntGroup *
5514 sMoleculeSearchIncludingAtoms(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5515 {
5516         int i, j;
5517         Int *ip;
5518         IntGroup *gp = NULL;
5519         if (atomgroup == NULL)
5520                 return NULL;
5521         for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5522                 for (j = 0; j < nsize; j++) {
5523                         if (IntGroupLookup(atomgroup, ip[j], NULL) != 0) {
5524                                 if (gp == NULL)
5525                                         gp = IntGroupNew();
5526                                 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5527                                         Panic("Low memory while searching %s", msg);
5528                                 break;
5529                         }
5530                 }
5531         }
5532         return gp;
5533 }
5534
5535 IntGroup *
5536 MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5537 {
5538         if (mp == NULL)
5539                 return NULL;
5540         return sMoleculeSearchIncludingAtoms(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5541 }
5542
5543 IntGroup *
5544 MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5545 {
5546         if (mp == NULL)
5547                 return NULL;
5548         return sMoleculeSearchIncludingAtoms(mp->nangles, mp->angles, 3, atomgroup, "angles");
5549 }
5550
5551 IntGroup *
5552 MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5553 {
5554         if (mp == NULL)
5555                 return NULL;
5556         return sMoleculeSearchIncludingAtoms(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5557 }
5558
5559 IntGroup *
5560 MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5561 {
5562         if (mp == NULL)
5563                 return NULL;
5564         return sMoleculeSearchIncludingAtoms(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5565 }
5566
5567 static IntGroup *
5568 sMoleculeSearchAcrossAtomGroup(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5569 {
5570         int i, j;
5571         Int *ip;
5572         IntGroup *gp = NULL;
5573         if (atomgroup == NULL)
5574                 return NULL;
5575         for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5576                 int k = -1;
5577                 for (j = 0; j < nsize; j++) {
5578                         int kk;
5579                         kk = (IntGroupLookup(atomgroup, ip[j], NULL) != 0);
5580                         if (k < 0)
5581                                 k = kk;
5582                         else if (k != kk) {
5583                                 /*  This bond etc. crosses the atom group border  */
5584                                 if (gp == NULL)
5585                                         gp = IntGroupNew();
5586                                 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5587                                         Panic("Low memory while searching %s", msg);
5588                                 break;
5589                         }
5590                 }
5591         }
5592         return gp;
5593 }
5594
5595 IntGroup *
5596 MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5597 {
5598         if (mp == NULL)
5599                 return NULL;
5600         return sMoleculeSearchAcrossAtomGroup(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5601 }
5602
5603 IntGroup *
5604 MoleculeSearchAnglesAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5605 {
5606         if (mp == NULL)
5607                 return NULL;
5608         return sMoleculeSearchAcrossAtomGroup(mp->nangles, mp->angles, 3, atomgroup, "angles");
5609 }
5610
5611 IntGroup *
5612 MoleculeSearchDihedralsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5613 {
5614         if (mp == NULL)
5615                 return NULL;
5616         return sMoleculeSearchAcrossAtomGroup(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5617 }
5618
5619 IntGroup *
5620 MoleculeSearchImpropersAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5621 {
5622         if (mp == NULL)
5623                 return NULL;
5624         return sMoleculeSearchAcrossAtomGroup(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5625 }
5626
5627 /*  Subroutine for MoleculeGuessBonds. It can be also used independently, but make sure that *outNbonds/*outBonds 
5628     _correctly_ represents an array of two integers (as in mp->nbonds/mp->bonds).  */
5629 /*  Find atoms within the given "distance" from the given position.  */
5630 /*  If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5631  the threshold distance is given by the sum of van der Waals radii times limit, and radius is
5632  the van der Waals radius of the atom at the given position. */
5633 /*  Index is the atom index of the given atom; it is only used in returning the "bond" array
5634  to the caller. If index is negative, then (-index) is the real atom index, and
5635  only atoms with lower indices than (-index) are looked for.  */
5636 int
5637 MoleculeFindCloseAtoms(Molecule *mp, const Vector *vp, Double radius, Double limit, Int *outNbonds, Int **outBonds, Int index)
5638 {
5639         Int n2, j, nlim, newbond[2];
5640         Double a2, alim;
5641         Vector dr, r2;
5642         if (index < 0) {
5643                 nlim = index = -index;
5644         } else {
5645                 nlim = mp->natoms;
5646         }
5647         for (j = 0; j < nlim; j++) {
5648                 Atom *bp = ATOM_AT_INDEX(mp->atoms, j);
5649                 if (index == j)
5650                         continue;
5651                 n2 = bp->atomicNumber;
5652                 if (n2 >= 0 && n2 < gCountElementParameters)
5653                         a2 = gElementParameters[n2].radius;
5654                 else a2 = gElementParameters[6].radius;
5655                 r2 = bp->r;
5656                 VecSub(dr, *vp, r2);
5657                 if (limit < 0)
5658                         alim = -limit;
5659                 else
5660                         alim = limit * (radius + a2);
5661                 if (VecLength2(dr) < alim * alim) {
5662                         newbond[0] = index;
5663                         newbond[1] = j;
5664                         /*      MoleculeAddBonds(mp, 1, newbonds); */
5665                         AssignArray(outBonds, outNbonds, sizeof(Int) * 2, *outNbonds, newbond);
5666                 }
5667         }
5668         return 0;
5669 }
5670
5671 /*  Guess the bonds from the coordinates  */
5672 /*  If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5673     the threshold distance is given by the sum of van der Waals radii times limit.  */
5674 int
5675 MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds)
5676 {
5677         Int nbonds, *bonds, i, newbond[2];
5678         Atom *ap;
5679         nbonds = 0;
5680         bonds = NULL;
5681         for (i = 1, ap = ATOM_NEXT(mp->atoms); i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5682                 Vector r = ap->r;
5683                 Int an = ap->atomicNumber;
5684                 Double rad;
5685                 if (an >= 0 && an < gCountElementParameters)
5686                         rad = gElementParameters[an].radius;
5687                 else rad = gElementParameters[6].radius;
5688                 MoleculeFindCloseAtoms(mp, &r, rad, limit, &nbonds, &bonds, -i);
5689         }
5690         if (nbonds > 0) {
5691                 newbond[0] = kInvalidIndex;
5692                 newbond[1] = 0;
5693                 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5694                 nbonds--;
5695         }
5696         if (outNbonds != NULL)
5697                 *outNbonds = nbonds;
5698         if (outBonds != NULL)
5699                 *outBonds = bonds;
5700         return 0;
5701 }
5702
5703 /*  Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information  */
5704 int
5705 MoleculeRebuildTablesFromConnects(Molecule *mp)
5706 {
5707         int i, j, k, retval;
5708         Atom *ap;
5709         Int ibuf[6], *cp;
5710         
5711         __MoleculeLock(mp);
5712
5713         /*  Find bonds   */
5714         if (mp->nbonds == 0) {
5715                 for (i = 0; i < mp->natoms; i++) {
5716                         ap = ATOM_AT_INDEX(mp->atoms, i);
5717                         cp = AtomConnectData(&ap->connect);
5718                         for (j = 0; j < ap->connect.count; j++) {
5719                                 k = cp[j];
5720                                 if (i >= k)
5721                                         continue;
5722                                 ibuf[0] = i;
5723                                 ibuf[1] = k;
5724                                 /*  MoleculeAddBonds() should not be used, because it assumes connects[] and
5725                                     bonds are already in sync  */
5726                                 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
5727                         /*      retval = MoleculeAddBonds(mp, 1, ibuf);
5728                                 if (retval != 0)
5729                                         goto abort; */
5730                         }
5731                 }
5732         }
5733         
5734         /*  Find angles  */
5735         if (mp->nangles == 0) {
5736                 for (i = 0; i < mp->natoms; i++) {
5737                         ap = ATOM_AT_INDEX(mp->atoms, i);
5738                         cp = AtomConnectData(&ap->connect);
5739                         for (j = 0; j < ap->connect.count; j++) {
5740                                 for (k = j + 1; k < ap->connect.count; k++) {
5741                                         ibuf[0] = cp[j];
5742                                         ibuf[1] = i;
5743                                         ibuf[2] = cp[k];
5744                                         ibuf[3] = -1;
5745                                         retval = MoleculeAddAngles(mp, ibuf, NULL);
5746                                         if (retval < 0)
5747                                                 goto abort;
5748                                 }
5749                         }
5750                 }
5751         }
5752         
5753         /*  Find dihedrals  */
5754         if (mp->ndihedrals == 0) {
5755                 for (i = 0; i < mp->natoms; i++) {
5756                         ap = ATOM_AT_INDEX(mp->atoms, i);
5757                         cp = AtomConnectData(&ap->connect);
5758                         for (j = 0; j < ap->connect.count; j++) {
5759                                 int jj, kk, mm, m;
5760                                 Atom *apjj;
5761                                 Int *cpjj;
5762                                 jj = cp[j];
5763                                 if (i >= jj)
5764                                         continue;
5765                                 apjj = ATOM_AT_INDEX(mp->atoms, jj);
5766                                 cpjj = AtomConnectData(&apjj->connect);
5767                                 for (k = 0; k < ap->connect.count; k++) {
5768                                         if (k == j)
5769                                                 continue;
5770                                         kk = cp[k];
5771                                         for (m = 0; m < apjj->connect.count; m++) {
5772                                                 mm = cpjj[m];
5773                                                 if (mm == i || mm == kk)
5774                                                         continue;
5775                                                 ibuf[0] = kk;
5776                                                 ibuf[1] = i;
5777                                                 ibuf[2] = jj;
5778                                                 ibuf[3] = mm;
5779                                                 ibuf[4] = -1;
5780                                                 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
5781                                                 if (retval < 0)
5782                                                         goto abort;
5783                                         }
5784                                 }
5785                         }
5786                 }
5787         }
5788         
5789         /*  Find impropers  */
5790         if (mp->nimpropers == 0) {
5791                 for (i = 0; i < mp->natoms; i++) {
5792                         int i1, i2, i4, n1, n2, n4;
5793                         ap = ATOM_AT_INDEX(mp->atoms, i);
5794                         cp = AtomConnectData(&ap->connect);
5795                         for (i1 = 0; i1 < ap->connect.count; i1++) {
5796                                 n1 = cp[i1];
5797                                 for (i2 = i1 + 1; i2 < ap->connect.count; i2++) {
5798                                         n2 = cp[i2];
5799                                         for (i4 = i2 + 1; i4 < ap->connect.count; i4++) {
5800                                                 n4 = cp[i4];
5801                                                 ibuf[0] = n1;
5802                                                 ibuf[1] = n2;
5803                                                 ibuf[2] = i;
5804                                                 ibuf[3] = n4;
5805                                                 ibuf[4] = -1;
5806                                                 retval = MoleculeAddImpropers(mp, ibuf, NULL);
5807                                                 if (retval < 0)
5808                                                         goto abort;
5809                                         }
5810                                 }
5811                         }
5812                 }
5813         }
5814
5815         mp->needsMDRebuild = 1;
5816         __MoleculeUnlock(mp);
5817         return 0;
5818
5819   abort:
5820         __MoleculeUnlock(mp);
5821         return retval;
5822 }
5823
5824 int
5825 MoleculeAreAtomsConnected(Molecule *mol, int idx1, int idx2)
5826 {
5827         Atom *ap1 = ATOM_AT_INDEX(mol->atoms, idx1);
5828         if (AtomConnectHasEntry(&ap1->connect, idx2))
5829                 return 1;
5830         else if (ap1->anchor != NULL && AtomConnectHasEntry(&(ap1->anchor->connect), idx2))
5831                 return 2;
5832         else return 0;
5833 }
5834
5835 #pragma mark ====== Atom names ======
5836
5837 /*  Look for the n1-th atom in resno-th residue (n1 is 0-based)  */
5838 int
5839 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
5840 {
5841         int i, j, lasti;
5842         Atom *ap;
5843         if (mp == NULL || mp->natoms == 0)
5844                 return -1;
5845         lasti = -1;
5846         for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5847                 if (ap->resSeq == resno) {
5848                         lasti = i;
5849                         if (j++ == n1)
5850                                 return i;
5851                 }
5852         }
5853         if (n1 == -1)
5854                 return lasti; /* max */
5855         return -1;
5856 }
5857
5858 int
5859 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
5860 {
5861     int n;
5862     char *p;
5863         n = strtol(s, &p, 0);
5864         if (p > s) {
5865                 while (isspace(*p))
5866                         p++;
5867                 if (*p == 0) {
5868                   resName[0] = 0;
5869                   *resSeq = -1;
5870                   atomName[0] = 0;
5871                   return n;
5872                 }
5873         }
5874
5875         if ((p = strchr(s, ':')) != NULL) {
5876                 /*  Residue is specified  */
5877                 char *pp;
5878                 if ((pp = strchr(s, '.')) != NULL && pp < p) {
5879                         /*  Residue number is also specified  */
5880                         char *ppp;
5881                         n = pp - s;
5882                         *resSeq = strtol(pp + 1, &ppp, 0);
5883                         if (ppp == pp + 1)
5884                                 return -2;  /*  Bad format  */
5885                         while (isspace(*ppp))
5886                                 ppp++;
5887                         if (ppp != p)
5888                                 return -2;  /*  Bad format  */
5889                 } else {
5890                         *resSeq = -1;
5891                         /*  Check whether the "residue name" is an integer  */
5892                         n = strtol(s, &pp, 0);
5893                         if (pp > s) {
5894                                 while (isspace(*pp))
5895                                         pp++;
5896                                 if (*pp == 0 || *pp == ':') {
5897                                         *resSeq = n;
5898                                         if (*resSeq < 0)
5899                                                 return -2;  /*  Bad format  */
5900                                 }
5901                         }
5902                         if (*resSeq >= 0)
5903                                 n = 0;
5904                         else
5905                                 n = p - s;
5906                 }
5907                 if (n >= sizeof(resName))
5908                         n = sizeof(resName) - 1;
5909                 strncpy(resName, s, n);
5910                 resName[n] = 0;
5911                 p++;
5912         } else {
5913                 resName[0] = 0;
5914                 *resSeq = -1;
5915                 p = (char *)s;
5916         }
5917         strncpy(atomName, p, 4);
5918         atomName[4] = 0;
5919         return 0;
5920 }
5921
5922 /*  Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer  */
5923 int
5924 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
5925 {
5926         char resName[6];
5927         int resSeq, n;
5928         char atomName[6];
5929         /*      char *p; */
5930
5931         n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
5932         if (atomName[0] == 0) {
5933           if (n >= mp->natoms)
5934             n = -1;  /* Out of range */
5935           return n;
5936         }
5937         for (n = 0; n < mp->natoms; n++) {
5938                 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
5939                 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
5940                         && (resSeq < 0 || ap->resSeq == resSeq)
5941                         && strncmp(atomName, ap->aname, 4) == 0) {
5942                         return n;
5943                 }
5944         }
5945         return -1;  /*  Not found  */
5946 }
5947
5948 void
5949 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
5950 {
5951         Atom *ap;
5952         int n;
5953         if (mp == NULL || index < 0 || index >= mp->natoms) {
5954                 buf[0] = 0;
5955                 return;
5956         }
5957         ap = mp->atoms + index;
5958         if (ap->resSeq != 0) {
5959                 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
5960                 buf += n;
5961                 bufsize -= n;
5962         }
5963         snprintf(buf, bufsize, "%.4s", ap->aname);
5964 }
5965
5966 #pragma mark ====== Selection ======
5967
5968 static void
5969 sMoleculeNotifyChangeSelection(Molecule *mp)
5970 {
5971         /*  TODO: Finer control of notification types may be necessary  */
5972         MoleculeCallback_notifyModification(mp, 0);
5973 }
5974
5975 void
5976 MoleculeSetSelection(Molecule *mp, IntGroup *select)
5977 {
5978         if (mp == NULL)
5979                 return;
5980         if (select != NULL)
5981                 IntGroupRetain(select);
5982         if (mp->selection != NULL)
5983                 IntGroupRelease(mp->selection);
5984         mp->selection = select;
5985         sMoleculeNotifyChangeSelection(mp);
5986 }
5987
5988 IntGroup *
5989 MoleculeGetSelection(Molecule *mp)
5990 {
5991         if (mp == NULL)
5992                 return NULL;
5993         else return mp->selection;
5994 }
5995
5996 void
5997 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
5998 {
5999         if (mp->selection == NULL)
6000                 mp->selection = IntGroupNew();
6001         if (!extending)
6002                 IntGroupClear(mp->selection);
6003         IntGroupAdd(mp->selection, n1, 1);
6004         sMoleculeNotifyChangeSelection(mp);
6005 }
6006
6007 void
6008 MoleculeUnselectAtom(Molecule *mp, int n1)
6009 {
6010         if (mp->selection != NULL)
6011                 IntGroupRemove(mp->selection, n1, 1);
6012         sMoleculeNotifyChangeSelection(mp);
6013 }
6014
6015 void
6016 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
6017 {
6018         if (mp->selection == NULL)
6019                 mp->selection = IntGroupNew();
6020         IntGroupReverse(mp->selection, n1, 1);
6021         sMoleculeNotifyChangeSelection(mp);
6022 }
6023
6024 int
6025 MoleculeIsAtomSelected(Molecule *mp, int n1)
6026 {
6027         if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
6028                 return 1;
6029         else return 0;
6030 }
6031
6032 int
6033 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
6034 {
6035         if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
6036                 return 1;
6037         else return 0;
6038 }
6039
6040 IntGroup *
6041 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
6042 {
6043         int status;
6044         IntGroup *remain, *ig1, *ig2;
6045         ig1 = ig2 = NULL;
6046         remain = IntGroupNewFromIntGroup(remove);
6047         if (remain == NULL)
6048                 status = -1;
6049         else
6050                 status = IntGroupReverse(remain, 0, mp->natoms);
6051         if (status == 0) {
6052                 ig1 = IntGroupNew();
6053                 if (ig1 == NULL)
6054                         status = -1;
6055                 else
6056                         status = IntGroupDifference(selection, remove, ig1);
6057         }
6058         if (status == 0) {
6059                 ig2 = IntGroupNew();
6060                 if (ig2 == NULL)
6061                         status = -1;
6062                 else
6063                         status = IntGroupDeconvolute(ig1, remain, ig2);
6064         }
6065         if (remain != NULL)
6066                 IntGroupRelease(remain);
6067         if (ig1 != NULL)
6068                 IntGroupRelease(ig1);
6069         if (status == 0)
6070                 return ig2;
6071         else {
6072                 if (ig2 != NULL)
6073                         IntGroupRelease(ig2);
6074                 return NULL;
6075         }
6076 }
6077
6078 #pragma mark ====== Atom Equivalence ======
6079
6080 struct sEqList {
6081         int i[2];
6082         struct sEqList *next;
6083         struct sEqList *link;
6084 };
6085
6086 static struct sEqList *sListBase = NULL;
6087 static struct sEqList *sListFree = NULL;
6088
6089 static struct sEqList *
6090 sAllocEqList(void)
6091 {
6092         struct sEqList *lp;
6093         if (sListFree != NULL) {
6094                 lp = sListFree;
6095                 sListFree = lp->next;
6096                 lp->i[0] = lp->i[1] = 0;
6097                 lp->next = NULL;
6098                 return lp;
6099         }
6100         lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
6101         lp->link = sListBase;
6102         sListBase = lp;
6103         return lp;
6104 }
6105
6106 static void
6107 sFreeEqList(struct sEqList *list)
6108 {
6109         list->next = sListFree;
6110         sListFree = list;
6111 }
6112
6113 static void
6114 sDeallocateEqLists(void)
6115 {
6116         struct sEqList *lp, *lp_link;
6117         for (lp = sListBase; lp != NULL; lp = lp_link) {
6118                 lp_link = lp->link;
6119                 free(lp);
6120         }
6121         sListBase = NULL;
6122         sListFree = NULL;
6123 }
6124
6125 static int
6126 sExistInEqList(int i, int idx, struct sEqList *list)
6127 {
6128         while (list != NULL) {
6129                 if (list->i[idx] == i)
6130                         return 1;
6131                 list = list->next;
6132         }
6133         return 0;
6134 }
6135
6136 static struct sEqList *
6137 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
6138 {
6139         Atom *api, *apj;
6140         struct sEqList *list1, *list2;
6141         Int ii, jj, ni, nj, *cpi, *cpj;
6142         api = ATOM_AT_INDEX(mol->atoms, i);
6143         apj = ATOM_AT_INDEX(mol->atoms, j);
6144         if (api->atomicNumber != apj->atomicNumber)
6145                 return NULL;
6146         list1 = sAllocEqList();
6147         if (list1 == NULL)
6148                 return NULL;
6149         list1->i[0] = i;
6150         list1->i[1] = j;
6151         list1->next = list;
6152         if (i == j || (db[i] != NULL && db[i] == db[j]))
6153                 return list1;
6154         cpi = AtomConnectData(&api->connect);
6155         cpj = AtomConnectData(&apj->connect);
6156         for (ni = 0; ni < api->connect.count; ni++) {
6157                 ii = cpi[ni];
6158                 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
6159                         continue;
6160                 if (sExistInEqList(ii, 0, list1))
6161                         continue;
6162                 list2 = NULL;
6163                 for (nj = 0; nj < apj->connect.count; nj++) {
6164                         jj = cpj[nj];
6165                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6166                                 continue;
6167                         if (sExistInEqList(jj, 1, list1))
6168                                 continue;
6169                         list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
6170                         if (list2 != NULL)
6171                                 break;
6172                 }
6173                 if (list2 == NULL) {
6174                         sFreeEqList(list1);
6175                         return NULL;    /*  No equivalent to ii  */
6176                 }
6177                 list1 = list2;      /*  ii is OK, try next  */
6178         }
6179         return list1;
6180 }
6181
6182 int
6183 sDBInclude(Int *ip, int i)
6184 {
6185         int j;
6186         if (ip == NULL)
6187                 return -1;
6188         for (j = ip[0] - 1; j >= 0; j--) {
6189                 if (ip[j] == i)
6190                         return j;
6191         }
6192         return -1;
6193 }
6194
6195 Int *
6196 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
6197 {
6198         Int **db;  /*  List of equivalents for each atom  */
6199         Int *ip, *result;
6200         Atom *api, *apj, *apk;
6201         Int *cpi, *cpj, *ibuf, nibuf;
6202         int i, j, k, ii, jj, kk;
6203         if (mol == NULL || mol->natoms == 0)
6204                 return NULL;
6205         db = (Int **)calloc(sizeof(Int *), mol->natoms);
6206         ibuf = NULL;
6207         nibuf = 0;
6208
6209         /*  Find the equivalent univalent atoms  */
6210         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6211                 if (api->connect.count < 2)
6212                         continue;
6213                 cpi = AtomConnectData(&api->connect);
6214                 for (j = 0; j < api->connect.count; j++) {
6215                         Int n;
6216                         n = 0;
6217                         jj = cpi[j];
6218                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6219                                 continue;
6220                         AssignArray(&ibuf, &nibuf, sizeof(Int), n, &jj);
6221                         n++;
6222                         apj = ATOM_AT_INDEX(mol->atoms, jj);
6223                         if (apj->connect.count != 1 || db[jj] != NULL)
6224                                 continue;
6225                         cpj = AtomConnectData(&apj->connect);
6226                         for (k = j + 1; k < api->connect.count; k++) {
6227                                 kk = cpj[k];
6228                                 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
6229                                         continue;
6230                                 apk = ATOM_AT_INDEX(mol->atoms, kk);
6231                                 if (apk->connect.count != 1 || db[kk] != NULL)
6232                                         continue;
6233                                 if (apj->atomicNumber == apk->atomicNumber) {
6234                                         AssignArray(&ibuf, &nibuf, sizeof(Int), n, &kk);
6235                                         n++;
6236                                 }
6237                         }
6238                         if (n > 1) {
6239                                 ip = (Int *)calloc(sizeof(Int), n + 1);
6240                                 if (ip == NULL)
6241                                         return NULL;
6242                                 ip[0] = n;
6243                                 memmove(ip + 1, ibuf, sizeof(Int) * n);
6244                                 for (k = 0; k < n; k++)
6245                                         db[ip[k + 1]] = ip;
6246                         }
6247                 }
6248         }
6249         if (ibuf != NULL) {
6250                 free(ibuf);
6251                 ibuf = NULL;
6252         }
6253         
6254         /*  Try matching (i,j) pair  */
6255         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6256                 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
6257                         continue;
6258                 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
6259                         struct sEqList *list;
6260                         if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
6261                                 continue;
6262                         if (api->atomicNumber != apj->atomicNumber)
6263                                 continue;  /*  Different elements do not match  */
6264                         if (db[i] != NULL && db[i] == db[j])
6265                                 continue;  /*  Already equivalent  */
6266                         list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
6267                         if (list == NULL)
6268                                 continue;  /*  (i,j) do not match  */
6269                         while (list != NULL) {
6270                                 ii = list->i[0];
6271                                 jj = list->i[1];
6272                                 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
6273                                         /*  Merge db[ii] and db[jj]  */
6274                                         k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
6275                                         ip = (Int *)calloc(sizeof(Int), k + 1);
6276                                         if (ip == NULL)
6277                                                 return NULL;  /*  Out of memory  */
6278                                         if (db[ii] == NULL) {
6279                                                 ip[1] = ii;
6280                                                 k = 2;
6281                                         } else {
6282                                                 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
6283                                                 k = db[ii][0] + 1;
6284                                         }
6285                                         if (db[jj] == NULL) {
6286                                                 ip[k++] = jj;
6287                                         } else {
6288                                                 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
6289                                                 k += db[jj][0];
6290                                         }
6291                                         ip[0] = k - 1;
6292                                         /*  Free old ones  */
6293                                         if (db[ii] != NULL)
6294                                                 free(db[ii]);
6295                                         if (db[jj] != NULL)
6296                                                 free(db[jj]);
6297                                         for (k = 0; k < ip[0]; k++)
6298                                                 db[ip[k + 1]] = ip;
6299                                         if (0) {
6300                                                 /*  For debug  */
6301                                                 printf("(%d,%d) matched: ", ii, jj);
6302                                                 for (k = 0; k < ip[0]; k++) {
6303                                                         printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
6304                                                 }
6305                                                 printf("]\n");
6306                                         }
6307                                 }
6308                                 list = list->next;
6309                         }
6310                 }
6311         }
6312         
6313         /*  Record the equivalent atoms with the lowest index for each atom  */
6314         result = (Int *)calloc(sizeof(Int), mol->natoms);
6315         for (i = 0; i < mol->natoms; i++)
6316                 result[i] = -1;
6317         for (i = 0; i < mol->natoms; i++) {
6318                 if (result[i] >= 0 || (ip = db[i]) == NULL)
6319                         continue;
6320                 k = mol->natoms;
6321                 for (j = 0; j < ip[0]; j++) {
6322                         kk = ip[j + 1];
6323                         if (kk < k)
6324                                 k = kk;
6325                 }
6326                 for (j = 0; j < ip[0]; j++) {
6327                         result[ip[j + 1]] = k;
6328                         db[ip[j + 1]] = NULL;
6329                 }
6330                 free(ip);
6331         }
6332         sDeallocateEqLists();
6333         return result;
6334 }
6335
6336 #pragma mark ====== Symmetry expansion ======
6337
6338 int
6339 MoleculeGetTransformForSymop(Molecule *mp, Symop symop, Transform *tf, int is_cartesian)
6340 {
6341         Transform t;
6342         if (mp == NULL || mp->cell == NULL)
6343                 return -1;
6344         if (symop.sym >= mp->nsyms && symop.sym != 0)
6345                 return -2;
6346         memmove(*tf, SYMMETRY_AT_INDEX(mp->syms, symop.sym), sizeof(Transform));
6347         (*tf)[9] += symop.dx;
6348         (*tf)[10] += symop.dy;
6349         (*tf)[11] += symop.dz;
6350         if (is_cartesian) {
6351                 TransformMul(t, *tf, mp->cell->rtr);
6352                 TransformMul(*tf, mp->cell->tr, t);
6353         }
6354         return 0;
6355 }
6356
6357 int
6358 MoleculeGetSymopForTransform(Molecule *mp, const Transform tf, Symop *symop, int is_cartesian)
6359 {
6360         Transform t;
6361         int i, j, n[3];
6362         if (mp == NULL || mp->cell == NULL)
6363                 return -1;
6364         if (is_cartesian) {
6365                 TransformMul(t, tf, mp->cell->tr);
6366                 TransformMul(t, mp->cell->rtr, t);
6367         } else {
6368                 memmove(t, tf, sizeof(Transform));
6369         }
6370         for (i = 0; i < mp->nsyms || i == 0; i++) {
6371                 Transform *tp = &(SYMMETRY_AT_INDEX(mp->syms, i));
6372                 for (j = 0; j < 9; j++) {
6373                         if (fabs((*tp)[j] - t[j]) > 1e-4)
6374                                 break;
6375                 }
6376                 if (j == 9) {
6377                         for (j = 9; j < 12; j++) {
6378                                 double f1 = t[j] - (*tp)[j];
6379                                 double f2 = floor(f1 + 0.5);
6380                                 if (fabs(f1 - f2) > 1e-4)
6381                                         break;
6382                                 n[j - 9] = f2;
6383                         }
6384                         if (j == 12) {
6385                                 /*  Found  */
6386                                 symop->sym = i;
6387                                 symop->dx = n[0];
6388                                 symop->dy = n[1];
6389                                 symop->dz = n[2];
6390                                 symop->alive = (SYMOP_ALIVE((*symop)) != 0);
6391                                 return 0;
6392                         }
6393                 }
6394         }
6395         return -3;  /*  Not found  */
6396 }
6397
6398 int
6399 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
6400 {
6401         if (mp == NULL)
6402                 return 1;
6403         if (symop.sym >= mp->nsyms && symop.sym != 0)
6404                 return 2;
6405         if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
6406                 TransformVec(vpout, mp->cell->rtr, vpin);
6407                 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpout);
6408                 vpout->x += symop.dx;
6409                 vpout->y += symop.dy;
6410                 vpout->z += symop.dz;
6411                 TransformVec(vpout, mp->cell->tr, vpout);
6412         } else {
6413                 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpin);
6414                 vpout->x += symop.dx;
6415                 vpout->y += symop.dy;
6416                 vpout->z += symop.dz;
6417         }
6418         return 0;
6419 }
6420
6421 /*  Add expanded atoms. Returns the number of newly created atoms.
6422         If indices is non-NULL, it should be an array of Int with at least 
6423         IntGroupGetCount(group) entries, and on return it contains the
6424     indices of the expanded atoms (may be existing atoms if the expanded
6425     atoms are already present)
6426     If allowOverlap is non-zero, then the new atom is created even when the
6427     coordinates coincide with the some other atom (special position) of the
6428     same element; otherwise, such atom will not be created and the existing
6429     atom is returned in indices[].  */
6430 int
6431 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group, Int *indices, Int allowOverlap)
6432 {
6433         int i, n, n0, n1, n2, base, count, *table;
6434         Atom *ap;
6435         IntGroupIterator iter;
6436         Transform tr, t1;
6437         Symop symop1;
6438         Atom *ap2;
6439         Vector nr, dr;
6440         
6441         if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
6442                 return -1;
6443         if (symop.sym != 0 && symop.sym >= mp->nsyms)
6444                 return -2;
6445
6446         /*  Create atoms, with avoiding duplicates  */
6447         n0 = n1 = mp->natoms;
6448         table = (int *)malloc(sizeof(int) * n0);
6449         if (table == NULL)
6450                 return -3;
6451         for (i = 0; i < n0; i++)
6452                 table[i] = -1;
6453         IntGroupIteratorInit(group, &iter);
6454         MoleculeGetTransformForSymop(mp, symop, &tr, 0);
6455         __MoleculeLock(mp);
6456         for (i = 0; i < count; i++) {
6457                 n = IntGroupIteratorNext(&iter);
6458                 ap = ATOM_AT_INDEX(mp->atoms, n);
6459                 if (SYMOP_ALIVE(ap->symop)) {
6460                         /*  Calculate the cumulative symop  */
6461                         Transform tr2;
6462                         MoleculeGetTransformForSymop(mp, ap->symop, &t1, 0);
6463                         TransformMul(tr2, tr, t1);
6464                         if (MoleculeGetSymopForTransform(mp, tr2, &symop1, 0) != 0) {
6465                                 if (indices != NULL)
6466                                         indices[i] = -1;
6467                                 continue;  /*  Skip this atom  */
6468                         }
6469                         base = ap->symbase;
6470                 } else {
6471                         symop1 = symop;
6472                         base = n;
6473                 }
6474
6475                 /*  Calculate the expande position  */
6476                 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
6477                 
6478                 /*  Is this expansion already present?  */
6479                 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
6480                         /*  Symmetry operation and the base atom are the same  */
6481                         if (ap2->symbase == base && SYMOP_EQUAL(symop1, ap2->symop))
6482                                 break;
6483                         /*  Atomic number and the position are the same  */
6484                         if (ap2->atomicNumber == ap->atomicNumber && allowOverlap == 0) {
6485                                 VecSub(dr, ap2->r, nr);
6486                                 if (VecLength2(dr) < 1e-6)
6487                                         break;
6488                         }
6489                 }
6490                 if (n2 < n0) {
6491                         /*  If yes, then skip it  */
6492                         if (indices != NULL)
6493                                 indices[i] = n2;
6494                         continue;
6495                 } else {
6496                         /*  Create a new atom  */
6497                         Atom newAtom;
6498                         AtomDuplicate(&newAtom, ap);
6499                         MoleculeCreateAnAtom(mp, &newAtom, -1);
6500                         AtomClean(&newAtom);
6501                         ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
6502                         ap2->r = nr;
6503                         ap2->symbase = base;
6504                         ap2->symop = symop1;
6505                         ap2->symop.alive = (symop1.dx != 0 || symop1.dy != 0 || symop1.dz != 0 || symop1.sym != 0);
6506                         table[n] = n1;  /*  The index of the new atom  */
6507                         MoleculeSetAnisoBySymop(mp, n1);  /*  Recalculate anisotropic parameters according to symop  */
6508                         if (indices != NULL)
6509                                 indices[i] = n1;
6510                         n1++;
6511                 }
6512         }
6513         IntGroupIteratorRelease(&iter);
6514
6515         /*  Create bonds  */
6516         for (i = n0; i < n1; i++) {
6517                 Int b[2], j;
6518                 ap = ATOM_AT_INDEX(mp->atoms, i);
6519                 if (SYMOP_ALIVE(ap->symop) && MoleculeGetTransformForSymop(mp, ap->symop, &tr, 1) == 0) {
6520                         /*  For each connected atom, look for the transformed atom  */
6521                         Int *cp;
6522                         ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6523                         cp = AtomConnectData(&ap2->connect);
6524                         n2 = ap2->connect.count;
6525                         for (n = 0; n < n2; n++) {
6526                                 Atom *apn = ATOM_AT_INDEX(mp->atoms, cp[n]);
6527                                 nr = apn->r;
6528                                 TransformVec(&nr, tr, &nr);
6529                                 /*  Look for the bonded atom transformed by ap->symop  */
6530                                 for (j = 0, ap2 = mp->atoms; j < mp->natoms; j++, ap2 = ATOM_NEXT(ap2)) {
6531                                         if (ap2->symbase == cp[n] && SYMOP_EQUAL(ap->symop, ap2->symop))
6532                                                 break;
6533                                         VecSub(dr, nr, ap2->r);
6534                                         if (ap2->atomicNumber == apn->atomicNumber && VecLength2(dr) < 1e-6)
6535                                                 break;
6536                                 }
6537                                 if (j < mp->natoms) {
6538                                         /*  Bond i-j is created  */
6539                                         b[0] = i;
6540                                         b[1] = j;
6541                                         if (MoleculeLookupBond(mp, b[0], b[1]) < 0)
6542                                                 MoleculeAddBonds(mp, 1, b, NULL, 1);
6543                                 }
6544                         }
6545                 }
6546         }
6547         mp->needsMDRebuild = 1;
6548         __MoleculeUnlock(mp);
6549         free(table);
6550         return n1 - n0;  /*  The number of added atoms  */
6551 }
6552
6553 /*  Recalculate the coordinates of symmetry expanded atoms.
6554     (Also recalculate the positions of pi-anchor atoms)
6555         Returns the number of affected atoms.
6556     If group is non-NULL, only the expanded atoms whose base atoms are in the
6557     given group are considered.
6558         If groupout and vpout are non-NULL, the indices of the affected atoms
6559         and the original positions are returned (for undo operation).
6560         The pointers returned in *groupout and *vpout must be released and 
6561         free()'ed by the caller  */
6562 int
6563 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
6564 {
6565         int i, count;
6566         Atom *ap, *bp;
6567         Vector nr, dr;
6568         IntGroup *ig = NULL;
6569         Vector *vp = NULL;
6570         
6571         if (mp == NULL || mp->natoms == 0)
6572                 return 0;
6573
6574         __MoleculeLock(mp);
6575         count = 0;
6576         if (mp->nsyms != 0) {
6577                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6578                         if (!SYMOP_ALIVE(ap->symop))
6579                                 continue;
6580                         if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
6581                                 continue;
6582                         bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6583                         MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
6584                         VecSub(dr, nr, ap->r);
6585                         if (VecLength2(dr) < 1e-20)
6586                                 continue;
6587                         if (groupout != NULL) {
6588                                 if (ig == NULL) {
6589                                         ig = IntGroupNew();
6590                                         vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
6591                                 }
6592                                 vp[count] = ap->r;
6593                                 IntGroupAdd(ig, i, 1);
6594                         }
6595                         ap->r = nr;
6596                         count++;
6597                 }
6598         }
6599         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6600                 Int *ip, j, n;
6601                 if (ap->anchor == NULL)
6602                         continue;
6603                 if (group != NULL) {
6604                         if (IntGroupLookup(group, i, NULL) == 0) {
6605                                 n = ap->anchor->connect.count;
6606                                 ip = AtomConnectData(&(ap->anchor->connect));
6607                                 for (j = 0; j < n; j++) {
6608                                         if (IntGroupLookup(group, ip[j], NULL) != 0)
6609                                                 break;
6610                                 }
6611                                 if (j == n)
6612                                         continue;  /*  This pi-anchor should not be modified  */
6613                         }
6614                 }
6615                 nr = ap->r;
6616                 MoleculeCalculatePiAnchorPosition(mp, i);
6617                 VecSub(dr, nr, ap->r);
6618                 if (VecLength2(dr) < 1e-20) {
6619                         ap->r = nr;  /*  No change  */
6620                         continue;
6621                 }
6622                 if (groupout != NULL) {
6623                         if (ig == NULL) {
6624                                 ig = IntGroupNew();
6625                                 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
6626                         }
6627                         vp[count] = nr;
6628                         IntGroupAdd(ig, i, 1);
6629                 }
6630                 count++;
6631         }
6632         mp->needsMDCopyCoordinates = 1;
6633         __MoleculeUnlock(mp);
6634
6635         if (count > 0) {
6636                 if (groupout != NULL && vpout != NULL) {
6637                         *groupout = ig;
6638                         *vpout = (Vector *)realloc(vp, sizeof(Vector) * count);
6639                 } else {
6640                         IntGroupRelease(ig);
6641                         free(vp);
6642                 }
6643         } else {
6644                 if (groupout != NULL && vpout != NULL) {
6645                         *groupout = NULL;
6646                         *vpout = NULL;
6647                 }
6648         }
6649         return count;
6650 }
6651
6652 #pragma mark ====== Show/hide atoms ======
6653
6654 static void
6655 sMoleculeNotifyChangeAppearance(Molecule *mp)
6656 {
6657         /*  TODO: Finer control of notification types may be necessary  */
6658         MoleculeCallback_notifyModification(mp, 0);
6659 }
6660
6661
6662 static void
6663 sMoleculeUnselectHiddenAtoms(Molecule *mp)
6664 {
6665         int i;
6666         if (mp == NULL || mp->selection == NULL)
6667                 return;
6668         for (i = 0; i < mp->natoms; i++) {
6669                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6670                 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
6671                         IntGroupRemove(mp->selection, i, 1);
6672         }
6673         sMoleculeNotifyChangeAppearance(mp);
6674 }
6675
6676 int
6677 MoleculeShowAllAtoms(Molecule *mp)
6678 {
6679         int i;
6680         if (mp == NULL)
6681                 return 0;
6682         for (i = 0; i < mp->natoms; i++) {
6683                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6684                 ap->exflags &= ~kAtomHiddenFlag;
6685         }
6686         sMoleculeNotifyChangeAppearance(mp);
6687         return 1;
6688 }
6689
6690 int
6691 MoleculeShowReverse(Molecule *mp)
6692 {
6693         int i;
6694         if (mp == NULL)
6695                 return 0;
6696         for (i = 0; i < mp->natoms; i++) {
6697                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6698                 ap->exflags ^= kAtomHiddenFlag;
6699         }
6700         sMoleculeUnselectHiddenAtoms(mp);
6701         sMoleculeNotifyChangeAppearance(mp);
6702         return 1;
6703 }
6704
6705 int
6706 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
6707 {
6708         int i;
6709         if (mp == NULL || ig == NULL)
6710                 return 0;
6711         for (i = 0; i < mp->natoms; i++) {
6712                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6713                 if (ap->exflags & kAtomHiddenFlag)
6714                         continue;  /*  Already hidden  */
6715                 if (IntGroupLookupPoint(ig, i) >= 0)
6716                         ap->exflags |= kAtomHiddenFlag;
6717         }
6718         sMoleculeUnselectHiddenAtoms(mp);
6719         sMoleculeNotifyChangeAppearance(mp);
6720         return 1;
6721 }
6722
6723 #pragma mark ====== Reversible Editing ======
6724
6725 /*
6726 static void
6727 sMoleculeNotifyModification(Molecule *mp)
6728 {
6729         **  TODO: Finer control of notification types may be necessary  **
6730         MoleculeCallback_notifyModification(mp, 0);
6731 }
6732 */
6733
6734 /*  Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup  */
6735 int
6736 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
6737 {
6738         int n1, n2, n3, i;
6739         if (where == NULL) {
6740                 /*  Append the new objects at the end  */
6741                 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
6742                 return 0;
6743         }
6744         n1 = IntGroupGetCount(where);  /*  Position to get new object  */
6745         n2 = nobjs;                    /*  Position to get old object  */
6746         n3 = n1 + n2;                  /*  Position to place new/old object  */
6747         for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
6748                 int start = IntGroupGetStartPoint(where, i);
6749                 int end = IntGroupGetEndPoint(where, i);
6750                 if (end < n3) {
6751                         /*  old[end-(n3-n2)..n2-1] is moved to old[end..n3-1]  */
6752                         memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
6753                         n2 = end - (n3 - n2);
6754                         n3 = end;
6755                 }
6756                 /*  new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1]  */
6757                 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
6758                 n3 -= end - start;
6759                 n1 -= end - start;
6760         }
6761         return 0;
6762 }
6763
6764 /*  Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
6765 int
6766 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6767 {
6768         int n1, n2, n3, start, end, i;
6769         if (objs == NULL || where == NULL)
6770                 return 1;  /*  Bad argument  */
6771         n1 = 0;  /*  Position to move remaining elements to */
6772         n2 = 0;  /*  Position to move remaining elements from  */
6773         n3 = 0;  /*  Position to move removed elements to  */
6774         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6775                 end = IntGroupGetEndPoint(where, i);
6776                 if (n2 < start) {
6777                         /*  Move (start - n2) elements from objs[n2] to objs[n1]  */
6778                         if (n1 < n2)
6779                                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
6780                         n1 += start - n2;
6781                         n2 = start;
6782                 }
6783                 /*  Move (end - start) elements from objs[n2] to clip[n3]  */
6784                 if (clip != NULL)
6785                         memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
6786                 n3 += (end - start);
6787                 n2 += (end - start);
6788         }
6789         /*  Move (nobjs - n2) elements from objs[n2] to objs[n1]  */
6790         if (nobjs > n2)
6791                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
6792         return 0;
6793 }
6794
6795 /*  Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
6796 int
6797 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6798 {
6799         int n1, start, end, i;
6800         if (objs == NULL || where == NULL)
6801                 return 1;  /*  Bad argument  */
6802         n1 = 0;  /*  Position to move removed elements to  */
6803         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6804                 end = IntGroupGetEndPoint(where, i);
6805                 /*  Copy (end - start) elements from objs[start] to clip[n1]  */
6806                 if (clip != NULL)
6807                         memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
6808                 n1 += (end - start);
6809         }
6810         return 0;
6811 }
6812
6813 /*  Create a new atom with no bonding information. ap must _not_ be inside the given molecule
6814    (Use AtomDuplicate() first) */
6815 int
6816 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
6817 {
6818     Atom *ap1, *api;
6819         int i;
6820         if (mp == NULL || ap == NULL || mp->noModifyTopology)
6821                 return -1;
6822         __MoleculeLock(mp);
6823         if (pos < 0 || pos >= mp->natoms)
6824                 pos = mp->natoms;
6825         ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
6826         if (ap1 == NULL)
6827                 goto error;  /*  Out of memory  */
6828         ap1 = ATOM_AT_INDEX(mp->atoms, pos);
6829         if (pos < mp->natoms - 1) {
6830                 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6831         }
6832         if (AtomDuplicate(ap1, ap) == NULL) {
6833                 /*  Cannot duplicate: restore the original state  */
6834                 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6835                 mp->natoms--;
6836                 goto error;
6837         }
6838         ap1->connect.count = 0;
6839         if (ap1->resSeq >= mp->nresidues)
6840                 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
6841         if (ap1->resName[0] == 0)
6842           strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
6843         if (ap1->segName[0] == 0)
6844           strncpy(ap1->segName, "MAIN", 4);
6845         if (pos < mp->natoms - 1) {
6846                 /*  Renumber the connect table, bonds, angles, etc. */
6847                 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
6848                         int j;
6849                         Int *cp;
6850                         cp = AtomConnectData(&api->connect);
6851                         for (j = 0; j < api->connect.count; j++) {
6852                                 if (cp[j] >= pos)
6853                                         cp[j]++;
6854                         }
6855                         if (api->anchor != NULL) {
6856                                 cp = AtomConnectData(&api->anchor->connect);
6857                                 for (j = 0; j < api->anchor->connect.count; j++) {
6858                                         if (cp[j] >= pos)
6859                                                 cp[j]++;
6860                                 }
6861                         }
6862                 }
6863                 for (i = 0; i < mp->nbonds * 2; i++) {
6864                         if (mp->bonds[i] >= pos)
6865                                 mp->bonds[i]++;
6866                 }
6867                 for (i = 0; i < mp->nangles * 3; i++) {
6868                         if (mp->angles[i] >= pos)
6869                                 mp->angles[i]++;
6870                 }
6871                 for (i = 0; i < mp->ndihedrals * 4; i++) {
6872                         if (mp->dihedrals[i] >= pos)
6873                                 mp->dihedrals[i]++;
6874                 }
6875                 for (i = 0; i < mp->nimpropers * 4; i++) {
6876                         if (mp->impropers[i] >= pos)
6877                                 mp->impropers[i]++;
6878                 }
6879         }
6880         mp->nframes = -1;  /*  Should be recalculated later  */
6881         MoleculeIncrementModifyCount(mp);
6882         mp->needsMDRebuild = 1;
6883         __MoleculeUnlock(mp);
6884         return pos;
6885 error:
6886         __MoleculeUnlock(mp);
6887         return -1;
6888 }
6889
6890 #if defined(DEBUG)
6891
6892 static int s_error_count;
6893
6894 static int
6895 s_fprintf(FILE *fp, const char *fmt, ...)
6896 {
6897         va_list va;
6898         va_start(va, fmt);
6899         s_error_count++;
6900         return vfprintf(fp, fmt, va);
6901 }
6902
6903 int
6904 MoleculeCheckSanity(Molecule *mol)
6905 {
6906         const char *fail = "Sanity check failure";
6907         Int i, j, *ip, c[4];
6908         Atom *ap;
6909         s_error_count = 0;
6910         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
6911                 if (ap->resSeq >= mol->nresidues)
6912                         s_fprintf(stderr, "%s: atom %d residue %d but nresidues %d\n", fail, i, ap->resSeq, mol->nresidues);
6913                 if (ap->type != 0 && ap->type < kAtomTypeMinimum)
6914                         s_fprintf(stderr, "%s: atom %d atom type %d less than minimum\n", fail, i, ap->type);
6915                 if (ap->atomicNumber < 0 || ap->atomicNumber > 113)
6916                         s_fprintf(stderr, "%s: atom %d atomic number %d\n", fail, i, ap->atomicNumber);
6917                 ip = AtomConnectData(&ap->connect);
6918                 for (j = 0; j < ap->connect.count; j++) {
6919                         if (ip[j] < 0 || ip[j] >= mol->natoms)
6920                                 s_fprintf(stderr, "%s: atom %d connect[%d] = %d out of range\n", fail, i, j, ip[j]);
6921                         if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[j])->connect), i) == 0)
6922                                 s_fprintf(stderr, "%s: atom %d has connect %d but atom %d has no connect %d\n", fail, i, ip[j], ip[j], i);
6923                 }
6924         }
6925         for (i = 0, ip = mol->bonds; i < mol->nbonds; i++, ip += 2) {
6926                 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms)
6927                         s_fprintf(stderr, "%s: bond %d %d-%d out of range\n", fail, i, ip[0], ip[1]);
6928                 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[0])->connect), ip[1]) == 0)
6929                         s_fprintf(stderr, "%s: bond %d %d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[0], ip[1]);
6930         }
6931         for (i = 0, ip = mol->angles; i < mol->nangles; i++, ip += 3) {
6932                 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms)
6933                         s_fprintf(stderr, "%s: angle %d %d-%d-%d out of range\n", fail, i, ip[0], ip[1], ip[2]);
6934                 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
6935                 if (c[0] == 0)
6936                         s_fprintf(stderr, "%s: angle %d %d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[1], ip[0]);
6937                 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
6938                 if (c[1] == 0)
6939                         s_fprintf(stderr, "%s: angle %d %d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[1], ip[2]);
6940                 if (c[0] == 2 && c[1] == 2)
6941                         s_fprintf(stderr, "%s: angle %d %d-%d-%d but bonds %d-%d and %d-%d are both virtual\n", fail, i, ip[0], ip[1], ip[2], ip[1], ip[0], ip[1], ip[2]);
6942         }
6943         for (i = 0, ip = mol->dihedrals; i < mol->ndihedrals; i++, ip += 4) {
6944                 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms || ip[3] < 0 || ip[3] >= mol->natoms)
6945                         s_fprintf(stderr, "%s: dihedral %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
6946                 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
6947                 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
6948                 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
6949                 if (c[0] == 0)
6950                         s_fprintf(stderr, "%s: dihedral %d %d-%d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[3], ip[1], ip[0]);
6951                 if (c[1] == 0)
6952                         s_fprintf(stderr, "%s: dihedral %d %d-%d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[3], ip[1], ip[2]);
6953                 if (c[2] == 0)
6954                         s_fprintf(stderr, "%s: dihedral %d %d-%d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[3], ip[2], ip[3]);
6955         }
6956         for (i = 0, ip = mol->impropers; i < mol->nimpropers; i++, ip += 4) {
6957                 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms || ip[3] < 0 || ip[3] >= mol->natoms)
6958                         s_fprintf(stderr, "%s: improper %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
6959                 c[0] = MoleculeAreAtomsConnected(mol, ip[2], ip[0]);
6960                 c[1] = MoleculeAreAtomsConnected(mol, ip[2], ip[1]);
6961                 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
6962                 if (c[0] == 0)
6963                         s_fprintf(stderr, "%s: improper %d %d-%d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[3], ip[2], ip[0]);
6964                 if (c[1] == 0)
6965                         s_fprintf(stderr, "%s: improper %d %d-%d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[3], ip[2], ip[1]);
6966                 if (c[2] == 0)
6967                         s_fprintf(stderr, "%s: improper %d %d-%d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[3], ip[2], ip[3]);
6968         }
6969         return s_error_count;
6970 }
6971 #endif
6972
6973 /*  Merge two molecules. We use this procedure for all add-atom operations.  */
6974 /*  resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
6975 /*  If nactions and actions are non-NULL, then the corresponding undo actions are created and returned. */
6976 /*  If forUndo is non-zero, then only the atoms are inserted; other information should be inserted
6977     separately by other undo actions.  */
6978 int
6979 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, Int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
6980 {
6981         Int nsrc, ndst;
6982         Int i, j, n1, n2, n3, n4, *cp;
6983         Int *new2old, *old2new;
6984         IntGroup *ig;
6985         Atom *ap;
6986         MolAction *act;
6987         
6988         if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
6989                 return 0;  /*  Do nothing  */
6990
6991         if (dst->noModifyTopology)
6992                 return 1;  /*  Prohibited operation  */
6993
6994         if (where != NULL && IntGroupGetCount(where) != src->natoms)
6995                 return 1;  /*  Bad parameter  */
6996
6997         if (nactions != NULL)
6998                 *nactions = 0;
6999         if (actions != NULL)
7000                 *actions = NULL;
7001         act = NULL;
7002
7003         __MoleculeLock(dst);
7004
7005         nsrc = src->natoms;
7006         ndst = dst->natoms;
7007         if (resSeqOffset < 0)
7008                 resSeqOffset = 0;
7009
7010         /*  Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
7011             and ndst..ndst+nsrc-1 are for atoms in src.  */ 
7012         new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
7013         if (new2old == NULL)
7014                 goto panic;
7015         old2new = new2old + ndst + nsrc;
7016         n1 = 0;  /*  dst index  */
7017         n2 = 0;  /*  src index  */
7018         n3 = 0;  /*  "merged" index  */
7019         i = 0;
7020         while (n1 < ndst || n2 < nsrc) {
7021                 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
7022                         n4 = ndst - n1;
7023                 else n4 -= n3;
7024                 /*  n4 elements from dst[n1] will go to merged[n3]  */
7025                 for (j = 0; j < n4; j++) {
7026                         old2new[n1 + j] = n3 + j;
7027                         new2old[n3 + j] = n1 + j;
7028                 }
7029                 n3 += n4;
7030                 n1 += n4;
7031                 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
7032                         n4 = nsrc - n2;
7033                 /*  n4 elements from src[n2] will go to merged[n3]  */
7034                 for (j = 0; j < n4; j++) {
7035                         old2new[ndst + n2 + j] = n3 + j;
7036                         new2old[n3 + j] = ndst + n2 + j;
7037                 }
7038                 n3 += n4;
7039                 n2 += n4;
7040                 i++;
7041         }
7042
7043         /*  Expand the destination array  */
7044         if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
7045                 goto panic;
7046
7047         /*  Move the atoms  */
7048         if (where == NULL) {
7049                 /*  Duplicate atoms to the end of the destination array  */
7050                 for (i = 0; i < nsrc; i++) {
7051                         ap = ATOM_AT_INDEX(dst->atoms, ndst + i);
7052                         if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7053                                 goto panic;
7054                         if (forUndo)  /*  For undo action, all bonds come from another undo action, so connection info are cleared */
7055                                 AtomConnectResize(&ap->connect, 0);
7056                 }
7057         } else {
7058                 /*  Duplicate to a temporary storage and then insert  */
7059                 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
7060                 if (tempatoms == NULL)
7061                         goto panic;
7062                 for (i = 0; i < nsrc; i++) {
7063                         ap = ATOM_AT_INDEX(tempatoms, i);
7064                         if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7065                                 goto panic;
7066                         if (forUndo)  /*  See above  */
7067                                 AtomConnectResize(&ap->connect, 0);                             
7068                 }
7069                 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
7070                         goto panic;
7071                 free(tempatoms);
7072         }
7073         dst->natoms = ndst + nsrc;
7074
7075         /*  Renumber the atom indices in connect[] and symbase, and modify the residue numbers  */
7076         for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7077                 if (new2old[i] < ndst) {
7078                         /*  This atom is from dst  */
7079                         n1 = 0;
7080                 } else {
7081                         /*  This atom is from src  */
7082                         n1 = ndst;  /*  Offset to the internal number  */
7083                         if (ap->resSeq != 0)
7084                                 ap->resSeq += resSeqOffset;  /*  Modify residue number  */
7085                 }
7086                 cp = AtomConnectData(&ap->connect);
7087                 for (j = 0; j < ap->connect.count; j++)
7088                         cp[j] = old2new[cp[j] + n1];
7089                 if (SYMOP_ALIVE(ap->symop))
7090                         ap->symbase = old2new[ap->symbase + n1];
7091                 if (ap->anchor != NULL) {
7092                         cp = AtomConnectData(&ap->anchor->connect);
7093                         for (j = 0; j < ap->anchor->connect.count; j++)
7094                                 cp[j] = old2new[cp[j] + n1];
7095                 }
7096         }
7097         
7098         /*  Move the bonds, angles, dihedrals, impropers  */
7099         for (i = 0; i < 4; i++) {
7100                 Int *nitems, *nitems_src;
7101                 Int **items, **items_src;
7102                 Int nsize;  /*  Number of Ints in one element  */
7103                 switch (i) {
7104                         case 0:
7105                                 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
7106                         case 1:
7107                                 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
7108                         case 2:
7109                                 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
7110                         case 3:
7111                                 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
7112                 }
7113                 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
7114                 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
7115                 if (forUndo) {
7116                         /*  During undo, no bonds etc. are copied from src; they will be taken care later
7117                             by undo actions  */
7118                         n1 = *nitems;
7119                         n2 = 0;
7120                 } else {
7121                         /*  Keep the old number of entries in dst, because it is updated by AssignArray()  */
7122                         n1 = *nitems;
7123                         /*  Also keep the old number of entries in src, in case src and dst point the same molecule  */
7124                         n2 = *nitems_src;
7125                         /*  Expand the array  */
7126                         if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
7127                                 goto panic;
7128                         /*  Copy the items  */
7129                         memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
7130                         if (i == 0) {
7131                                 /*  Copy the bond order info if present */
7132                                 Int nn1 = dst->nbondOrders;
7133                                 if (dst->bondOrders != NULL || src->bondOrders != NULL) {
7134                                         if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), dst->nbonds - 1, NULL) == NULL)
7135                                                 goto panic;
7136                                         memset(dst->bondOrders + nn1, 0, sizeof(Double) * (dst->nbonds - nn1));
7137                                         if (src->bondOrders != NULL)
7138                                                 memmove(dst->bondOrders + n1, src->bondOrders, sizeof(Double) * n2);
7139                                 }
7140                         }
7141                 }
7142                 /*  Renumber  */
7143                 for (j = 0; j < n1 * nsize; j++)
7144                         (*items)[j] = old2new[(*items)[j]];
7145                 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
7146                         (*items)[j] = old2new[(*items)[j] + ndst];
7147                 if (forUndo == 0 && actions != NULL) {
7148                         ig = IntGroupNewWithPoints(n1, n2, -1);
7149                         switch (i) {
7150                                 case 0: act = MolActionNew(gMolActionDeleteBonds, ig); break;
7151                                 case 1: act = MolActionNew(gMolActionDeleteAngles, ig); break;
7152                                 case 2: act = MolActionNew(gMolActionDeleteDihedrals, ig); break;
7153                                 case 3: act = MolActionNew(gMolActionDeleteImpropers, ig); break;
7154                         }
7155                         IntGroupRelease(ig);
7156                         AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7157                         act = NULL;
7158                 }
7159         }
7160         
7161         /*  Renumber existing parameters  */
7162         if (dst->par != NULL) {
7163                 int type;
7164                 for (type = kFirstParType; type <= kLastParType; type++) {
7165                         UnionPar *up1;
7166                         n1 = ParameterGetCountForType(dst->par, type);
7167                         for (i = 0; i < n1; i++) {
7168                                 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7169                                 ParameterRenumberAtoms(type, up1, ndst, old2new);
7170                         }
7171                 }
7172         }
7173
7174         /*  Merge parameters from src  */
7175         if (src->par != NULL && forUndo == 0) {
7176                 UnionPar *up1, *up2;
7177                 int type;
7178                 if (dst->par == NULL)
7179                         dst->par = ParameterNew();
7180                 else {
7181                         /*  Renumber existing parameters  */
7182                         for (type = kFirstParType; type <= kLastParType; type++) {
7183                                 n1 = ParameterGetCountForType(dst->par, type);
7184                                 for (i = 0; i < n1; i++) {
7185                                         up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7186                                         ParameterRenumberAtoms(type, up1, ndst, old2new);
7187                                 }
7188                         }
7189                 }
7190                 ig = IntGroupNew();
7191                 for (type = kFirstParType; type <= kLastParType; type++) {
7192                         n1 = ParameterGetCountForType(src->par, type);
7193                         n2 = ParameterGetCountForType(dst->par, type);
7194                         if (n1 == 0)
7195                                 continue;
7196                         /*  Determine which parameter should be copied from src to dst  */
7197                         for (i = 0; i < n1; i++) {
7198                                 UInt types[4];
7199                                 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
7200                                 n3 = ParameterGetAtomTypes(type, up1, types);
7201                                 for (j = 0; j < n3; j++) {
7202                                         /*  If it includes explicit atom index, then it should be copied  */
7203                                         if (types[j] < kAtomTypeMinimum) {
7204                                                 IntGroupAdd(ig, i, 1);
7205                                                 break;
7206                                         }
7207                                 }
7208                                 if (j == n3) {
7209                                         for (j = 0; j < n2; j++) {
7210                                                 up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
7211                                                 if (ParameterCompare(up1, up2, type))
7212                                                         break;
7213                                         }
7214                                         if (j >= n2)
7215                                                 /*  This is an unknown parameter; should be copied  */
7216                                                 IntGroupAdd(ig, i, 1);
7217                                 }
7218                         }
7219                         n1 = IntGroupGetCount(ig);
7220                         if (n1 == 0)
7221                                 continue;
7222                         up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
7223                         if (up1 == NULL)
7224                                 goto panic;
7225                         /*  Copy parameters and renumber indices if necessary  */
7226                         for (i = j = 0; i < n1; i++) {
7227                                 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
7228                                 if (up2 == NULL)
7229                                         continue;
7230                                 up1[j] = *up2;
7231                                 ParameterRenumberAtoms(type, up1 + j, nsrc, old2new + ndst);
7232                                 j++;
7233                         }
7234                         /*  Merge parameters  */
7235                         IntGroupClear(ig);
7236                         IntGroupAdd(ig, n2, j);
7237                         if (ParameterInsert(dst->par, type, up1, ig) < j)
7238                                 goto panic;
7239                         if (actions != NULL) {
7240                                 act = MolActionNew(gMolActionDeleteParameters, type, ig);
7241                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7242                                 act = NULL;
7243                         }
7244                         IntGroupClear(ig);
7245                         free(up1);
7246                 }
7247                 IntGroupRelease(ig);
7248         }
7249         
7250         /*  Copy the residues if necessary  */
7251         /*  src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
7252             However, 1+resSeqOffset should not overwrite the existing residue in dst;
7253                 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1].  */
7254         if (forUndo == 0) {
7255                 n1 = dst->nresidues;
7256                 if (1 + resSeqOffset < n1) {
7257                         n2 = n1;
7258                 } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
7259                 if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
7260                         if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
7261                                 goto panic;
7262                         memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
7263                         if (nactions != NULL) {
7264                                 act = MolActionNew(gMolActionChangeNumberOfResidues, n1);
7265                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7266                                 act = NULL;
7267                         }
7268                 }
7269         }
7270
7271         MoleculeCleanUpResidueTable(dst);
7272         
7273         free(new2old);
7274         dst->nframes = -1;  /*  Should be recalculated later  */
7275
7276         MoleculeIncrementModifyCount(dst);
7277         dst->needsMDRebuild = 1;
7278         __MoleculeUnlock(dst);
7279         return 0;
7280
7281   panic:
7282         __MoleculeUnlock(dst);
7283     Panic("Low memory while adding atoms");
7284         return 1;  /*  Not reached  */
7285 }
7286
7287 /*  Unmerge the molecule. If necessary, the undo actions are stored in nactions/actions array.
7288     (The nactions/actions array must be initialized by the caller)  */
7289 static int
7290 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag, Int *nactions, MolAction ***actions, Int forUndo)
7291 {
7292         Int nsrc, ndst, nsrcnew;
7293         Int i, j, n1, n2, n3, n4, *cp;
7294         Int *new2old, *old2new;
7295         IntGroup *move_g, *del_g, *remain_g, *dst_par_g, *remove_par_g;
7296         Molecule *dst;
7297         Atom *ap, *dst_ap;
7298         UnionPar *up;
7299         MolAction *act;
7300
7301         if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
7302                 /*  Do nothing  */
7303                 if (dstp != NULL)
7304                         *dstp = NULL;
7305                 return 0;
7306         }
7307         
7308         if (src->noModifyTopology && moveFlag)
7309                 return 1;  /*  Prohibit editing  */
7310
7311         if ((ndst = IntGroupGetCount(where)) > src->natoms)
7312                 return 1;  /*  Bad parameter  */
7313
7314         __MoleculeLock(src);
7315         
7316         act = NULL;
7317         
7318         nsrc = src->natoms;
7319         nsrcnew = nsrc - ndst;
7320         if (resSeqOffset < 0)
7321                 resSeqOffset = 0;
7322
7323         /*  Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
7324             and nsrcnew..nsrc-1 are for atoms moved into dst.  */ 
7325         new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
7326         if (new2old == NULL)
7327                 goto panic;
7328         old2new = new2old + nsrc;
7329         n1 = 0;  /*  src index  */
7330         n2 = 0;  /*  dst index  */
7331         n3 = 0;  /*  src index after "unmerge"  */
7332         i = 0;
7333         while (n1 < nsrc || n2 < ndst) {
7334                 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
7335                         n4 = nsrc - n1;
7336                 else n4 -= n1;
7337                 /*  n4 elements from src[n1] will go to unmerged[n3]  */
7338                 for (j = 0; j < n4; j++) {
7339                         old2new[n1 + j] = n3 + j;
7340                         new2old[n3 + j] = n1 + j;
7341                 }
7342                 n3 += n4;
7343                 n1 += n4;
7344                 if ((n4 = IntGroupGetInterval(where, i)) < 0)
7345                         n4 = nsrc - n1;
7346                 /*  n4 elements from src[n1] will go to dst[n2]  */
7347                 for (j = 0; j < n4; j++) {
7348                         old2new[n1 + j] = nsrcnew + n2 + j;
7349                         new2old[nsrcnew + n2 + j] = n1 + j;
7350                 }
7351                 n1 += n4;
7352                 n2 += n4;
7353                 i++;
7354         }
7355
7356         /*  Atoms to remain in the source group  */
7357         if (moveFlag) {
7358                 remain_g = IntGroupNewWithPoints(0, nsrc, -1);
7359                 IntGroupRemoveIntGroup(remain_g, where);
7360         } else remain_g = NULL;
7361         
7362         /*  Find parameters to be moved to the dst (dst_par_g), and to be removed from the src (remove_par_g) */
7363         if (src->par != NULL) {
7364                 dst_par_g = IntGroupNew();
7365                 if (moveFlag)
7366                         remove_par_g = IntGroupNew();
7367                 else remove_par_g = NULL;
7368                 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7369                         n2 = ParameterGetCountForType(src->par, n1);
7370                         if (n2 == 0)
7371                                 continue;
7372                         for (i = 0; i < n2; i++) {
7373                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7374                                 if (ParameterIsRelevantToAtomGroup(n1, up, src->atoms, where)) {
7375                                         /*  This parameter is to be copied to dst  */
7376                                         IntGroupAdd(dst_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7377                                 }
7378                                 if (moveFlag && !ParameterIsRelevantToAtomGroup(n1, up, src->atoms, remain_g)) {
7379                                         /*  This parameter is to be removed  */
7380                                         IntGroupAdd(remove_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7381                                 }
7382                         }
7383                 }
7384         } else dst_par_g = remove_par_g = NULL;
7385         
7386         /*  Pi anchors should be modified if the anchor and its component atoms become separated between
7387             src anc dst  */
7388         if (moveFlag) {
7389                 Int ibufsize, *ibuf, flag_i, flag_j;
7390                 ibufsize = 8;
7391                 ibuf = (Int *)malloc(sizeof(Int) * ibufsize);
7392                 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7393                         if (ap->anchor == NULL)
7394                                 continue;
7395                         flag_i = (old2new[i] < nsrcnew);
7396                         cp = AtomConnectData(&ap->anchor->connect);
7397                         for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7398                                 flag_j = (old2new[cp[j]] < nsrcnew);
7399                                 if (flag_i == flag_j) {
7400                                         if (n1 >= ibufsize) {
7401                                                 ibufsize += 8;
7402                                                 ibuf = (Int *)realloc(ibuf, sizeof(Int) * ibufsize);
7403                                         }
7404                                         ibuf[n1++] = cp[j];
7405                                 }
7406                         }
7407                         if (n1 < j) {
7408                                 /*  Need to modify the pi anchor list  */
7409                                 if (n1 <= 1)
7410                                         n1 = 0;
7411                                 MolActionCreateAndPerform(src, SCRIPT_ACTION("isI"), "set_atom_attr", i, "anchor_list", n1, ibuf);
7412                         }
7413                 }
7414         }
7415         
7416         /*  Make a new molecule  */
7417         if (dstp != NULL) {
7418                 dst = MoleculeNew();
7419                 if (dst == NULL)
7420                         goto panic;
7421                 /*  Expand the destination array  */
7422                 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
7423                         goto panic;
7424                 dst_ap = dst->atoms;
7425         } else {
7426                 dst = NULL;
7427                 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
7428                 if (dst_ap == NULL)
7429                         goto panic;
7430         }
7431         
7432         /*  Move the atoms  */
7433         if (moveFlag) {
7434                 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
7435                         goto panic;
7436                 src->natoms = nsrcnew;
7437                 if (dst == NULL) {
7438                         /*  The atom record must be deallocated correctly  */
7439                         for (i = 0; i < ndst; i++)
7440                                 AtomClean(ATOM_AT_INDEX(dst_ap, i));
7441                 }
7442         } else {
7443                 if (dst != NULL) {
7444                         for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
7445                                 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
7446                 }
7447         }
7448         
7449         if (dst == NULL) {
7450                 /*  The dummy destination array is no longer needed  */
7451                 free(dst_ap);
7452                 dst_ap = NULL;
7453         }
7454         
7455         /*  Renumber the atom indices in connect[] (src) */
7456         if (moveFlag) {
7457                 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7458                         cp = AtomConnectData(&ap->connect);
7459                         for (j = n1 = 0; j < ap->connect.count; j++) {
7460                                 n2 = old2new[cp[j]];
7461                                 if (n2 < nsrcnew)
7462                                         cp[n1++] = n2;
7463                         }
7464                         AtomConnectResize(&ap->connect, n1);
7465                         if (ap->anchor != NULL) {
7466                                 cp = AtomConnectData(&ap->anchor->connect);
7467                                 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7468                                         n2 = old2new[cp[j]];
7469                                         if (n2 < nsrcnew)
7470                                                 cp[n1++] = n2;
7471                                 }
7472                                 if (n1 != ap->anchor->connect.count) {
7473                                         /*  This should not happen!!  */
7474                                         AtomConnectResize(&ap->anchor->connect, n1);
7475                                         fprintf(stderr, "Internal error in sMoleculeUnmergeSub (line %d)\n", __LINE__);
7476                                         if (n1 == 0) {
7477                                                 free(ap->anchor->coeffs);
7478                                                 free(ap->anchor);
7479                                                 ap->anchor = NULL;
7480                                         }
7481                                 }
7482                         }
7483                 }
7484         }
7485         
7486         /*  Renumber the atom indices in connect[] (dst)  */
7487         if (dst != NULL) {
7488                 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7489                         if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
7490                                 ap->resSeq -= resSeqOffset;
7491                         else ap->resSeq = 0;
7492                         cp = AtomConnectData(&ap->connect);
7493                         for (j = n1 = 0; j < ap->connect.count; j++) {
7494                                 n2 = old2new[cp[j]] - nsrcnew;
7495                                 if (n2 >= 0)
7496                                         cp[n1++] = n2;
7497                         }
7498                         AtomConnectResize(&ap->connect, n1);
7499                         if (ap->anchor != NULL) {
7500                                 cp = AtomConnectData(&ap->anchor->connect);
7501                                 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7502                                         n2 = old2new[cp[j]] - nsrcnew;
7503                                         if (n2 >= 0)
7504                                                 cp[n1++] = n2;
7505                                 }
7506                                 if (n1 != ap->anchor->connect.count) {
7507                                         /*  This can happen, and the anchor info is silently modified  */
7508                                         if (n1 <= 1) {
7509                                                 AtomConnectResize(&ap->anchor->connect, 0);
7510                                                 free(ap->anchor->coeffs);
7511                                                 free(ap->anchor);
7512                                                 ap->anchor = NULL;
7513                                         } else {
7514                                                 Double d;
7515                                                 AtomConnectResize(&ap->anchor->connect, n1);
7516                                                 d = 0.0;
7517                                                 for (j = 0; j < n1; j++)
7518                                                         d += ap->anchor->coeffs[j];
7519                                                 for (j = 0; j < n1; j++)
7520                                                         ap->anchor->coeffs[j] /= d;
7521                                                 MoleculeCalculatePiAnchorPosition(dst, i);
7522                                         }
7523                                 }
7524                         }
7525                 }
7526         }
7527
7528         /*  Separate the bonds, angles, dihedrals, impropers  */
7529         /*  TODO: Improper torsions should also be copied!  */
7530         move_g = IntGroupNew();
7531         if (move_g == NULL)
7532                 goto panic;
7533         for (i = 3; i >= 0; i--) {
7534                 Int *nitems, *nitems_dst;
7535                 Int **items, **items_dst;
7536                 Int nsize;  /*  Number of Ints in one element  */
7537                 unsigned char *counts;
7538                 del_g = IntGroupNew();
7539                 switch (i) {
7540                         case 0:
7541                                 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
7542                         case 1:
7543                                 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
7544                         case 2:
7545                                 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
7546                         case 3:
7547                                 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
7548                         default:
7549                                 nitems = NULL; items = NULL; nsize = 0; break;  /*  Not reached  */
7550                 }
7551                 if (dst != NULL) {
7552                         nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
7553                         items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
7554                 } else {
7555                         nitems_dst = NULL;
7556                         items_dst = NULL;
7557                 }
7558                 counts = (unsigned char *)calloc(1, *nitems);
7559                 /*  Find the entries that should be moved to dst  */
7560                 n2 = 0;
7561                 for (j = 0; j < *nitems * nsize; j++) {
7562                         n1 = old2new[(*items)[j]];
7563                         if (n1 >= nsrcnew)
7564                                 counts[j / nsize]++; /* Count the atom belonging to dst */ 
7565                 }
7566                 for (j = n2 = n3 = 0; j < *nitems; j++) {
7567                         if (counts[j] > 0) {
7568                                 /*  Remove from src  */
7569                                 n2++;
7570                                 if (IntGroupAdd(del_g, j, 1) != 0)
7571                                         goto panic;
7572                                 if (counts[j] == nsize) {
7573                                         /*  Move to dst  */
7574                                         n3++;
7575                                         if (IntGroupAdd(move_g, j, 1) != 0)
7576                                                 goto panic;
7577                                 }
7578                         }
7579                 }
7580                 if (n2 > 0) {
7581                         /*  Expand the destination array  */
7582                         if (items_dst != NULL && n3 > 0) {
7583                                 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
7584                                         goto panic;
7585                                 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
7586                                         goto panic;
7587                                 if (i == 0 && src->bondOrders != NULL) {
7588                                         if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), n3 - 1, NULL) == NULL)
7589                                                 goto panic;
7590                                         if (sCopyElementsFromArrayAtPositions(src->bondOrders, src->nbondOrders, dst->bondOrders, sizeof(Double), move_g) != 0)
7591                                                 goto panic;
7592                                 }
7593                         }
7594                         /*  Remove from src  */
7595                         if (moveFlag && forUndo == 0) {
7596                                 if (nactions != NULL) {
7597                                         Int k, *ip;
7598                                         Double *dp;
7599                                         ip = (Int *)malloc(sizeof(Int) * nsize * n2);
7600                                         for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
7601                                                 memmove(ip + j * nsize, *items + k * nsize, sizeof(Int) * nsize);
7602                                         if (i == 0 && src->bondOrders != NULL) {
7603                                                 dp = (Double *)malloc(sizeof(Double) * n2);
7604                                                 for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
7605                                                         dp[j] = src->bondOrders[k];
7606                                         } else dp = NULL;
7607                                         switch (i) {
7608                                                 case 0:
7609                                                         act = MolActionNew(gMolActionAddBondsForUndo, n2 * nsize, ip, del_g); break;
7610                                                 case 1:
7611                                                         act = MolActionNew(gMolActionAddAngles, n2 * nsize, ip, del_g); break;
7612                                                 case 2:
7613                                                         act = MolActionNew(gMolActionAddDihedrals, n2 * nsize, ip, del_g); break;
7614                                                 case 3:
7615                                                         act = MolActionNew(gMolActionAddImpropers, n2 * nsize, ip, del_g); break;
7616                                         }
7617                                         if (act != NULL) {
7618                                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7619                                                 act = NULL;
7620                                         }
7621                                         free(ip);
7622                                         if (dp != NULL) {
7623                                                 act = MolActionNew(gMolActionAssignBondOrders, n2, dp, del_g);
7624                                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7625                                                 act = NULL;
7626                                                 free(dp);
7627                                         }
7628                                 }
7629                                 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
7630                                         goto panic;
7631                                 (*nitems) -= n2;
7632                         }
7633                 }
7634                 /*  Renumber the entries  */
7635                 if (moveFlag) {
7636                         for (j = 0; j < *nitems * nsize; j++) {
7637                                 (*items)[j] = old2new[(*items)[j]];
7638                         }
7639                 }
7640                 if (items_dst != NULL) {
7641                         for (j = 0; j < *nitems_dst * nsize; j++) {
7642                                 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
7643                         }
7644                 }
7645                 free(counts);
7646                 IntGroupClear(move_g);
7647                 IntGroupRelease(del_g);
7648         }
7649         IntGroupRelease(move_g);
7650         
7651         /*  Copy the residues  */
7652         if (dst != NULL) {
7653                 /*  src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset)  */
7654                 n1 = src->nresidues - resSeqOffset;  /*  This will be dst->nresidues (if >0)  */
7655                 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
7656                         goto panic;
7657                 if (n1 > 1) {
7658                         memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
7659                 }
7660         }
7661
7662         /*  Copy the parameters to dst */
7663         if (dst != NULL && dst_par_g != NULL && (n2 = IntGroupGetCount(dst_par_g)) > 0) {
7664                 IntGroup *dst_new_g = IntGroupNew();
7665                 Int dst_par_count[kLastParType - kFirstParType + 1];
7666                 if (dst_new_g == NULL)
7667                         goto panic;
7668                 for (i = 0; i <= kLastParType - kFirstParType; i++)
7669                         dst_par_count[i] = 0;
7670                 up = (UnionPar *)calloc(sizeof(UnionPar), n2);
7671                 if (up == NULL)
7672                         goto panic;
7673                 if (ParameterCopy(src->par, kFirstParType, up, dst_par_g) < n2)
7674                         goto panic;
7675                 /*  Renumber the explicit atom indices  */
7676                 for (i = 0; i < nsrc; i++)
7677                         old2new[i] -= nsrcnew;  /*  new indices for atoms in dst; otherwise negative numbers  */
7678                 for (i = 0; i < n2; i++) {
7679                         /*  Renumber the indices, and count the number of parameters for each type  */
7680                         n1 = kFirstParType + IntGroupGetNthPoint(dst_par_g, i) / kParameterIndexOffset;
7681                         dst_par_count[n1 - kFirstParType]++;
7682                         ParameterRenumberAtoms(n1, up + i, nsrc, old2new);
7683                 }
7684                 for (i = 0; i < nsrc; i++)
7685                         old2new[i] += nsrcnew;
7686                 if (dst->par == NULL)
7687                         dst->par = ParameterNew();
7688                 for (i = 0; i <= kLastParType - kFirstParType; i++) {
7689                         if (dst_par_count[i] > 0)
7690                                 IntGroupAdd(dst_new_g, i * kParameterIndexOffset, dst_par_count[i]);
7691                 }
7692                 if (ParameterInsert(dst->par, kFirstParType, up, dst_new_g) < n2)
7693                         goto panic;
7694                 free(up);
7695                 IntGroupRelease(dst_new_g);
7696         }
7697         IntGroupRelease(dst_par_g);
7698
7699         /*  Remove the unused parameter. Note: the parameters that are in remove_par_g and not in 
7700             dst_par_g will disappear. To support undo, these parameters should be taken care separately.  */
7701         if (forUndo == 0 && remove_par_g != NULL && (n2 = IntGroupGetCount(remove_par_g)) > 0) {
7702                 UnionPar *up = (UnionPar *)malloc(sizeof(UnionPar) * n2);
7703                 ParameterDelete(src->par, kFirstParType, up, remove_par_g);
7704                 if (nactions != NULL) {
7705                         act = MolActionNew(gMolActionAddParameters, kFirstParType, remove_par_g, n2, up);
7706                         AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7707                         act = NULL;
7708                 }
7709                 free(up);
7710         }
7711         IntGroupRelease(remove_par_g);
7712         
7713         /*  Renumber the parameter records remaining in the src  */
7714         if (moveFlag) {
7715                 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7716                         n2 = ParameterGetCountForType(src->par, n1);
7717                         for (i = 0; i < n2; i++) {
7718                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7719                                 ParameterRenumberAtoms(n1, up, nsrc, old2new);
7720                         }
7721                 }
7722         }
7723
7724         /*  Clean up  */
7725         IntGroupRelease(remain_g);
7726         MoleculeCleanUpResidueTable(src);
7727         if (dst != NULL)
7728                 MoleculeCleanUpResidueTable(dst);
7729         free(new2old);
7730
7731         src->nframes = -1;  /*  Should be recalculated later  */
7732         if (dst != NULL)
7733                 dst->nframes = -1;  /*  Should be recalculated later  */
7734
7735         
7736         if (dstp != NULL)
7737                 *dstp = dst;
7738
7739         MoleculeIncrementModifyCount(src);
7740         src->needsMDRebuild = 1;
7741         __MoleculeUnlock(src);
7742         
7743         return 0;
7744
7745   panic:
7746         __MoleculeUnlock(src);
7747 /*    Panic("Low memory while removing atoms"); */
7748         return -1;
7749 }
7750
7751 /*  Separate molecule into two parts. The atoms specified by 'where' are moved
7752     from src to a new molecule, which is returned as *dstp. Dstp can be NULL, 
7753         in which case the moved atoms are discarded.  */
7754 int
7755 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7756 {
7757         return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1, nactions, actions, forUndo);
7758 }
7759
7760 /*  Extract atoms from a given molecule into two parts. The atoms specified by 
7761         'where' are copied from src to a new molecule, which is returned as *dstp.
7762     If dummyFlag is non-zero, then the atoms that are not included in the group 
7763         but are connected to any atoms in the group are converted to "dummy" atoms 
7764         (i.e. with element "Du" and names beginning with an underscore) and included 
7765         in the new molecule object.  */
7766 int
7767 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
7768 {
7769         int retval;
7770
7771         /*  Extract the fragment  */
7772         retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0, NULL, NULL, 0);
7773         if (retval != 0)
7774                 return retval;
7775
7776         if (dummyFlag) {
7777
7778                 /*  Search bonds crossing the molecule border  */
7779                 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
7780                 if (ig != NULL) {
7781                         IntGroupIterator iter;
7782                         Int i, idx;
7783                         idx = 1;
7784                         IntGroupIteratorInit(ig, &iter);
7785                         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7786                                 /*  The atoms at the border  */
7787                                 Int n1, n2, nn[3];
7788                                 Atom a, *ap;
7789                                 n1 = src->bonds[i*2];
7790                                 n2 = src->bonds[i*2+1];
7791                                 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
7792                                         int w = n1;
7793                                         n1 = n2;
7794                                         n2 = w;
7795                                         if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
7796                                                 continue;  /*  Actually this is an internal error  */
7797                                 }
7798                                 /*  n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule  */
7799                                 /*  Create a new dummy atom with the same segment/residue info with n1
7800                                     and the same position as n2  */
7801                                 ap = ATOM_AT_INDEX(src->atoms, n1);
7802                                 memset(&a, 0, gSizeOfAtomRecord);
7803                                 a.segSeq = ap->segSeq;
7804                                 memmove(a.segName, ap->segName, 4);
7805                                 a.resSeq = ap->resSeq;
7806                                 memmove(a.resName, ap->resName, 4);
7807                                 ElementToString(0, a.element);  /*  "Du"  */
7808                                 snprintf(a.aname, 4, "_%d", idx++);
7809                                 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
7810                                 /*  Add the dummy atom to the new molecule; nn[1] is the index
7811                                     of the new dummy atom in the new molecule  */
7812                                 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
7813                                 /*  Connect nn1 and nn2  */
7814                                 nn[2] = kInvalidIndex;
7815                                 MoleculeAddBonds(*dstp, 1, nn, NULL, 1);
7816                         }
7817                         IntGroupIteratorRelease(&iter);
7818                         IntGroupRelease(ig);
7819                 }
7820         }
7821         
7822         return 0;
7823 }
7824
7825 int
7826 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds, IntGroup *where, Int autoGenerate)
7827 {
7828         Int nangles, ndihedrals;
7829         Int *angles, *dihedrals;
7830         Int i, j, k, kk, n1, n2, cn1, cn2;
7831         Int *cp1, *cp2;
7832         Int temp[4];
7833         Atom *ap1, *ap2, *ap3;
7834         
7835         if (mp == NULL || bonds == NULL || nbonds <= 0)
7836                 return 0;
7837         if (mp->noModifyTopology)
7838                 return -4;  /*  Prohibited operation  */
7839
7840         /*  Note: Duplicates and validity are not checked (the caller must do that)  */
7841
7842         __MoleculeLock(mp);
7843
7844         n1 = mp->nbonds;
7845         if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, n1 + nbonds - 1, NULL) == NULL
7846                 || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nbonds, sizeof(Int) * 2, where) != 0) {
7847                 __MoleculeUnlock(mp);
7848                 return -4;  /*  Out of memory  */
7849         }
7850         if (mp->bondOrders != NULL) {
7851                 /*  Expand the bond order info (all new entries are zero)  */
7852                 Double *dp = (Double *)calloc(sizeof(Double), nbonds);
7853                 if (dp == NULL)
7854                         return -4;
7855                 if (AssignArray(&(mp->bondOrders), &(mp->nbondOrders), sizeof(Double), n1 + nbonds - 1, NULL) == NULL
7856                         || sInsertElementsToArrayAtPositions(mp->bondOrders, n1, dp, nbonds, sizeof(Double), where) != 0) {
7857                         __MoleculeUnlock(mp);
7858                         free(dp);
7859                         return -4;
7860                 }
7861                 free(dp);
7862         }
7863         
7864         angles = dihedrals = NULL;
7865         nangles = ndihedrals = 0;
7866         
7867         /*  Add connects[], and angles/dihedrals (if autoGenerate is true)  */
7868         for (i = 0; i < nbonds; i++) {
7869                 
7870                 /*  One entry at time  */
7871                 /*  (Otherwise, duplicate entries of angles and dihedrals result)  */
7872                 n1 = bonds[i * 2];
7873                 n2 = bonds[i * 2 + 1];
7874                 
7875                 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
7876                 AtomConnectInsertEntry(&ap1->connect, -1, n2);
7877                 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
7878                 AtomConnectInsertEntry(&ap2->connect, -1, n1);
7879         
7880                 /*  Add angles and dihedrals  */
7881                 if (autoGenerate) {
7882                         AtomConnect *ac1, *ac2;
7883                         if (ap1->anchor == NULL || ap2->anchor == NULL) {
7884                                 /*  N1-N2-{XY} or N2-N1-{XY} angles (X: connected atom, Y: constitute atom of pi-anchor)  */
7885                                 for (j = 0; j < 4; j++) {
7886                                         switch (j) {
7887                                                 case 0: temp[0] = n1; temp[1] = n2; ac1 = &ap2->connect; break;  /* N1-N2-X */
7888                                                 case 1: if (ap2->anchor == NULL) continue; else ac1 = &ap2->anchor->connect; break; /* N1-N2-Y */
7889                                                 case 2: temp[0] = n2; temp[1] = n1; ac1 = &ap1->connect; break;  /* N2-N1-X */
7890                                                 case 3: if (ap1->anchor == NULL) continue; else ac1 = &ap1->anchor->connect; break; /* N2-N1-Y */
7891                                         }
7892                                         cp1 = AtomConnectData(ac1);
7893                                         cn1 = ac1->count;
7894                                         for (k = 0; k < cn1; k++) {
7895                                                 temp[2] = cp1[k];
7896                                                 if (temp[2] == temp[0])
7897                                                         continue;
7898                                                 ap3 = ATOM_AT_INDEX(mp->atoms, temp[2]);
7899                                                 if (ap3->anchor != NULL) {
7900                                                         /*  Avoid X-anchor-anchor angle (anchor-X-anchor is allowed)  */
7901                                                         if ((j < 2 && ap2->anchor != NULL) || (j >= 2 && ap1->anchor != NULL))
7902                                                                 continue;
7903                                                 }
7904                                                 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7905                                                         goto panic;
7906                                                 /*  Dihedrals N1-N2-X-{XY} or N2-N1-X-{XY}  */
7907                                                 if (j == 1 || j == 3)
7908                                                         continue;
7909                                                 cp2 = AtomConnectData(&ap3->connect);
7910                                                 for (kk = 0; kk < ap3->connect.count; kk++) {
7911                                                         temp[3] = cp2[kk];
7912                                                         if (temp[3] == temp[0] || temp[3] == temp[1])
7913                                                                 continue;
7914                                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7915                                                                 goto panic;
7916                                                 }
7917                                                 if (ap3->anchor != NULL) {
7918                                                         /*  N1-N2-X-Y or N2-N1-X-Y  */
7919                                                         /*  for Y, only the first constitute atom is considered  */
7920                                                         cp2 = AtomConnectData(&ap3->anchor->connect);
7921                                                         temp[3] = cp2[0];
7922                                                         if (temp[3] == temp[0] || temp[3] == temp[1])
7923                                                                 continue;
7924                                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7925                                                                 goto panic;
7926                                                 }
7927                                         }
7928                                 }
7929                         }
7930                         /*  X-N1-N2-X dihedrals  */
7931                         /*  Y-N1-N2-anchor is allowed, but the force may be zero if the angle N1-N2-anchor is */
7932                         /*  close to 180 deg (e.g. in ferrocene, C-anchor-Fe-anchor dihedral should be k=0)  */
7933                         if (ap1->anchor == NULL) {
7934                                 ac1 = &ap1->connect;
7935                                 cn1 = ac1->count;
7936                         } else {
7937                                 ac1 = &ap1->anchor->connect;
7938                                 cn1 = 1;  /*  Only the first constitute atom of pi-anchor is considered  */
7939                         }
7940                         if (ap2->anchor == NULL) {
7941                                 ac2 = &ap2->connect;
7942                                 cn2 = ac2->count;
7943                         } else {
7944                                 ac2 = &ap2->anchor->connect;
7945                                 cn2 = 1;  /*  Only the first constitute atom of pi-anchor is considered  */
7946                         }
7947                         temp[1] = n1;
7948                         temp[2] = n2;
7949                         cp1 = AtomConnectData(ac1);
7950                         cp2 = AtomConnectData(ac2);
7951                         for (j = 0; j < cn1; j++) {
7952                                 temp[0] = cp1[j];
7953                                 if (temp[0] == temp[2])
7954                                         continue;
7955                                 for (k = 0; k < cn2; k++) {
7956                                         temp[3] = cp2[k];
7957                                         if (temp[3] == temp[0] || temp[3] == temp[1])
7958                                                 continue;
7959                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7960                                                 goto panic;
7961                                 }
7962                         }
7963                 }
7964         }
7965         
7966         if (angles != NULL) {
7967                 temp[0] = kInvalidIndex;
7968                 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7969                         goto panic;
7970                 MoleculeAddAngles(mp, angles, NULL);
7971                 free(angles);
7972         }
7973         if (dihedrals != NULL) {
7974                 temp[0] = kInvalidIndex;
7975                 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7976                         goto panic;
7977                 MoleculeAddDihedrals(mp, dihedrals, NULL);
7978                 free(dihedrals);
7979         }
7980
7981         MoleculeIncrementModifyCount(mp);
7982         mp->needsMDRebuild = 1;
7983         __MoleculeUnlock(mp);
7984
7985         return nbonds;
7986
7987   panic:
7988         __MoleculeUnlock(mp);
7989         Panic("Low memory while adding bonds");
7990         return -1;  /*  Not reached  */
7991 }
7992
7993 /*  Delete bonds  */
7994 /*  The deleted angles and dihedrals are stored in outRemoval.  */
7995 /*  (*outRemoval) is an array of integers, containing:
7996       [0..na*3-1]: the angle indices
7997       [na*3..na*3+nd*4-1]: the dihedral indices
7998           [na*3+nd*4..na*3+nd*4+ni*4-1]: the improper indices
7999     *outRemovedPos is an intgroup denoting the positions of the removed angles/dihedrals/impropers.
8000           the angle indices are included as they are,
8001       the dihedral indices are offset by ATOMS_MAX_NUMBER,
8002       the improper indices are offset by ATOMS_MAX_NUMBER*2.
8003     Note: the removed bond indices are not returned, because the caller should already know them.  */
8004 int
8005 MoleculeDeleteBonds(Molecule *mp, Int *bonds, IntGroup *where, Int **outRemoved, IntGroup **outRemovedPos)
8006 {
8007         Int i, j, n1, n2, nw;
8008         Int *ip, *jp, na, nd, ni;
8009         IntGroup *ag, *dg, *ig;
8010         Atom *ap;
8011         IntGroupIterator iter;
8012
8013         if (mp == NULL)
8014                 return 0;
8015         if (mp->noModifyTopology)
8016                 return -4;  /*  Prohibited operation  */
8017
8018         __MoleculeLock(mp);
8019
8020         /*  Update connects[]  */
8021         IntGroupIteratorInit(where, &iter);
8022         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8023                 n1 = mp->bonds[i * 2];
8024                 n2 = mp->bonds[i * 2 + 1];
8025                 ap = ATOM_AT_INDEX(mp->atoms, n1);
8026                 ip = AtomConnectData(&ap->connect);
8027                 for (j = 0; j < ap->connect.count; j++) {
8028                         if (ip[j] == n2) {
8029                                 AtomConnectDeleteEntry(&ap->connect, j);
8030                                 break;
8031                         }
8032                 }
8033                 ap = ATOM_AT_INDEX(mp->atoms, n2);
8034                 ip = AtomConnectData(&ap->connect);
8035                 for (j = 0; j < ap->connect.count; j++) {
8036                         if (ip[j] == n1) {
8037                                 AtomConnectDeleteEntry(&ap->connect, j);
8038                                 break;
8039                         }
8040                 }
8041         }
8042         
8043         /*  Remove bonds, angles, dihedrals, impropers  */
8044         ag = IntGroupNew();
8045         dg = ig = NULL;
8046         na = nd = ni = 0;
8047         
8048         nw = IntGroupGetCount(where);
8049         jp = (Int *)malloc(sizeof(Int) * nw * 2);
8050         j = 0;
8051         IntGroupIteratorReset(&iter);
8052         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8053                 jp[j++] = mp->bonds[i * 2];
8054                 jp[j++] = mp->bonds[i * 2 + 1];
8055         }
8056         IntGroupIteratorRelease(&iter);
8057
8058         for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8059                 for (j = 0; j < nw; j++) {
8060                         n1 = jp[j * 2];
8061                         n2 = jp[j * 2 + 1];
8062                         if ((ip[0] == n1 && ip[1] == n2)
8063                                 || (ip[1] == n1 && ip[0] == n2)
8064                                 || (ip[1] == n1 && ip[2] == n2)
8065                                 || (ip[2] == n1 && ip[1] == n2)) {
8066                                 if (IntGroupAdd(ag, i, 1) != 0)
8067                                         goto panic;
8068                                 na++;
8069                                 break;
8070                         }
8071                 }
8072         }
8073         for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8074                 for (j = 0; j < nw; j++) {
8075                         n1 = jp[j * 2];
8076                         n2 = jp[j * 2 + 1];
8077                         if ((ip[0] == n1 && ip[1] == n2)
8078                          || (ip[1] == n1 && ip[0] == n2)
8079                          || (ip[1] == n1 && ip[2] == n2)
8080                          || (ip[2] == n1 && ip[1] == n2)
8081                          || (ip[2] == n1 && ip[3] == n2)
8082                          || (ip[3] == n1 && ip[2] == n2)) {
8083                                 if (dg == NULL)
8084                                         dg = IntGroupNew();
8085                                 if (IntGroupAdd(dg, i, 1) != 0)
8086                                         goto panic;
8087                                 nd++;
8088                                 break;
8089                         }
8090                 }
8091         }
8092         for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8093                 for (j = 0; j < nw; j++) {
8094                         n1 = jp[j * 2];
8095                         n2 = jp[j * 2 + 1];
8096                         if ((ip[0] == n1 && ip[2] == n2)
8097                          || (ip[1] == n1 && ip[2] == n2)
8098                          || (ip[3] == n1 && ip[2] == n2)
8099                          || (ip[0] == n2 && ip[2] == n1)
8100                          || (ip[1] == n2 && ip[2] == n1)
8101                          || (ip[3] == n2 && ip[2] == n1)) {
8102                                 if (ig == NULL)
8103                                         ig = IntGroupNew();
8104                                 if (IntGroupAdd(ig, i, 1) != 0)
8105                                         goto panic;
8106                                 ni++;
8107                                 break;
8108                         }
8109                 }
8110         }
8111         free(jp);
8112         
8113         if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, bonds, sizeof(Int) * 2, where) != 0)
8114                 goto panic;
8115         mp->nbonds -= IntGroupGetCount(where);
8116         if (mp->nbonds == 0) {
8117                 free(mp->bonds);
8118                 mp->bonds = NULL;
8119         }
8120         if (mp->bondOrders != NULL) {
8121                 if (sRemoveElementsFromArrayAtPositions(mp->bondOrders, mp->nbondOrders, NULL, sizeof(Double), where) != 0)
8122                         goto panic;
8123                 mp->nbondOrders -= IntGroupGetCount(where);
8124                 if (mp->nbondOrders == 0) {
8125                         free(mp->bondOrders);
8126                         mp->bondOrders = NULL;
8127                 }
8128         }
8129         if (na == 0 && nd == 0 && ni == 0)
8130                 ip = NULL;
8131         else
8132                 ip = (Int *)malloc(sizeof(Int) * (na * 3 + nd * 4 + ni * 4));
8133         if (na > 0)
8134                 MoleculeDeleteAngles(mp, ip, ag);
8135         if (nd > 0)
8136                 MoleculeDeleteDihedrals(mp, ip + na * 3, dg);
8137         if (ni > 0)
8138                 MoleculeDeleteImpropers(mp, ip + na * 3 + nd * 4, ig);
8139         if (ip != NULL) {
8140                 IntGroupOffset(dg, ATOMS_MAX_NUMBER);
8141                 IntGroupOffset(ig, ATOMS_MAX_NUMBER * 2);
8142                 IntGroupAddIntGroup(ag, dg);
8143                 IntGroupAddIntGroup(ag, ig);
8144                 IntGroupRelease(dg);
8145                 IntGroupRelease(ig);
8146         }
8147
8148         if (IntGroupGetCount(ag) == 0) {
8149                 IntGroupRelease(ag);
8150                 ag = NULL;
8151         }
8152         
8153         *outRemoved = ip;
8154         *outRemovedPos = ag;
8155
8156         MoleculeIncrementModifyCount(mp);
8157         mp->needsMDRebuild = 1;
8158         __MoleculeUnlock(mp);
8159
8160         return na * 3 + nd * 4 + ni * 4;
8161
8162   panic:
8163         __MoleculeUnlock(mp);
8164         Panic("Low memory while removing bonds");
8165         return -1;  /*  Not reached  */
8166 }
8167
8168 int
8169 MoleculeAssignBondOrders(Molecule *mp, const Double *orders, IntGroup *where)
8170 {
8171         Int i, j;
8172         IntGroupIterator iter;
8173         if (mp == NULL || orders == NULL || mp->nbonds == 0)
8174                 return 0;
8175         if (mp->noModifyTopology)
8176                 return -4;  /*  Prohibited operation  */
8177         if (mp->bondOrders == NULL) {
8178                 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbonds - 1, NULL);
8179                 memset(mp->bondOrders, 0, sizeof(Double) * mp->nbondOrders);
8180         }
8181         IntGroupIteratorInit(where, &iter);
8182         j = 0;
8183         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8184                 if (i >= mp->nbondOrders)
8185                         break;
8186                 mp->bondOrders[i] = orders[j++];
8187         }
8188         IntGroupIteratorRelease(&iter);
8189         return 0;
8190 }
8191
8192 int
8193 MoleculeGetBondOrders(Molecule *mp, Double *outOrders, IntGroup *where)
8194 {
8195         Int i, j;
8196         IntGroupIterator iter;
8197         if (mp == NULL || mp->nbonds == 0)
8198                 return 0;
8199         if (mp->bondOrders == NULL) {
8200                 /*  Returns all zero  */
8201                 i = IntGroupGetCount(where);
8202                 for (j = 0; j < i; j++)
8203                         outOrders[j] = 0.0;
8204         } else {
8205                 IntGroupIteratorInit(where, &iter);
8206                 j = 0;
8207                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8208                         if (i < mp->nbondOrders)
8209                                 outOrders[j] = mp->bondOrders[i];
8210                         else outOrders[j] = 0.0;
8211                         j++;
8212                 }
8213         }
8214         return 0;
8215 }
8216
8217 int
8218 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
8219 {
8220         int n1, nc;
8221         if (mp == NULL || angles == NULL)
8222                 return 0;
8223         if (mp->noModifyTopology)
8224                 return -4;  /*  Prohibited operation  */
8225
8226         __MoleculeLock(mp);
8227         if (where != NULL)
8228                 nc = IntGroupGetCount(where);
8229         else {
8230                 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
8231                         ;
8232                 nc = n1;
8233         }
8234         if (nc > 0) {
8235                 n1 = mp->nangles;
8236                 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
8237                         || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
8238                         __MoleculeUnlock(mp);
8239                         Panic("Low memory while adding angles");
8240                 }
8241         }
8242         mp->needsMDRebuild = 1;
8243         __MoleculeUnlock(mp);
8244         return nc;
8245 }
8246
8247 int
8248 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
8249 {
8250         int nc;
8251         if (mp == NULL || where == NULL)
8252                 return 0;
8253         if (mp->noModifyTopology)
8254                 return -4;  /*  Prohibited operation  */
8255         __MoleculeLock(mp);
8256         if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
8257                 __MoleculeUnlock(mp);
8258                 Panic("Low memory while adding angles");
8259         }
8260         mp->nangles -= (nc = IntGroupGetCount(where));
8261         if (mp->nangles == 0) {
8262                 free(mp->angles);
8263                 mp->angles = NULL;
8264         }
8265         mp->needsMDRebuild = 1;
8266         __MoleculeUnlock(mp);
8267         return nc;
8268 }
8269
8270 int
8271 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
8272 {
8273         int n1, nc;
8274         if (mp == NULL || dihedrals == NULL)
8275                 return 0;
8276         if (mp->noModifyTopology)
8277                 return -4;  /*  Prohibited operation  */
8278         if (where != NULL)
8279                 nc = IntGroupGetCount(where);
8280         else {
8281                 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
8282                         ;
8283                 nc = n1;
8284         }
8285         if (nc <= 0)
8286                 return 0;
8287         n1 = mp->ndihedrals;
8288         __MoleculeLock(mp);
8289         if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8290         || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
8291                 __MoleculeUnlock(mp);
8292                 Panic("Low memory while adding dihedrals");
8293         }
8294         mp->needsMDRebuild = 1;
8295         __MoleculeUnlock(mp);
8296         return nc;
8297 }
8298
8299 int
8300 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
8301 {       
8302         int nc;
8303         if (mp == NULL || where == NULL)
8304                 return 0;
8305         if (mp->noModifyTopology)
8306                 return -4;  /*  Prohibited operation  */
8307         __MoleculeLock(mp);
8308         if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
8309                 __MoleculeUnlock(mp);
8310                 Panic("Low memory while adding dihedrals");
8311         }
8312         mp->ndihedrals -= (nc = IntGroupGetCount(where));
8313         if (mp->ndihedrals == 0) {
8314                 free(mp->dihedrals);
8315                 mp->dihedrals = NULL;
8316         }
8317         mp->needsMDRebuild = 1;
8318         __MoleculeUnlock(mp);
8319         return nc;
8320 }
8321
8322 int
8323 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
8324 {
8325         int n1, nc;
8326         if (mp == NULL || impropers == NULL)
8327                 return 0;
8328         if (mp->noModifyTopology)
8329                 return -4;  /*  Prohibited operation  */
8330         if (where != NULL)
8331                 nc = IntGroupGetCount(where);
8332         else {
8333                 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
8334                         ;
8335                 nc = n1;
8336         }
8337         if (nc <= 0)
8338                 return 0;
8339         n1 = mp->nimpropers;
8340         __MoleculeLock(mp);
8341         if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8342         || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
8343                 __MoleculeUnlock(mp);
8344                 Panic("Low memory while adding impropers");
8345         }
8346         mp->needsMDRebuild = 1;
8347         __MoleculeUnlock(mp);
8348         return nc;
8349 }
8350
8351 int
8352 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
8353 {
8354         int nc;
8355         if (mp == NULL || where == NULL)
8356                 return 0;
8357         if (mp->noModifyTopology)
8358                 return -4;  /*  Prohibited operation  */
8359         __MoleculeLock(mp);
8360         if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
8361                 __MoleculeUnlock(mp);
8362                 Panic("Low memory while adding impropers");
8363         }
8364         mp->nimpropers -= (nc = IntGroupGetCount(where));
8365         if (mp->impropers == NULL) {
8366                 free(mp->impropers);
8367                 mp->impropers = NULL;
8368         }
8369         __MoleculeUnlock(mp);
8370         return nc;
8371 }
8372
8373 int
8374 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
8375 {
8376         Int i, *ip;
8377         if (mp == NULL || mp->bonds == NULL)
8378                 return -1;
8379         for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
8380                 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
8381                         return i;
8382         }
8383         return -1;
8384 }
8385
8386 int
8387 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
8388 {
8389         Int i, *ip;
8390         if (mp == NULL || mp->angles == NULL)
8391                 return -1;
8392         for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8393                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
8394                         (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
8395                         return i;
8396         }
8397         return -1;
8398 }
8399
8400 int
8401 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8402 {
8403         Int i, *ip;
8404         if (mp == NULL || mp->dihedrals == NULL)
8405                 return -1;
8406         for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8407                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
8408                         (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
8409                         return i;
8410         }
8411         return -1;
8412 }
8413
8414 int
8415 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8416 {
8417         Int i, *ip;
8418         if (mp == NULL || mp->impropers == NULL)
8419                 return -1;
8420         for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8421                 if (n3 != ip[2])
8422                         continue;
8423                 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
8424                         (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
8425                         (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
8426                         return i;
8427         }
8428         return -1;
8429 }
8430
8431 /*  Remove the bond at bondIndex and create two dummy atoms instead.
8432     The dummy atoms are placed at the end of atoms[], and the residue
8433         numbers are the same as the root atoms (i.e. the atoms to which
8434         the dummy atoms are connected). The indices are returned in
8435         dummyIndices[0,1].  */
8436 int
8437 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
8438 {
8439         Int roots[3], newBonds[5];
8440         Vector dr;
8441         Atom *rootp[2];
8442         Atom na[2], *nap;
8443         int i, natoms;
8444         IntGroup *ig;
8445         if (mp == NULL || mp->noModifyTopology)
8446                 return 0;
8447         if (bondIndex < 0 || bondIndex >= mp->nbonds)
8448                 return -1;
8449         roots[0] = mp->bonds[bondIndex * 2];
8450         roots[1] = mp->bonds[bondIndex * 2 + 1];
8451         roots[2] = kInvalidIndex;
8452         rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
8453         rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
8454         VecSub(dr, rootp[0]->r, rootp[1]->r);
8455         for (i = 0; i < 2; i++) {
8456                 float w;
8457                 nap = &na[i];
8458                 memmove(nap, rootp[i], sizeof(na));
8459                 nap->aname[0] = '*';
8460                 strcpy(nap->element, "Du");
8461                 nap->type = 0;
8462                 nap->charge = nap->weight = 0.0;
8463                 nap->atomicNumber = 0;
8464                 nap->connect.count = 0;
8465                 w = (i == 0 ? 0.4 : -0.4);
8466                 VecScaleInc(nap->r, dr, w);
8467                 VecZero(nap->v);
8468                 VecZero(nap->f);
8469                 nap->intCharge = 0;
8470                 nap->exflags = 0;
8471         }
8472
8473         /*  Expand atoms array and append the dummy atoms at the end  */
8474         __MoleculeLock(mp);
8475         natoms = mp->natoms;
8476         if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
8477                 goto panic;
8478         memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
8479         dummyIndices[0] = natoms;
8480         dummyIndices[1] = natoms + 1;
8481
8482         /*  Remove the old bond and create new bonds  */
8483         ig = IntGroupNewWithPoints(bondIndex, 1, -1);
8484         if (ig == NULL)
8485                 goto panic;
8486         MoleculeDeleteBonds(mp, NULL, ig, NULL, NULL);
8487         IntGroupRelease(ig);
8488         newBonds[0] = roots[0];
8489         newBonds[1] = dummyIndices[0];
8490         newBonds[2] = roots[1];
8491         newBonds[3] = dummyIndices[1];
8492         newBonds[4] = kInvalidIndex;
8493         
8494         i = (MoleculeAddBonds(mp, 2, newBonds, NULL, 1) < 0 ? -1 : 0);
8495         mp->needsMDRebuild = 1;
8496         __MoleculeUnlock(mp);
8497         return i;
8498
8499 panic:
8500         __MoleculeUnlock(mp);
8501         Panic("Low memory during creating dummy atoms");
8502         return 1;
8503 }
8504
8505 /*  Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
8506     a bond between the two root atoms. The value bondIndex is used as a
8507         hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
8508         the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
8509         is ignored and the new bond is stored at the end of bonds[].  */
8510 int
8511 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
8512 {
8513         return 0;
8514 }
8515
8516 /*
8517 Int
8518 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
8519 {
8520         Int n1, *np1;
8521         if (mol == NULL || mol->noModifyTopology)
8522                 return -1;
8523         n1 = mol->nangles;
8524         np1 = mol->angles;
8525         mol->nangles = 0;
8526         mol->angles = NULL;
8527         if (nangles > 0) {
8528                 __MoleculeLock(mol);
8529                 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
8530                 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
8531                 mol->needsMDRebuild = 1;
8532                 __MoleculeUnlock(mol);
8533         }
8534         *outAngles = np1;
8535         return n1;
8536 }
8537                                                 
8538 Int
8539 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
8540 {
8541         Int n1, *np1;
8542         if (mol == NULL || mol->noModifyTopology)
8543                 return -1;
8544         n1 = mol->ndihedrals;
8545         np1 = mol->dihedrals;
8546         mol->ndihedrals = 0;
8547         mol->dihedrals = NULL;
8548         if (ndihedrals > 0) {
8549                 __MoleculeLock(mol);
8550                 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
8551                 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
8552                 mol->needsMDRebuild = 1;
8553                 __MoleculeUnlock(mol);
8554         }
8555         *outDihedrals = np1;
8556         return n1;
8557 }
8558
8559 Int
8560 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
8561 {
8562         Int n1, *np1;
8563         if (mol == NULL || mol->noModifyTopology)
8564                 return -1;
8565         n1 = mol->nimpropers;
8566         np1 = mol->impropers;
8567         mol->nimpropers = 0;
8568         mol->impropers = NULL;
8569         if (nimpropers > 0) {
8570                 __MoleculeLock(mol);
8571                 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
8572                 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
8573                 mol->needsMDRebuild = 1;
8574                 __MoleculeUnlock(mol);
8575         }
8576         *outImpropers = np1;
8577         return n1;
8578 }
8579 */
8580
8581 Int
8582 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
8583 {
8584         Int i, j, k, *ip;
8585         Atom *ap;
8586         Int nangles;
8587         Int *angles;
8588         
8589         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8590                 return 0;  /*  molecule is empty  */
8591         if (mol->noModifyTopology)
8592                 return -1;
8593         nangles = 0;
8594         angles = NULL;
8595         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
8596                 Int *cp = AtomConnectData(&ap->connect);
8597                 if (ap->anchor != NULL)
8598                         continue;
8599                 for (j = 0; j < ap->connect.count; j++) {
8600                         Int j0 = cp[j];
8601                         if (ATOM_AT_INDEX(mol->atoms, j0)->anchor != NULL)
8602                                 continue;
8603                         for (k = j + 1; k < ap->connect.count; k++) {
8604                                 Int k0 = cp[k];
8605                                 if (ATOM_AT_INDEX(mol->atoms, k0)->anchor != NULL)
8606                                         continue;
8607                                 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
8608                                         ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
8609                                         ip[0] = j0;
8610                                         ip[1] = i;
8611                                         ip[2] = k0;
8612                                 }
8613                         }
8614                 }
8615         }
8616         if (nangles > 0) {
8617                 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
8618                 ip[0] = -1;
8619                 nangles--;
8620         }
8621         if (outAngles != NULL)
8622                 *outAngles = angles;
8623         return nangles;
8624 }
8625
8626 Int
8627 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
8628 {
8629         Int n1, n2, n3, n4, *ip, *cp2, *cp3;
8630         Atom *ap2, *ap3;
8631         Int ndihedrals;
8632         Int *dihedrals;
8633         
8634         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8635                 return 0;  /*  molecule is empty  */
8636         ndihedrals = 0;
8637         dihedrals = NULL;
8638         for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
8639                 Int i1, i3, i4, *ip;
8640                 if (ap2->anchor != NULL)
8641                         continue;
8642                 cp2 = AtomConnectData(&ap2->connect);
8643                 for (i3 = 0; i3 < ap2->connect.count; i3++) {
8644                         n3 = cp2[i3];
8645                         if (n2 > n3)
8646                                 continue;
8647                         ap3 = ATOM_AT_INDEX(mol->atoms, n3);
8648                         if (ap3->anchor != NULL)
8649                                 continue;
8650                         cp3 = AtomConnectData(&ap3->connect);
8651                         for (i1 = 0; i1 < ap2->connect.count; i1++) {
8652                                 n1 = cp2[i1];
8653                                 if (n1 == n3)
8654                                         continue;
8655                                 if (ATOM_AT_INDEX(mol->atoms, n1)->anchor != NULL)
8656                                         continue;
8657                                 for (i4 = 0; i4 < ap3->connect.count; i4++) {
8658                                         n4 = cp3[i4];
8659                                         if (n2 == n4 || n1 == n4)
8660                                                 continue;
8661                                         if (ATOM_AT_INDEX(mol->atoms, n4)->anchor != NULL)
8662                                                 continue;
8663                                         if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
8664                                                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
8665                                                 ip[0] = n1;
8666                                                 ip[1] = n2;
8667                                                 ip[2] = n3;
8668                                                 ip[3] = n4;
8669                                         }
8670                                 }
8671                         }
8672                 }
8673         }
8674         if (ndihedrals > 0) {
8675                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
8676                 ip[0] = -1;
8677                 ndihedrals--;
8678         }
8679         if (outDihedrals != NULL)
8680                 *outDihedrals = dihedrals;
8681         return ndihedrals;
8682 }
8683
8684 Int
8685 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
8686 {
8687         Int n1, n2, n3, n4, t1, t2, t3, t4, *ip, *cp;
8688         Parameter *par = mol->par;
8689         Atom *ap, *ap3;
8690         Int nimpropers;
8691         Int *impropers;
8692         
8693         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8694                 return 0;  /*  molecule is empty  */
8695         if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
8696                 return 0;  /*  No improper parameters are defined  */
8697         nimpropers = 0;
8698         impropers = NULL;
8699         ap = mol->atoms;
8700         for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
8701                 Int i1, i2, i4, found, *ip;
8702                 t3 = ap3->type;
8703                 cp = AtomConnectData(&ap3->connect);
8704                 for (i1 = 0; i1 < ap3->connect.count; i1++) {
8705                         n1 = cp[i1];
8706                         t1 = ATOM_AT_INDEX(ap, n1)->type;
8707                         for (i2 = i1 + 1; i2 < ap3->connect.count; i2++) {
8708                                 n2 = cp[i2];
8709                                 t2 = ATOM_AT_INDEX(ap, n2)->type;
8710                                 for (i4 = i2 + 1; i4 < ap3->connect.count; i4++) {
8711                                         n4 = cp[i4];
8712                                         t4 = ATOM_AT_INDEX(ap, n4)->type;
8713                                         found = 0;
8714                                         if (ParameterLookupImproperPar(par, t1, t2, t3, t4, n1, n2, n3, n4, 0) != NULL)
8715                                                 found = 1;
8716                                         else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, -1, -1, -1, -1, 0) != NULL)
8717                                                 found = 1;
8718                                         if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
8719                                                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8720                                                 ip[0] = n1;
8721                                                 ip[1] = n2;
8722                                                 ip[2] = n3;
8723                                                 ip[3] = n4;
8724                                         }
8725                                 }
8726                         }
8727                 }
8728         }
8729         if (nimpropers > 0) {
8730                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8731                 ip[0] = -1;
8732                 nimpropers--;
8733         }
8734         if (outImpropers != NULL)
8735                 *outImpropers = impropers;
8736         return nimpropers;
8737 }
8738
8739 #pragma mark ====== Residues ======
8740
8741 void
8742 MoleculeCleanUpResidueTable(Molecule *mp)
8743 {
8744         int i, maxres;
8745         Atom *ap;
8746         if (mp == NULL || mp->natoms == 0)
8747                 return;
8748         maxres = 0;
8749         __MoleculeLock(mp);
8750         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8751                 if (ap->resSeq >= maxres)
8752                         maxres = ap->resSeq + 1;
8753                 if (ap->resSeq < mp->nresidues) {
8754                         if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
8755                                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8756                 } else {
8757                         AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
8758                 }
8759         }
8760         if (maxres < mp->nresidues)
8761                 mp->nresidues = maxres;
8762         __MoleculeUnlock(mp);
8763 }
8764
8765 /*  Change the number of residues. If nresidues is greater than the current value,
8766     then the array mp->residues is expanded with null names. If nresidues is smaller
8767         than the current value, mp->nresidues is set to the smallest possible value
8768         that is no smaller than nresidues and larger than any of the resSeq values.  */
8769 int
8770 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
8771 {
8772         int n;
8773         if (mp == NULL)
8774                 return 0;
8775         if (mp->nresidues == nresidues)
8776                 return nresidues;
8777         else if (mp->nresidues < nresidues) {
8778                 __MoleculeLock(mp);
8779                 n = mp->nresidues;
8780                 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
8781                 while (n < nresidues)
8782                         mp->residues[n++][0] = 0;
8783                 __MoleculeUnlock(mp);
8784                 return nresidues;
8785         } else {
8786                 int i;
8787                 Atom *ap;
8788                 n = nresidues;
8789                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8790                         if (ap->resSeq >= n)
8791                                 n = ap->resSeq + 1;
8792                 }
8793                 mp->nresidues = n;
8794                 return n;
8795         }
8796 }
8797
8798 int
8799 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
8800 {
8801         IntGroupIterator iter;
8802         int withArray, resSeq, maxSeq;
8803         int i, j;
8804         Atom *ap;
8805         
8806         /*  If LSB of resSeqs is 1, then a constant value is used for all specified atoms  */
8807         if (((int)resSeqs & 1) == 0) {
8808                 withArray = 1;
8809                 resSeq = 0;
8810         } else {
8811                 withArray = 0;
8812                 resSeq = ((int)resSeqs - 1) / 2;
8813         }
8814         
8815         IntGroupIteratorInit(group, &iter);
8816
8817         /*  Change resSeqs  */
8818         maxSeq = 0;
8819         j = 0;
8820         __MoleculeLock(mp);
8821         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8822                 ap = ATOM_AT_INDEX(mp->atoms, i);
8823                 if (withArray)
8824                         resSeq = resSeqs[j++];
8825                 if (resSeq > maxSeq)
8826                         maxSeq = resSeq;
8827                 ap->resSeq = resSeq;
8828         }
8829         __MoleculeUnlock(mp);
8830
8831         /*  Expand array if necessary  */
8832         if (maxSeq >= mp->nresidues)
8833                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8834
8835         /*  Synchronize resName and residues[]  */
8836         __MoleculeLock(mp);
8837         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8838                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8839                         continue;
8840                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8841         }
8842         IntGroupIteratorRelease(&iter);
8843         __MoleculeUnlock(mp);
8844         
8845         MoleculeIncrementModifyCount(mp);
8846         
8847         return 0;
8848 }
8849
8850 int
8851 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
8852 {
8853         return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(resSeq * 2 + 1));
8854 }
8855
8856 /*  Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
8857     specifies the mp->nresidues after modifying the residue numbers.
8858         If all atoms are modified, then the table of residue names is also shifted. Otherwise,
8859         the table of residue names is not touched. */
8860 int
8861 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
8862 {
8863         int i, maxSeq, nmodatoms;
8864         Atom *ap;
8865         IntGroupIterator iter;
8866         IntGroupIteratorInit(group, &iter);
8867         maxSeq = 0;
8868         if (nresidues < 0)
8869                 nresidues = mp->nresidues;
8870         nmodatoms = 0;
8871         __MoleculeLock(mp);
8872         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8873                 ap = ATOM_AT_INDEX(mp->atoms, i);
8874                 ap->resSeq += offset;
8875                 if (ap->resSeq < 0) {
8876                         /*  Bad argument; undo change and returns this index + 1  */
8877                         int bad_index = i;
8878                         ap->resSeq -= offset;
8879                         while ((i = IntGroupIteratorLast(&iter)) >= 0) {
8880                                 ap = ATOM_AT_INDEX(mp->atoms, i);
8881                                 ap->resSeq -= offset;
8882                         }
8883                         IntGroupIteratorRelease(&iter);
8884                         return bad_index + 1;
8885                 }
8886                 if (ap->resSeq > maxSeq)
8887                         maxSeq = ap->resSeq;
8888                 nmodatoms++;
8889         }
8890         if (maxSeq >= nresidues)
8891                 nresidues = maxSeq + 1;
8892         if (offset < 0 && nmodatoms == mp->natoms) {
8893                 /*  Shift the residue names downward  */
8894                 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
8895         }
8896         __MoleculeUnlock(mp);
8897         MoleculeChangeNumberOfResidues(mp, nresidues);
8898         if (offset > 0 && nmodatoms == mp->natoms) {
8899                 /*  Shift the residue names upward  */
8900                 __MoleculeLock(mp);
8901                 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
8902                 __MoleculeUnlock(mp);
8903         }
8904         IntGroupIteratorRelease(&iter);
8905
8906         MoleculeIncrementModifyCount(mp);
8907         
8908         return 0;
8909 }
8910
8911 /*  Change residue names for the specified residue numbers. Names is an array of
8912     chars containing argc*4 characters, and every 4 characters represent a
8913         residue name; characters '\x01'-'\x1f' are converted to '\0', which allow 
8914         names to be handled as a C string.  */
8915 int
8916 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
8917 {
8918         int i, maxSeq;
8919         Atom *ap;
8920         maxSeq = 0;
8921         for (i = 0; i < argc; i++) {
8922                 if (maxSeq < resSeqs[i])
8923                         maxSeq = resSeqs[i];
8924         }
8925         if (maxSeq >= mp->nresidues)
8926                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8927         __MoleculeLock(mp);
8928         for (i = 0; i < argc; i++) {
8929                 char *p = mp->residues[resSeqs[i]];
8930                 int j;
8931                 strncpy(p, names + i * 4, 4);
8932                 for (j = 0; j < 4; j++) {
8933                         if (p[j] >= 0 && p[j] < 0x20)
8934                                 p[j] = 0;
8935                 }
8936         }
8937         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8938                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8939         }
8940         __MoleculeUnlock(mp);
8941
8942         MoleculeIncrementModifyCount(mp);
8943         
8944         return 0;
8945 }
8946
8947 /*  Returns the maximum residue number actually used  */
8948 int
8949 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
8950 {
8951         int i, maxSeq;
8952         Atom *ap;
8953         maxSeq = -1;
8954         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8955                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8956                         continue;
8957                 if (ap->resSeq > maxSeq)
8958                         maxSeq = ap->resSeq;
8959         }
8960         return maxSeq;
8961 }
8962
8963 /*  Returns the minimum residue number actually used  */
8964 int
8965 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
8966 {
8967         int i, minSeq;
8968         Atom *ap;
8969         minSeq = ATOMS_MAX_NUMBER;
8970         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8971                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8972                         continue;
8973                 if (ap->resSeq < minSeq)
8974                         minSeq = ap->resSeq;
8975         }
8976         return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
8977 }
8978
8979 #pragma mark ====== Sort by Residues ======
8980
8981 static int
8982 sAtomSortComparator(const void *a, const void *b)
8983 {
8984         const Atom *ap, *bp;
8985         ap = *((const Atom **)a);
8986         bp = *((const Atom **)b);
8987         if (ap->resSeq == bp->resSeq) {
8988                 /*  Retain the original order (i.e. atom with larger pointer address is larger)  */
8989                 if (ap < bp)
8990                         return -1;
8991                 else if (ap > bp)
8992                         return 1;
8993                 else return 0;
8994         } else {
8995                 /*  Compare the residue sequence. However, residue sequence 0 is always larger.  */
8996                 if (ap->resSeq == 0)
8997                         return 1;
8998                 else if (bp->resSeq == 0)
8999                         return -1;
9000                 else if (ap->resSeq < bp->resSeq)
9001                         return -1;
9002                 else if (ap->resSeq > bp->resSeq)
9003                         return 1;
9004                 else return 0;
9005         }
9006 }
9007
9008 static void
9009 sMoleculeReorder(Molecule *mp)
9010 {
9011         int i, res, prevRes;
9012         Atom **apArray;
9013         Int *old2new;
9014         Atom *newAtoms;
9015         if (mp == NULL || mp->natoms <= 1)
9016                 return;
9017
9018         /*  Sort the atoms, bonds, etc. */
9019         apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
9020         old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9021         newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9022         if (apArray == NULL || old2new == NULL || newAtoms == NULL)
9023                 Panic("Low memory during reordering atoms");
9024         for (i = 0; i < mp->natoms; i++)
9025                 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
9026
9027         /*  Sort the atoms. Note: apArray is an array of "Pointer to Atom"  */
9028         qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
9029         
9030         /*  Make a table of 'which atom becomes which'  */
9031         for (i = 0; i < mp->natoms; i++) {
9032                 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
9033                 old2new[j] = i;
9034         }
9035         
9036         /*  Renumber the bonds, etc.  */
9037         for (i = 0; i < mp->nbonds * 2; i++) {
9038                 mp->bonds[i] = old2new[mp->bonds[i]];
9039         }
9040         for (i = 0; i < mp->nangles * 3; i++) {
9041                 mp->angles[i] = old2new[mp->angles[i]];
9042         }
9043         for (i = 0; i < mp->ndihedrals * 4; i++) {
9044                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9045         }
9046         for (i = 0; i < mp->nimpropers * 4; i++) {
9047                 mp->impropers[i] = old2new[mp->impropers[i]];
9048         }
9049         for (i = 0; i < mp->natoms; i++) {
9050                 Int *ip, j;
9051                 ip = AtomConnectData(&(apArray[i]->connect));
9052                 for (j = 0; j < apArray[i]->connect.count; j++, ip++)
9053                         *ip = old2new[*ip];
9054         }
9055         
9056         /*  Renumber the residues so that the residue numbers are contiguous  */
9057         res = prevRes = 0;
9058         for (i = 0; i < mp->natoms; i++) {
9059                 if (apArray[i]->resSeq == 0)
9060                         break;
9061                 if (apArray[i]->resSeq != prevRes) {
9062                         res++;
9063                         prevRes = apArray[i]->resSeq;
9064                         if (prevRes != res) {
9065                                 strncpy(mp->residues[res], mp->residues[prevRes], 4);
9066                         }
9067                 }
9068                 apArray[i]->resSeq = res;
9069         }
9070         mp->nresidues = res + 1;
9071
9072         /*  Sort the atoms and copy back to atoms[] */
9073         for (i = 0; i < mp->natoms; i++) {
9074                 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
9075         }
9076         memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
9077         
9078         /*  Free the locally allocated storage  */
9079         free(newAtoms);
9080         free(old2new);
9081         free(apArray);
9082 }
9083
9084 /*  Renumber atoms  */
9085 int
9086 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
9087 {
9088         Int *old2new, i, j, retval;
9089         Atom *saveAtoms;
9090         if (mp == NULL)
9091                 return 0;
9092         if (mp->noModifyTopology)
9093                 return -1;
9094         if (old2new_out != NULL)
9095                 old2new = old2new_out;
9096         else
9097                 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9098         saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9099         if (old2new == NULL || saveAtoms == NULL)
9100                 Panic("Low memory during reordering atoms");
9101         memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
9102         __MoleculeLock(mp);
9103         for (i = 0; i < mp->natoms; i++)
9104                 old2new[i] = -1;
9105         for (i = 0; i < isize && i < mp->natoms; i++) {
9106                 j = new2old[i];
9107                 if (j < 0 || j >= mp->natoms) {
9108                         retval = 1; /* Out of range */
9109                         goto end;
9110                 }
9111                 if (old2new[j] != -1) {
9112                         retval = 2;  /*  Duplicate entry  */
9113                         goto end;
9114                 }
9115                 old2new[j] = i;
9116         }
9117         if (i < mp->natoms) {
9118                 for (j = 0; j < mp->natoms; j++) {
9119                         if (old2new[j] != -1)
9120                                 continue;
9121                         old2new[j] = i++;
9122                 }
9123         }
9124         if (i != mp->natoms) {
9125                 retval = 3;  /*  Internal inconsistency  */
9126                 goto end;
9127         }
9128
9129         /*  Renumber the bonds, etc.  */
9130         for (i = 0; i < mp->nbonds * 2; i++) {
9131                 mp->bonds[i] = old2new[mp->bonds[i]];
9132         }
9133         for (i = 0; i < mp->nangles * 3; i++) {
9134                 mp->angles[i] = old2new[mp->angles[i]];
9135         }
9136         for (i = 0; i < mp->ndihedrals * 4; i++) {
9137                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9138         }
9139         for (i = 0; i < mp->nimpropers * 4; i++) {
9140                 mp->impropers[i] = old2new[mp->impropers[i]];
9141         }
9142         /*  Renumber the connection table and pi anchor table  */
9143         for (i = 0; i < mp->natoms; i++) {
9144                 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
9145                 Int *ip = AtomConnectData(&ap->connect);
9146                 for (j = 0; j < ap->connect.count; j++, ip++)
9147                         *ip = old2new[*ip];
9148                 if (ap->anchor != NULL) {
9149                         ip = AtomConnectData(&ap->anchor->connect);
9150                         for (j = 0; j < ap->anchor->connect.count; j++, ip++)
9151                                 *ip = old2new[*ip];
9152                 }
9153         }
9154         
9155         if (mp->par != NULL) {
9156                 /*  Renumber the parameters  */
9157                 int n;
9158                 for (j = kFirstParType; j <= kLastParType; j++) {
9159                         n = ParameterGetCountForType(mp->par, j);
9160                         for (i = 0; i < n; i++) {
9161                                 UnionPar *up = ParameterGetUnionParFromTypeAndIndex(mp->par, j, i);
9162                                 if (up != NULL)
9163                                         ParameterRenumberAtoms(j, up, mp->natoms, old2new);
9164                         }
9165                 }
9166         }
9167         
9168         /*  Renumber the atoms  */
9169         for (i = 0; i < mp->natoms; i++)
9170                 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
9171         retval = 0;
9172         
9173         MoleculeIncrementModifyCount(mp);
9174         mp->needsMDRebuild = 1;
9175
9176   end:
9177         __MoleculeUnlock(mp);
9178         free(saveAtoms);
9179         if (old2new_out == NULL)
9180                 free(old2new);
9181         return retval;
9182 }
9183
9184 #pragma mark ====== Coordinate Transform ======
9185
9186 void
9187 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
9188 {
9189         int i;
9190         Atom *ap;
9191         Symop new_symop;
9192         Transform rtr, symtr;
9193         if (mp == NULL || tr == NULL)
9194                 return;
9195         TransformInvert(rtr, tr);
9196         __MoleculeLock(mp);
9197         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9198                 if (group == NULL || IntGroupLookup(group, i, NULL) != 0) {
9199                         TransformVec(&ap->r, tr, &ap->r);
9200                         if (!SYMOP_ALIVE(ap->symop))
9201                                 continue;
9202                         /*  Transform symop  */
9203                         if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9204                                 continue;
9205                         TransformMul(symtr, tr, symtr);
9206                         if (group == NULL || IntGroupLookup(group, ap->symbase, NULL) != 0)
9207                                 TransformMul(symtr, symtr, rtr);
9208                 } else {
9209                         if (!SYMOP_ALIVE(ap->symop))
9210                                 continue;
9211                         /*  Transform symop if the base atom is transformed  */
9212                         if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
9213                                 continue;
9214                         if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9215                                 continue;
9216                         TransformMul(symtr, symtr, rtr);
9217                 }
9218                 if (MoleculeGetSymopForTransform(mp, symtr, &new_symop, 1) != 0)
9219                         continue;
9220                 ap->symop = new_symop;
9221         }
9222         mp->needsMDCopyCoordinates = 1;
9223         __MoleculeUnlock(mp);
9224         sMoleculeNotifyChangeAppearance(mp);
9225 }
9226
9227 /*
9228 void
9229 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
9230 {
9231         int i;
9232         Atom *ap;
9233         if (mp == NULL || tr == NULL)
9234                 return;
9235         __MoleculeLock(mp);
9236         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9237                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9238                         continue;
9239                 TransformVec(&ap->r, tr, &ap->r);
9240         }
9241         mp->needsMDCopyCoordinates = 1;
9242         __MoleculeUnlock(mp);
9243         sMoleculeNotifyChangeAppearance(mp);
9244 }
9245 */
9246
9247 void
9248 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
9249 {
9250         Transform tr;
9251         if (mp == NULL || vp == NULL)
9252                 return;
9253         memset(tr, 0, sizeof(tr));
9254         tr[0] = tr[4] = tr[8] = 1.0;
9255         tr[9] = vp->x;
9256         tr[10] = vp->y;
9257         tr[11] = vp->z;
9258         MoleculeTransform(mp, tr, group);
9259 }
9260
9261 void
9262 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
9263 {
9264         Transform tr;
9265         TransformForRotation(tr, axis, angle, center);
9266         MoleculeTransform(mp, tr, group);
9267 }
9268
9269 int
9270 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
9271 {
9272         int i;
9273         Atom *ap;
9274         Double w;
9275         if (mp == NULL || center == NULL)
9276                 return 1;
9277         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9278                 return 2;   /*  Empty molecule  */
9279         w = 0.0;
9280         center->x = center->y = center->z = 0.0;
9281         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9282                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9283                         continue;
9284                 VecScaleInc(*center, ap->r, ap->weight);
9285                 w += ap->weight;
9286         }
9287         if (w < 1e-7)
9288                 return 3;  /*  Atomic weights are not defined?  */
9289         w = 1.0 / w;
9290         VecScaleSelf(*center, w);
9291         return 0;
9292 }
9293
9294 int
9295 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
9296 {
9297         Vector vmin, vmax;
9298         int i;
9299         Atom *ap;
9300         if (mp == NULL)
9301                 return 1;
9302         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9303                 return 2;   /*  Empty molecule  */
9304         vmin.x = vmin.y = vmin.z = 1e50;
9305         vmax.x = vmax.y = vmax.z = -1e50;
9306         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9307                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9308                         continue;
9309                 if (vmin.x > ap->r.x)
9310                         vmin.x = ap->r.x;
9311                 if (vmin.y > ap->r.y)
9312                         vmin.y = ap->r.y;
9313                 if (vmin.z > ap->r.z)
9314                         vmin.z = ap->r.z;
9315                 if (vmax.x < ap->r.x)
9316                         vmax.x = ap->r.x;
9317                 if (vmax.y < ap->r.y)
9318                         vmax.y = ap->r.y;
9319                 if (vmax.z < ap->r.z)
9320                         vmax.z = ap->r.z;
9321         }
9322         if (min != NULL)
9323                 *min = vmin;
9324         if (max != NULL)
9325                 *max = vmax;
9326         return 0;       
9327 }
9328
9329 #pragma mark ====== Measurements ======
9330
9331 Double
9332 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
9333 {
9334         Vector r1, r2;
9335 /*      if (mp->is_xtal_coord) {
9336                 TransformVec(&r1, mp->cell->tr, vp1);
9337                 TransformVec(&r2, mp->cell->tr, vp2);
9338         } else */ {
9339                 r1 = *vp1;
9340                 r2 = *vp2;
9341         }
9342         VecDec(r1, r2);
9343         return VecLength(r1);
9344 }
9345
9346 Double
9347 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
9348 {
9349         Vector r1, r2, r3;
9350         double w;
9351 /*      if (mp->is_xtal_coord) {
9352                 TransformVec(&r1, mp->cell->tr, vp1);
9353                 TransformVec(&r2, mp->cell->tr, vp2);
9354                 TransformVec(&r3, mp->cell->tr, vp3);
9355         } else */ {
9356                 r1 = *vp1;
9357                 r2 = *vp2;
9358                 r3 = *vp3;
9359         }
9360         VecDec(r1, r2);
9361         VecDec(r3, r2);
9362         w = VecLength(r1) * VecLength(r3);
9363         if (w < 1e-20)
9364                 return NAN;
9365         return acos(VecDot(r1, r3) / w) * kRad2Deg;
9366 }
9367
9368 Double
9369 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
9370 {
9371         Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
9372         double w1, w2, w3;
9373 /*      if (mp->is_xtal_coord) {
9374                 TransformVec(&r1, mp->cell->tr, vp1);
9375                 TransformVec(&r2, mp->cell->tr, vp2);
9376                 TransformVec(&r3, mp->cell->tr, vp3);
9377                 TransformVec(&r4, mp->cell->tr, vp4);
9378         } else */ {
9379                 r1 = *vp1;
9380                 r2 = *vp2;
9381                 r3 = *vp3;
9382                 r4 = *vp4;
9383         }
9384         VecSub(r21, r1, r2);
9385         VecSub(r32, r2, r3);
9386         VecSub(r43, r3, r4);
9387         VecCross(v1, r21, r32);
9388         VecCross(v2, r32, r43);
9389         VecCross(v3, r32, v1);
9390         w1 = VecLength(v1);
9391         w2 = VecLength(v2);
9392         w3 = VecLength(v3);
9393         if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
9394                 return NAN;
9395         } else {
9396                 w1 = 1.0 / w1;
9397                 w2 = 1.0 / w2;
9398                 w3 = 1.0 / w3;
9399                 VecScaleSelf(v1, w1);
9400                 VecScaleSelf(v2, w2);
9401                 VecScaleSelf(v3, w3);
9402                 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
9403         }
9404 }
9405
9406 #pragma mark ====== XtalCell Parameters ======
9407
9408 void
9409 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
9410 {
9411         if (mp->cell != NULL) {
9412                 TransformVec(dst, mp->cell->tr, src);
9413         } else *dst = *src;
9414 }
9415
9416 void
9417 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
9418 {
9419         if (mp->cell != NULL) {
9420                 TransformVec(dst, mp->cell->rtr, src);
9421         } else *dst = *src;
9422 }
9423
9424 int
9425 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
9426 {
9427         static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
9428         int n1, n2, n3;
9429         Vector *vp1, *vp2, *vp3;
9430         Vector v1, v2;
9431
9432         if (cp == NULL)
9433                 return 0;
9434         for (n1 = 0; n1 < 3; n1++) {
9435                 if (cp->flags[n1] != 0)
9436                         break;
9437         }
9438         if (n1 == 3) {
9439                 /*  All directions are non-periodic  */
9440                 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
9441                 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
9442         } else {
9443                 n2 = (n1 + 1) % 3;
9444                 n3 = (n1 + 2) % 3;
9445                 vp1 = &(cp->axes[n1]);
9446                 vp2 = &(cp->axes[n2]);
9447                 vp3 = &(cp->axes[n3]);
9448                 cp->tr[n1*3] = vp1->x;
9449                 cp->tr[n1*3+1] = vp1->y;
9450                 cp->tr[n1*3+2] = vp1->z;
9451                 cp->tr[9] = cp->origin.x;
9452                 cp->tr[10] = cp->origin.y;
9453                 cp->tr[11] = cp->origin.z;
9454                 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
9455                         /*  1-dimensional or 2-dimensional system  */
9456                         /*  Create "dummy" axes, so that transforms between internal and cartesian coordinates are
9457                          possible with a single matrix  */
9458                         if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
9459                                 /*  1-dimensional  */
9460                                 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
9461                                 VecCross(v1, *vp1, xvec);
9462                                 VecCross(v2, *vp1, yvec);
9463                                 if (VecLength2(v1) < VecLength2(v2))
9464                                         v1 = v2;
9465                                 VecCross(v2, *vp1, v1);
9466                                 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
9467                                         return -1;   /*  Non-regular transform  */
9468                         } else if (cp->flags[n2] == 0) {
9469                                 v2 = *vp3;
9470                                 VecCross(v1, v2, *vp1);
9471                                 if (NormalizeVec(&v1, &v1))
9472                                         return -1;  /*  Non-regular transform  */
9473                         } else {
9474                                 v1 = *vp2;
9475                                 VecCross(v2, *vp1, v1);
9476                                 if (NormalizeVec(&v2, &v2))
9477                                         return -1;  /*  Non-regular transform  */
9478                         }
9479                         cp->tr[n2*3] = v1.x;
9480                         cp->tr[n2*3+1] = v1.y;
9481                         cp->tr[n2*3+2] = v1.z;
9482                         cp->tr[n3*3] = v2.x;
9483                         cp->tr[n3*3+1] = v2.y;
9484                         cp->tr[n3*3+2] = v2.z;
9485                 } else {
9486                         VecCross(v1, *vp1, *vp2);
9487                         if (fabs(VecDot(v1, *vp3)) < 1e-7)
9488                                 return -1;  /*  Non-regular transform  */
9489                         cp->tr[n2*3] = vp2->x;
9490                         cp->tr[n2*3+1] = vp2->y;
9491                         cp->tr[n2*3+2] = vp2->z;
9492                         cp->tr[n3*3] = vp3->x;
9493                         cp->tr[n3*3+1] = vp3->y;
9494                         cp->tr[n3*3+2] = vp3->z;
9495                 }
9496         }
9497         if (TransformInvert(cp->rtr, cp->tr))
9498                 return -1;  /*  Non-regular transform  */
9499
9500         /*  Calculate the reciprocal cell parameters  */
9501         cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[3] * cp->rtr[3] + cp->rtr[6] * cp->rtr[6]);
9502         cp->rcell[1] = sqrt(cp->rtr[1] * cp->rtr[1] + cp->rtr[4] * cp->rtr[4] + cp->rtr[7] * cp->rtr[7]);
9503         cp->rcell[2] = sqrt(cp->rtr[2] * cp->rtr[2] + cp->rtr[5] * cp->rtr[5] + cp->rtr[8] * cp->rtr[8]);
9504         cp->rcell[3] = acos((cp->rtr[1] * cp->rtr[2] + cp->rtr[4] * cp->rtr[5] + cp->rtr[7] * cp->rtr[8]) / (cp->rcell[1] * cp->rcell[2])) * kRad2Deg;
9505         cp->rcell[4] = acos((cp->rtr[2] * cp->rtr[0] + cp->rtr[5] * cp->rtr[3] + cp->rtr[8] * cp->rtr[6]) / (cp->rcell[2] * cp->rcell[0])) * kRad2Deg;
9506         cp->rcell[5] = acos((cp->rtr[0] * cp->rtr[1] + cp->rtr[3] * cp->rtr[4] + cp->rtr[6] * cp->rtr[7]) / (cp->rcell[0] * cp->rcell[1])) * kRad2Deg;
9507         
9508         if (calc_abc) {
9509                 /*  Calculate a, b, c, alpha, beta, gamma  */
9510                 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[1] * cp->tr[1] + cp->tr[2] * cp->tr[2]);
9511                 cp->cell[1] = sqrt(cp->tr[3] * cp->tr[3] + cp->tr[4] * cp->tr[4] + cp->tr[5] * cp->tr[5]);
9512                 cp->cell[2] = sqrt(cp->tr[6] * cp->tr[6] + cp->tr[7] * cp->tr[7] + cp->tr[8] * cp->tr[8]);
9513                 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;
9514                 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;
9515                 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;
9516         }
9517         return 0;
9518 }
9519
9520 void
9521 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
9522 {
9523         XtalCell *cp;
9524         int i;
9525         Atom *ap;
9526         Transform cmat;
9527         if (mp == NULL)
9528                 return;
9529         __MoleculeLock(mp);
9530         memset(&cmat, 0, sizeof(Transform));
9531         if (mp->cell != NULL)
9532                 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
9533         else
9534                 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
9535         if (a == 0.0) {
9536                 if (mp->cell != NULL) {
9537                         free(mp->cell);
9538                         mp->needsMDRebuild = 1;
9539                 }
9540                 mp->cell = NULL;
9541         } else {
9542                 cp = mp->cell;
9543                 if (cp == NULL) {
9544                         cp = (XtalCell *)calloc(sizeof(XtalCell), 1);
9545                         if (cp == NULL)
9546                                 Panic("Low memory during setting cell parameters");
9547                         mp->cell = cp;
9548                         mp->needsMDRebuild = 1;
9549                 }
9550                 /*  alpha, beta, gamma are in degree  */
9551                 cp->cell[0] = a;
9552                 cp->cell[1] = b;
9553                 cp->cell[2] = c;
9554                 cp->cell[3] = alpha;
9555                 cp->cell[4] = beta;
9556                 cp->cell[5] = gamma;
9557                 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
9558                         /*  c unique (hexagonal etc.)  */
9559                         Double cosa, cosb, sinb, cosg;
9560                         cosa = cos(alpha * kDeg2Rad);
9561                         cosb = cos(beta * kDeg2Rad);
9562                         sinb = sin(beta * kDeg2Rad);
9563                         cosg = cos(gamma * kDeg2Rad);
9564                         cp->axes[0].x = a * sinb;
9565                         cp->axes[0].y = 0;
9566                         cp->axes[0].z = a * cosb;
9567                         cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
9568                         cp->axes[1].z = b * cosa;
9569                         cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
9570                         cp->axes[2].x = 0;
9571                         cp->axes[2].y = 0;
9572                         cp->axes[2].z = c;
9573                 } else {
9574                         /*  b unique  */
9575                         Double cosg, sing, cosa, cosb;
9576                         cosa = cos(alpha * kDeg2Rad);
9577                         cosb = cos(beta * kDeg2Rad);
9578                         cosg = cos(gamma * kDeg2Rad);
9579                         sing = sin(gamma * kDeg2Rad);
9580                         cp->axes[0].x = a * sing;
9581                         cp->axes[0].y = a * cosg;
9582                         cp->axes[0].z = 0;
9583                         cp->axes[1].x = 0;
9584                         cp->axes[1].y = b;
9585                         cp->axes[1].z = 0;
9586                         cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
9587                         cp->axes[2].y = c * cosa;
9588                         cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
9589                 }
9590                 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
9591                 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
9592                 MoleculeCalculateCellFromAxes(cp, 0);
9593                 TransformMul(cmat, cp->tr, cmat);
9594         }
9595         
9596         /*  Update the coordinates (if requested)  */
9597         if (convertCoordinates) {
9598                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9599                         TransformVec(&(ap->r), cmat, &(ap->r));
9600                 }
9601         }
9602         
9603         /*  Update the anisotropic parameters  */
9604         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9605                 Aniso *anp = ap->aniso;
9606                 if (anp != NULL) {
9607                         MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
9608                 }
9609         }
9610         __MoleculeUnlock(mp);
9611         sMoleculeNotifyChangeAppearance(mp);
9612 }
9613
9614 void
9615 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x13, Double x23, const Double *sigmaptr)
9616 {
9617         Double d, dx;
9618         int u = 0;
9619         const Double log2 = 0.693147180559945;
9620         const Double pi22 = 19.7392088021787;  /* 2*pi**2 */
9621         Transform m1, m2;
9622         Aniso *anp;
9623         XtalCell *cp;
9624         Vector axis[3];
9625         Double val[3];
9626         if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
9627                 return;
9628         anp = mp->atoms[n1].aniso;
9629         __MoleculeLock(mp);
9630         if (anp == NULL) {
9631                 anp = (Aniso *)calloc(sizeof(Aniso), 1);
9632                 if (anp == NULL) {
9633                         __MoleculeUnlock(mp);
9634                         Panic("Low memory during setting anisotropic atom parameters");
9635                 }
9636                 mp->atoms[n1].aniso = anp;
9637         }
9638         switch (type) {
9639                 case 1: d = 1; dx = 0.5; break;
9640                 case 2: d = log2; dx = log2; break;
9641                 case 3: d = log2; dx = log2 * 0.5; break;
9642                 case 4: u = 1; d = 0.25; dx = 0.25; break;
9643                 case 5: u = 1; d = 0.25; dx = 0.125; break;
9644                 case 8: u = 1; d = pi22; dx = pi22; break;
9645                 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
9646                 case 10: d = pi22; dx = pi22; break;
9647                 default: d = dx = 1; break;
9648         }
9649         anp->bij[0] = x11 * d;
9650         anp->bij[1] = x22 * d;
9651         anp->bij[2] = x33 * d;
9652         anp->bij[3] = x12 * dx;
9653         anp->bij[4] = x13 * dx;
9654         anp->bij[5] = x23 * dx;
9655         if (sigmaptr != NULL) {
9656                 anp->has_bsig = 1;
9657                 anp->bsig[0] = sigmaptr[0] * d;
9658                 anp->bsig[1] = sigmaptr[1] * d;
9659                 anp->bsig[2] = sigmaptr[2] * d;
9660                 anp->bsig[3] = sigmaptr[3] * dx;
9661                 anp->bsig[4] = sigmaptr[4] * dx;
9662                 anp->bsig[5] = sigmaptr[5] * dx;
9663         } else {
9664                 anp->has_bsig = 0;
9665                 anp->bsig[0] = anp->bsig[1] = anp->bsig[2] = anp->bsig[3] = anp->bsig[4] = anp->bsig[5] = 0.0;
9666         }
9667         cp = mp->cell;
9668         if (cp != NULL && u == 1) {
9669                 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
9670                 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
9671                 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
9672                 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
9673                 anp->bij[4] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[3] * kDeg2Rad); */
9674                 anp->bij[5] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[4] * kDeg2Rad); */
9675                 if (sigmaptr != NULL) {
9676                         anp->bsig[0] *= cp->rcell[0] * cp->rcell[0];
9677                         anp->bsig[1] *= cp->rcell[1] * cp->rcell[1];
9678                         anp->bsig[2] *= cp->rcell[2] * cp->rcell[2];
9679                         anp->bsig[3] *= cp->rcell[0] * cp->rcell[1];
9680                         anp->bsig[4] *= cp->rcell[2] * cp->rcell[0];
9681                         anp->bsig[5] *= cp->rcell[1] * cp->rcell[2];
9682                 }
9683         }
9684         
9685         /*  Calculate the principal axes (in Cartesian coordinates)  */
9686         /*  The principal axes are the eigenvectors of matrix At(B^-1)A, where
9687                 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
9688                 in which x and z are the crystal-space and cartesian coordinates. */
9689         m1[0] = anp->bij[0] / pi22;
9690         m1[4] = anp->bij[1] / pi22;
9691         m1[8] = anp->bij[2] / pi22;
9692         m1[1] = m1[3] = anp->bij[3] / pi22;
9693         m1[2] = m1[6] = anp->bij[4] / pi22;
9694         m1[5] = m1[7] = anp->bij[5] / pi22;
9695         MatrixInvert(m1, m1);
9696         if (cp != NULL) {
9697                 memmove(m2, cp->rtr, sizeof(Mat33));
9698                 MatrixMul(m1, m1, m2);
9699                 MatrixTranspose(m2, m2);
9700                 MatrixMul(m1, m2, m1);
9701         }
9702         MatrixSymDiagonalize(m1, val, axis);
9703         for (u = 0; u < 3; u++) {
9704                 if (val[u] < 0) {
9705                         fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
9706                         val[u] = 0.001;
9707                 } else {
9708                         val[u] = 1 / sqrt(val[u]);
9709                 }
9710                 anp->pmat[u*3] = axis[u].x * val[u];
9711                 anp->pmat[u*3+1] = axis[u].y * val[u];
9712                 anp->pmat[u*3+2] = axis[u].z * val[u];
9713         }
9714         __MoleculeUnlock(mp);
9715 }
9716
9717 /*  Set the anisotropic parameter for atom idx according to the symop. If symop is not alive, nothing is done. */
9718 void
9719 MoleculeSetAnisoBySymop(Molecule *mp, int idx)
9720 {
9721         Atom *ap, *ap2;
9722         Transform t1, t2;
9723         if (mp == NULL || idx < 0 || idx >= mp->natoms)
9724                 return;
9725         ap = ATOM_AT_INDEX(mp->atoms, idx);
9726         if (!SYMOP_ALIVE(ap->symop))
9727                 return;
9728         ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
9729         if (ap2->aniso == NULL) {
9730                 if (ap->aniso != NULL) {
9731                         free(ap->aniso);
9732                         ap->aniso = NULL;
9733                 }
9734                 return;
9735         }
9736         if (ap->aniso == NULL)
9737                 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
9738         if (ap->symop.sym == 0 || ap->symop.sym >= mp->nsyms) {
9739                 /*  Just copy the aniso parameters  */
9740                 memmove(ap->aniso, ap2->aniso, sizeof(Aniso));
9741                 return;
9742         }
9743         memmove(t1, SYMMETRY_AT_INDEX(mp->syms, ap->symop.sym), sizeof(Transform));
9744         t1[9] = t1[10] = t1[11] = 0.0;
9745         memset(t2, 0, sizeof(Transform));
9746         t2[0] = ap2->aniso->bij[0];
9747         t2[4] = ap2->aniso->bij[1];
9748         t2[8] = ap2->aniso->bij[2];
9749         t2[1] = t2[3] = ap2->aniso->bij[3];
9750         t2[2] = t2[6] = ap2->aniso->bij[4];
9751         t2[5] = t2[7] = ap2->aniso->bij[5];
9752         TransformMul(t2, t1, t2);
9753         TransformInvert(t1, t1);
9754         TransformMul(t2, t2, t1);
9755         MoleculeSetAniso(mp, idx, 0, t2[0], t2[4], t2[8], t2[1], t2[2], t2[5], (ap2->aniso->has_bsig ? ap2->aniso->bsig : NULL));
9756 }
9757
9758 int
9759 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic, int convertCoordinates)
9760 {
9761         static Vector zeroVec = {0, 0, 0};
9762         XtalCell b;
9763         Transform cmat;
9764         int i, n;
9765         Atom *ap;
9766         if (mp == NULL)
9767                 return 0;
9768         if (mp->cell != NULL)
9769                 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
9770         else
9771                 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
9772         if (ax == NULL) {
9773                 if (mp->cell != NULL) {
9774                         free(mp->cell);
9775                         mp->needsMDRebuild = 1;
9776                 }
9777                 mp->cell = NULL;
9778                 return 0;
9779         }       
9780         memset(&b, 0, sizeof(b));
9781         b.axes[0] = (ax != NULL ? *ax : zeroVec);
9782         b.axes[1] = (ay != NULL ? *ay : zeroVec);
9783         b.axes[2] = (az != NULL ? *az : zeroVec);
9784         b.origin = *ao;
9785         memmove(b.flags, periodic, 3);
9786         if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
9787                 return -1;
9788         __MoleculeLock(mp);
9789         if (mp->cell == NULL) {
9790                 mp->needsMDRebuild = 1;
9791         } else {
9792                 if (mp->cell->has_sigma) {
9793                         /*  Keep the sigma  */
9794                         b.has_sigma = 1;
9795                         memmove(b.cellsigma, mp->cell->cellsigma, sizeof(mp->cell->cellsigma));
9796                 }
9797                 if ((b.flags[0] != mp->cell->flags[0]) || (b.flags[1] != mp->cell->flags[1]) || (b.flags[2] != mp->cell->flags[2])) {
9798                         mp->needsMDRebuild = 1;
9799                 }
9800                 free(mp->cell);
9801         }
9802         mp->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
9803         if (mp->cell != NULL) {
9804                 memmove(mp->cell, &b, sizeof(XtalCell));
9805                 TransformMul(cmat, b.tr, cmat);
9806                 /*  Update the coordinates (if requested)  */
9807                 if (convertCoordinates) {
9808                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9809                                 TransformVec(&(ap->r), cmat, &(ap->r));
9810                         }
9811                 }
9812                 
9813                 /*  Update the anisotropic parameters  */
9814                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9815                         Aniso *anp = ap->aniso;
9816                         if (anp != NULL) {
9817                                 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
9818                         }
9819                 }
9820                 n = 0;
9821         } else n = -2;  /*  Out of memory  */
9822         __MoleculeUnlock(mp);
9823         sMoleculeNotifyChangeAppearance(mp);
9824         return n;
9825 }
9826
9827 #pragma mark ====== Fragment manipulation ======
9828
9829 static void
9830 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
9831 {
9832         Atom *ap;
9833         Int i, *cp, idx2;
9834         if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
9835                 return;
9836         IntGroupAdd(result, idx, 1);
9837         ap = ATOM_AT_INDEX(mp->atoms, idx);
9838         cp = AtomConnectData(&ap->connect);
9839         for (i = 0; i < ap->connect.count; i++) {
9840                 idx2 = cp[i];
9841                 if (IntGroupLookup(result, idx2, NULL))
9842                         continue;
9843                 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, idx2)->anchor != NULL)
9844                         continue;  /*  bond between two pi_anchors is ignored  */
9845                 sMoleculeFragmentSub(mp, idx2, result, exatoms);
9846         }
9847         if (ap->anchor != NULL) {
9848                 cp = AtomConnectData(&ap->anchor->connect);
9849                 for (i = 0; i < ap->anchor->connect.count; i++) {
9850                         idx2 = cp[i];
9851                         if (IntGroupLookup(result, idx2, NULL))
9852                                 continue;
9853                         sMoleculeFragmentSub(mp, idx2, result, exatoms);
9854                 }
9855         }
9856 }
9857
9858 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
9859     not containing the atoms in exatoms  */
9860 IntGroup *
9861 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
9862 {
9863         IntGroup *result;
9864         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9865                 return NULL;
9866         result = IntGroupNew();
9867         sMoleculeFragmentSub(mp, n1, result, exatoms);
9868         return result;
9869 }
9870
9871 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
9872     not containing the atoms n2, n3, ... (terminated by -1)  */
9873 IntGroup *
9874 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
9875 {
9876         int i;
9877         IntGroup *exatoms, *result;
9878         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9879                 return NULL;
9880         exatoms = IntGroupNew();
9881         for (i = 0; i < argc; i++)
9882                 IntGroupAdd(exatoms, argv[i], 1);
9883         result = IntGroupNew();
9884         sMoleculeFragmentSub(mp, n1, result, exatoms);
9885         IntGroupRelease(exatoms);
9886         return result;
9887 }
9888
9889 /*  The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
9890     not containing the atoms in exatoms  */
9891 IntGroup *
9892 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
9893 {
9894         IntGroupIterator iter;
9895         IntGroup *result;
9896         int i;
9897         if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
9898                 return NULL;
9899         IntGroupIteratorInit(inatoms, &iter);
9900         result = IntGroupNew();
9901         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9902                 sMoleculeFragmentSub(mp, i, result, exatoms);
9903         }
9904         IntGroupIteratorRelease(&iter);
9905         return result;
9906 }
9907
9908 /*  Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
9909     group is bound to the rest of the molecule via only one bond.
9910         If the result is true, then the atoms belonging to the (only) bond are returned
9911         in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
9912         and n2 can be NULL, if those informations are not needed.  */
9913 int
9914 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
9915 {
9916         Int i, i1, i2, j, k, bond_count, nval1, nval2, *cp;
9917         Atom *ap;
9918         if (mp == NULL || mp->natoms == 0 || group == NULL)
9919                 return 0;  /*  Invalid arguments  */
9920         bond_count = 0;
9921         for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
9922                 i2 = IntGroupGetEndPoint(group, i);
9923                 for (j = i1; j < i2; j++) {
9924                         if (j < 0 || j >= mp->natoms)
9925                                 return 0;  /*  Invalid atom group  */
9926                         ap = ATOM_AT_INDEX(mp->atoms, j);
9927                         cp = AtomConnectData(&ap->connect);
9928                         for (k = 0; k < ap->connect.count; k++) {
9929                                 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, cp[k])->anchor != NULL)
9930                                         continue;  /*  Ignore bond between two pi_anchors  */
9931                                 if (IntGroupLookup(group, cp[k], NULL) == 0) {
9932                                         bond_count++;
9933                                         nval1 = j;
9934                                         nval2 = cp[k];
9935                                         if (bond_count > 1)
9936                                                 return 0;  /*  Too many bonds  */
9937                                 }
9938                         }
9939                         if (ap->anchor != NULL) {
9940                                 cp = AtomConnectData(&ap->anchor->connect);
9941                                 for (k = 0; k < ap->anchor->connect.count; k++) {
9942                                         if (IntGroupLookup(group, cp[k], NULL) == 0) {
9943                                                 bond_count++;
9944                                                 nval1 = j;
9945                                                 nval2 = cp[k];
9946                                                 if (bond_count > 1)
9947                                                         return 0;  /*  Too many bonds  */
9948                                         }
9949                                 }                                       
9950                         }
9951                 }
9952         }
9953         if (bond_count == 1) {
9954                 if (n1 != NULL)
9955                         *n1 = nval1;
9956                 if (n2 != NULL)
9957                         *n2 = nval2;
9958                 return 1;
9959         } else {
9960                 return 0;
9961         }       
9962 }
9963
9964 /*  Returns non-zero if the given group is 'rotatable' in the molecule. The group
9965     is said to be 'rotatable' when either of the following conditions are met; (1)
9966         the group is detachable, or (2) the group consists of two bonded atoms that define
9967         a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
9968         (either a new IntGroup or 'group' with incremented reference count; thus the caller
9969         is responsible for releasing the returned value).  */
9970 int
9971 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
9972 {
9973         int i1, i2;
9974         if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
9975                 if (rotGroup != NULL) {
9976                         IntGroupRetain(group);
9977                         *rotGroup = group;
9978                 }
9979                 return 1;
9980         }
9981         if (group != NULL && IntGroupGetCount(group) == 2) {
9982                 i1 = IntGroupGetNthPoint(group, 0);
9983                 i2 = IntGroupGetNthPoint(group, 1);
9984                 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
9985                         IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
9986                         if (frag == NULL)
9987                                 return 0;
9988                         i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
9989                         if (i1 == 0) {
9990                                 IntGroupRelease(frag);
9991                                 if (rotGroup != NULL)
9992                                         *rotGroup = NULL;
9993                                 return 0;
9994                         }
9995                         if (rotGroup != NULL)
9996                                 *rotGroup = frag;
9997                         else if (frag != NULL)
9998                                 IntGroupRelease(frag);
9999                         return i1;
10000                 }
10001         }
10002         return 0;
10003 }
10004
10005 #pragma mark ====== Multiple frame ======
10006
10007 int
10008 MoleculeGetNumberOfFrames(Molecule *mp)
10009 {
10010         if (mp == NULL)
10011                 return 0;
10012         if (mp->nframes <= 0) {
10013                 /*  Recalculate  */
10014                 int i, n;
10015                 Atom *ap;
10016                 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10017                         if (ap->nframes > n)
10018                                 n = ap->nframes;
10019                 }
10020                 if (n == 0)
10021                         n = 1;
10022                 mp->nframes = n;
10023         }
10024         return mp->nframes;
10025 }
10026
10027 int
10028 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
10029 {
10030         int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
10031         Vector *tempv, *vp;
10032         Atom *ap;
10033         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
10034                 return -1;
10035
10036         n_old = MoleculeGetNumberOfFrames(mp);
10037         n_new = n_old + count;
10038         last_inserted = IntGroupGetNthPoint(group, count - 1);
10039         if (n_new <= last_inserted) {
10040                 exframes = last_inserted - n_new + 1;  /*  number of extra frames that will be silently inserted  */
10041                 n_new += exframes;
10042         } else exframes = 0;
10043
10044         tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4);  /*  "*4" for handling cells  */
10045         if (tempv == NULL)
10046                 return -1;
10047
10048         __MoleculeLock(mp);
10049
10050         /*  Copy back the current coordinates  */
10051         /*  No change in the current coordinates, but the frame buffer is updated  */
10052         MoleculeSelectFrame(mp, mp->cframe, 1); 
10053         
10054         /*  Expand ap->frames for all atoms  */
10055         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10056                 if (ap->frames == NULL)
10057                         vp = (Vector *)calloc(sizeof(Vector), n_new);
10058                 else
10059                         vp = (Vector *)realloc(ap->frames, sizeof(Vector) * n_new);
10060                 if (vp == NULL) {
10061                         __MoleculeUnlock(mp);
10062                         return -1;
10063                 }
10064                 for (j = ap->nframes; j < n_new; j++)
10065                         vp[j] = ap->r;
10066                 ap->frames = vp;
10067         }
10068         if (mp->cell != NULL) {
10069                 j = mp->nframe_cells;
10070                 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, n_new - 1, NULL);
10071                 for (i = j; i < n_new; i++) {
10072                         /*  Set the current cell parameters to the expanded frames  */
10073                         mp->frame_cells[i * 4] = mp->cell->axes[0];
10074                         mp->frame_cells[i * 4 + 1] = mp->cell->axes[1];
10075                         mp->frame_cells[i * 4 + 2] = mp->cell->axes[2];
10076                         mp->frame_cells[i * 4 + 3] = mp->cell->origin;
10077                 }
10078         }
10079         
10080         /*  group = [n0..n1-1, n2..n3-1, ...]  */
10081         /*  s = t = 0,  */
10082         /*  tempv[0..n0-1] <- ap[0..n0-1], s += n0,
10083             tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
10084                 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
10085                 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
10086                 ...
10087                 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
10088                 At last, s will become n_old and t will become count.  */
10089         for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10090                 int s, t, ns, ne, mult;
10091                 Vector cr;
10092                 ne = s = t = 0;
10093                 if (i == mp->natoms) {
10094                         if (mp->cell == NULL || mp->frame_cells == NULL)
10095                                 break;
10096                         vp = mp->frame_cells;
10097                         mult = 4;
10098                 } else {
10099                         cr = ap->r;
10100                         vp = ap->frames;
10101                         mult = 1;
10102                 }
10103                 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10104                         if (ns > ne) {
10105                                 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
10106                                 s += ns - ne;
10107                         }
10108                         ne = IntGroupGetEndPoint(group, j);
10109                         while (ns < ne) {
10110                                 if (i == mp->natoms) {
10111                                         if (inFrameCell != NULL) {
10112                                                 tempv[ns * 4] = inFrameCell[t * 4];
10113                                                 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
10114                                                 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
10115                                                 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
10116                                         } else {
10117                                                 tempv[ns * 4] = mp->cell->axes[0];
10118                                                 tempv[ns * 4 + 1] = mp->cell->axes[1];
10119                                                 tempv[ns * 4 + 2] = mp->cell->axes[2];
10120                                                 tempv[ns * 4 + 3] = mp->cell->origin;
10121                                         }
10122                                 } else {
10123                                         if (inFrame != NULL)
10124                                                 tempv[ns] = inFrame[natoms * t + i];
10125                                         else
10126                                                 tempv[ns] = cr;
10127                                 }
10128                                 t++;
10129                                 ns++;
10130                         }
10131                 }
10132                 if (n_new > ne) {
10133                         memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
10134                         s += n_new - ne;
10135                 }
10136                 if (i < mp->natoms)
10137                         ap->nframes = n_new;
10138                 memmove(vp, tempv, sizeof(Vector) * mult * n_new);
10139         }
10140         free(tempv);
10141         mp->nframes = n_new;
10142         MoleculeSelectFrame(mp, last_inserted, 0);
10143         MoleculeIncrementModifyCount(mp);
10144         __MoleculeUnlock(mp);
10145         return count;
10146 }
10147
10148 int
10149 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
10150 {
10151         int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
10152         Vector *tempv, *vp;
10153         Atom *ap;
10154         IntGroup *group, *group2;
10155
10156         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
10157                 return -1;
10158
10159         /*  outFrame[] should have enough size for Vector * natoms * group.count  */
10160         memset(outFrame, 0, sizeof(Vector) * natoms * count);
10161         if (mp->cell != NULL && mp->frame_cells != NULL)
10162                 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
10163
10164         n_old = MoleculeGetNumberOfFrames(mp);
10165         if (n_old == 1)
10166                 return -2;  /*  Cannot delete last frame  */
10167
10168         group = IntGroupNew();
10169         group2 = IntGroupNewWithPoints(0, n_old, -1);
10170         IntGroupIntersect(inGroup, group2, group);
10171         IntGroupRelease(group2);
10172         count = IntGroupGetCount(group);
10173         n_new = n_old - count;
10174         if (n_new < 1) {
10175                 IntGroupRelease(group);
10176                 return -2;  /*  Trying to delete too many frames  */
10177         }
10178         tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4);  /*  "*4" for handling cells  */
10179         if (tempv == NULL) {
10180                 IntGroupRelease(group);
10181                 return -1;
10182         }
10183
10184         __MoleculeLock(mp);
10185
10186         /*  Copy back the current coordinates  */
10187         /*  No change in the current coordinates, but the frame buffer is updated  */
10188         MoleculeSelectFrame(mp, mp->cframe, 1); 
10189
10190         /*  Determine which frame should be selected after removal is completed  */
10191         {
10192                 int n1;
10193                 if (IntGroupLookup(group, mp->cframe, &i)) {
10194                         /*  cframe will be removed  */
10195                         n1 = IntGroupGetStartPoint(group, i) - 1;
10196                         if (n1 < 0)
10197                                 n1 = IntGroupGetEndPoint(group, i);
10198                 } else n1 = mp->cframe;
10199                 /*  Change to that frame  */
10200                 MoleculeSelectFrame(mp, n1, 0);
10201                 group2 = IntGroupNewFromIntGroup(group);
10202                 IntGroupReverse(group2, 0, n_old);
10203                 new_cframe = IntGroupLookupPoint(group2, n1);
10204                 if (new_cframe < 0)
10205                         return -3;  /*  This cannot happen  */
10206                 IntGroupRelease(group2);
10207         }
10208
10209         /*  group = [n0..n1-1, n2..n3-1, ...]  */
10210         /*  s = t = 0, */
10211         /*  tempv[0..n0-1] -> ap[0..n0-1], s += n0,
10212             tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
10213                 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
10214                 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
10215                 ...
10216                 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
10217                 At last, s will become n_new and t will become count.  */
10218         nframes = 0;
10219         for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10220                 int s, t, j, ns, ne;
10221                 int mult;
10222                 /*  if i == mp->natoms, mp->frame_cells is handled  */
10223                 if (i == mp->natoms) {
10224                         if (mp->cell == NULL || mp->frame_cells == NULL)
10225                                 break;
10226                         mult = 4;
10227                         vp = mp->frame_cells;
10228                         old_count = n_old;
10229                 } else {
10230                         mult = 1;
10231                         vp = ap->frames;
10232                         if (vp == NULL) {
10233                                 ap->frames = vp = (Vector *)calloc(sizeof(Vector), n_old);
10234                                 if (vp == NULL) {
10235                                         __MoleculeUnlock(mp);
10236                                         return -1;
10237                                 }
10238                         }
10239                         old_count = ap->nframes;
10240                 }
10241
10242                 /*  Copy vp to tempv  */
10243                 memset(tempv, 0, sizeof(Vector) * mult * n_old);
10244                 memmove(tempv, vp, sizeof(Vector) * mult * (old_count > n_old ? n_old : old_count));
10245                 ne = ns = s = t = 0;
10246                 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10247                         if (ns > n_old)
10248                                 ns = n_old;
10249                         if (ns > ne) {
10250                                 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (ns - ne));
10251                                 s += ns - ne;
10252                         }
10253                         ne = IntGroupGetEndPoint(group, j);
10254                         if (ne > n_old)
10255                                 ne = n_old;
10256                         while (ns < ne) {
10257                                 if (i < mp->natoms)
10258                                         outFrame[natoms * t + i] = tempv[ns];
10259                                 else if (outFrameCell != NULL) {
10260                                         outFrameCell[t * 4] = tempv[ns * 4];
10261                                         outFrameCell[t * 4 + 1] = tempv[ns * 4 + 1];
10262                                         outFrameCell[t * 4 + 2] = tempv[ns * 4 + 2];
10263                                         outFrameCell[t * 4 + 3] = tempv[ns * 4 + 3];
10264                                 }
10265                                 t++;
10266                                 ns++;
10267                         }
10268                 }
10269                 if (n_old > ne) {
10270                         memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (n_old - ne));
10271                         s += n_old - ne;
10272                 }
10273                 if (i < mp->natoms)
10274                         ap->nframes = s;
10275                 if (nframes < s)
10276                         nframes = s;
10277                 if (s <= 1) {
10278                         if (i < mp->natoms) {
10279                                 free(ap->frames);
10280                                 ap->frames = NULL;
10281                                 ap->nframes = 0;
10282                         } else {
10283                                 free(mp->frame_cells);
10284                                 mp->frame_cells = NULL;
10285                                 mp->nframe_cells = 0;
10286                         }
10287                 } else {
10288                         if (i < mp->natoms)
10289                                 ap->frames = (Vector *)realloc(ap->frames, sizeof(Vector) * s);
10290                         else {
10291                                 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, s - 1, NULL);
10292                                 mp->nframe_cells = s;
10293                         }
10294                 }
10295         }
10296         free(tempv);
10297         mp->nframes = nframes;
10298         
10299         /*  Select the "last" frame; do not "copy back" the coordinates to the frame table  */
10300 /*      i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
10301         MoleculeSelectFrame(mp, new_cframe, 0);
10302
10303         IntGroupRelease(group);
10304
10305         MoleculeIncrementModifyCount(mp);
10306         __MoleculeUnlock(mp);
10307         return count;
10308 }
10309
10310 int
10311 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
10312 {
10313         int i, cframe, nframes, modified;
10314         Atom *ap;
10315         cframe = mp->cframe;
10316         nframes = MoleculeGetNumberOfFrames(mp);
10317         if (frame == -1)
10318                 frame = mp->cframe;
10319         if (mp == NULL || mp->natoms == 0 || frame < 0 || frame >= nframes)
10320                 return -1;
10321         modified = 0;
10322         __MoleculeLock(mp);
10323         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10324                 if (copyback && cframe >= 0 && cframe < ap->nframes) {
10325                         /*  Write the current coordinate back to the frame array  */
10326                         ap->frames[cframe] = ap->r;
10327                 }
10328                 if (frame != cframe && frame >= 0 && frame < ap->nframes) {
10329                         /*  Read the coordinate from the frame array  */
10330                         ap->r = ap->frames[frame];
10331                         modified = 1;
10332                 }
10333         }
10334
10335         if (mp->cell != NULL && mp->frame_cells != NULL) {
10336                 /*  Write the current cell back to the frame_cells array  */
10337                 if (copyback && cframe >= 0) {
10338                         Vector *vp = (Vector *)AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, cframe, NULL);
10339                         vp[0] = mp->cell->axes[0];
10340                         vp[1] = mp->cell->axes[1];
10341                         vp[2] = mp->cell->axes[2];
10342                         vp[3] = mp->cell->origin;
10343                 }
10344                 /*  Set the cell from the frame array  */
10345                 if (frame != cframe && frame >= 0 && frame < mp->nframe_cells) {
10346                         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);
10347                         modified = 1;
10348                         MoleculeAmendBySymmetry(mp, NULL, NULL, NULL);
10349                 }
10350         }
10351         mp->cframe = frame;
10352         if (modified)
10353                 mp->needsMDCopyCoordinates = 1;
10354         __MoleculeUnlock(mp);
10355         sMoleculeNotifyChangeAppearance(mp);
10356         return frame;
10357 }
10358
10359 /*  If molecule is multi-frame, then flush the current information to the frame buffer.
10360     Returns the number of frames.  */
10361 int
10362 MoleculeFlushFrames(Molecule *mp)
10363 {
10364         int nframes = MoleculeGetNumberOfFrames(mp);
10365         if (nframes > 1)
10366                 MoleculeSelectFrame(mp, mp->cframe, 1);
10367         return nframes;
10368 }
10369
10370 #pragma mark ====== Pi Atoms ======
10371
10372 static inline void
10373 sMoleculeCalculatePiAnchorPosition(Atom *ap, Atom *atoms)
10374 {
10375         Int *cp, j, n;
10376         Atom *ap2;
10377         cp = AtomConnectData(&ap->anchor->connect);
10378         n = ap->anchor->connect.count;
10379         VecZero(ap->r);
10380         for (j = 0; j < n; j++) {
10381                 Double w = ap->anchor->coeffs[j];
10382                 ap2 = ATOM_AT_INDEX(atoms, cp[j]);
10383                 VecScaleInc(ap->r, ap2->r, w);
10384         }       
10385 }
10386
10387 void
10388 MoleculeUpdatePiAnchorPositions(Molecule *mol)
10389 {
10390         Int i;
10391         Atom *ap;
10392         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
10393                 if (ap->anchor == NULL)
10394                         continue;
10395                 sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
10396         }
10397 }
10398
10399 void
10400 MoleculeCalculatePiAnchorPosition(Molecule *mol, int idx)
10401 {
10402         Atom *ap;
10403         if (mol == NULL || idx < 0 || idx >= mol->natoms)
10404                 return;
10405         ap = ATOM_AT_INDEX(mol->atoms, idx);
10406         if (ap->anchor == NULL)
10407                 return;
10408         sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
10409 }
10410
10411 int
10412 MoleculeSetPiAnchorList(Molecule *mol, Int idx, Int nentries, Int *entries, Double *weights, Int *nUndoActions, struct MolAction ***undoActions)
10413 {
10414         Atom *ap;
10415         Int *ip, i, j, n, *np;
10416         Double d;
10417         if (mol == NULL || idx < 0 || idx >= mol->natoms || nentries <= 1)
10418                 return -1;  /*  Invalid argument  */
10419         if (weights != NULL) {
10420                 d = 0.0;
10421                 for (i = 0; i < nentries; i++) {
10422                         if (weights[i] <= 0.0) {
10423                                 return 10;  /*  Weights must be positive  */
10424                         }
10425                         d += weights[i];
10426                 }
10427                 d = 1.0 / d;
10428         } else d = 1.0 / nentries;
10429         ap = ATOM_AT_INDEX(mol->atoms, idx);
10430         if (ap->anchor != NULL) {
10431                 /*  Already an anchor: check if bonds/angles/dihedrals have entries related to this anchor  */
10432                 IntGroup *bg, *ag, *dg, *ig;
10433                 Int *ibuf, ibufsize;
10434                 MolAction *act;
10435                 bg = ag = dg = ig = NULL;
10436                 ip = AtomConnectData(&ap->anchor->connect);
10437                 for (i = 0; i < ap->anchor->connect.count; i++) {
10438                         n = ip[i];
10439                         for (j = 0; j < nentries; j++) {
10440                                 if (n == entries[j])
10441                                         break;
10442                         }
10443                         if (j == nentries) {
10444                                 /*  This entry will disappear: if any bond/angle/dihedral has idx-n pair, that should be removed.  */
10445                                 for (j = 0, np = mol->bonds; j < mol->nbonds; j++, np += 2) {
10446                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[0])) {
10447                                                 if (bg == NULL)
10448                                                         bg = IntGroupNew();
10449                                                 IntGroupAdd(bg, j, 1);
10450                                         }
10451                                 }
10452                                 for (j = 0, np = mol->angles; j < mol->nangles; j++, np += 3) {
10453                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) ||
10454                                                 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1])) {
10455                                                 if (ag == NULL)
10456                                                         ag = IntGroupNew();
10457                                                 IntGroupAdd(ag, j, 1);
10458                                         }
10459                                 }
10460                                 for (j = 0, np = mol->dihedrals; j < mol->ndihedrals; j++, np += 4) {
10461                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) || (idx == np[2] && n == np[3]) ||
10462                                                 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[3] && n == np[2])) {
10463                                                 if (dg == NULL)
10464                                                         dg = IntGroupNew();
10465                                                 IntGroupAdd(dg, j, 1);
10466                                         }
10467                                 }
10468                                 for (j = 0, np = mol->impropers; j < mol->nimpropers; j++, np += 4) {
10469                                         if ((idx == np[0] && n == np[2]) || (idx == np[1] && n == np[2]) || (idx == np[3] && n == np[2]) ||
10470                                                 (idx == np[2] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[2] && n == np[3])) {
10471                                                 if (ig == NULL)
10472                                                         ig = IntGroupNew();
10473                                                 IntGroupAdd(ig, j, 1);
10474                                         }
10475                                 }
10476                         }
10477                 }
10478                 ibuf = NULL;
10479                 ibufsize = 0;
10480                 if (ig != NULL) {
10481                         /*  Delete impropers (with undo info) */
10482                         i = IntGroupGetCount(ig);
10483                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
10484                         MoleculeDeleteImpropers(mol, ibuf, ig);
10485                         if (nUndoActions != NULL && undoActions != NULL) {
10486                                 act = MolActionNew(gMolActionAddImpropers, i * 4, ibuf, ig);
10487                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10488                         }
10489                         IntGroupRelease(ig);
10490                 }
10491                 if (dg != NULL) {
10492                         /*  Delete dihedrals (with undo info)  */
10493                         i = IntGroupGetCount(dg);
10494                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
10495                         MoleculeDeleteDihedrals(mol, ibuf, dg);
10496                         if (nUndoActions != NULL && undoActions != NULL) {
10497                                 act = MolActionNew(gMolActionAddDihedrals, i * 4, ibuf, dg);
10498                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10499                         }
10500                         IntGroupRelease(dg);
10501                 }
10502                 if (ag != NULL) {
10503                         /*  Delete angles (with undo info) */
10504                         i = IntGroupGetCount(ag);
10505                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 3 - 1, NULL);
10506                         MoleculeDeleteAngles(mol, ibuf, ag);
10507                         if (nUndoActions != NULL && undoActions != NULL) {
10508                                 act = MolActionNew(gMolActionAddAngles, i * 3, ibuf, ag);
10509                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10510                         }
10511                         IntGroupRelease(ag);
10512                 }
10513                 if (bg != NULL) {
10514                         /*  Delete bonds (with undo info) */
10515                         i = IntGroupGetCount(bg);
10516                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 2 - 1, NULL);
10517                         MoleculeDeleteBonds(mol, ibuf, bg, NULL, NULL);
10518                         if (nUndoActions != NULL && undoActions != NULL) {
10519                                 act = MolActionNew(gMolActionAddBondsForUndo, i * 2, ibuf, bg);
10520                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10521                         }
10522                         IntGroupRelease(bg);
10523                 }
10524         } else {
10525                 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
10526         }
10527         AtomConnectResize(&ap->anchor->connect, nentries);
10528         memmove(AtomConnectData(&ap->anchor->connect), entries, sizeof(Int) * nentries);
10529         AssignArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), nentries - 1, NULL);
10530         if (weights != NULL) {
10531                 memmove(ap->anchor->coeffs, weights, sizeof(Double) * nentries);
10532                 for (i = 0; i < nentries; i++)
10533                         ap->anchor->coeffs[i] *= d;   /*  Normalize weight  */
10534         } else {
10535                 for (i = 0; i < nentries; i++)
10536                         ap->anchor->coeffs[i] = d;
10537         }
10538         MoleculeCalculatePiAnchorPosition(mol, idx);
10539         return 0;
10540 }
10541
10542 #pragma mark ====== MO calculation ======
10543
10544 /*  Calculate an MO value for a single point.  */
10545 /*  Index is the MO number (1-based)  */
10546 /*  tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom.  */
10547 static Double
10548 sCalcMOPoint(const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
10549 {
10550         ShellInfo *sp;
10551         PrimInfo *pp;
10552         Double val, tval, *cnp, *tmpp, *mobasep, *mop;
10553         Int i, j;
10554         /*  Cache dr and |dr|^2  */
10555         for (i = 0; i < bset->natoms; i++) {
10556                 Vector r = bset->pos[i];
10557                 tmp[i * 4] = r.x = vp->x - r.x;
10558                 tmp[i * 4 + 1] = r.y = vp->y - r.y;
10559                 tmp[i * 4 + 2] = r.z = vp->z - r.z;
10560                 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
10561         }
10562         /*  Iterate over all shells  */
10563         val = 0.0;
10564         mobasep = bset->mo + (index - 1) * bset->ncomps;
10565         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
10566                 pp = bset->priminfos + sp->p_idx;
10567                 cnp = bset->cns + sp->cn_idx;
10568                 tmpp = tmp + sp->a_idx * 4;
10569                 mop = mobasep + sp->m_idx;
10570                 switch (sp->sym) {
10571                         case kGTOType_S: {
10572                                 tval = 0;
10573                                 for (j = 0; j < sp->nprim; j++) {
10574                                         tval += *cnp++ * exp(-pp->A * tmpp[3]);
10575                                         pp++;
10576                                 }
10577                                 val += mop[0] * tval;
10578                                 break;
10579                         }
10580                         case kGTOType_P: {
10581                                 Double x, y, z;
10582                                 x = y = z = 0;
10583                                 for (j = 0; j < sp->nprim; j++) {
10584                                         tval = exp(-pp->A * tmpp[3]);
10585                                         x += *cnp++ * tval;
10586                                         y += *cnp++ * tval;
10587                                         z += *cnp++ * tval;
10588                                         pp++;
10589                                 }
10590                                 x *= mop[0] * tmpp[0];
10591                                 y *= mop[1] * tmpp[1];
10592                                 z *= mop[2] * tmpp[2];
10593                                 val += x + y + z;
10594                                 break;
10595                         }
10596                         case kGTOType_SP: {
10597                                 Double t, x, y, z;
10598                                 t = x = y = z = 0;
10599                                 for (j = 0; j < sp->nprim; j++) {
10600                                         tval = exp(-pp->A * tmpp[3]);
10601                                         t += *cnp++ * tval;
10602                                         x += *cnp++ * tval;
10603                                         y += *cnp++ * tval;
10604                                         z += *cnp++ * tval;
10605                                         pp++;
10606                                 }
10607                                 t *= mop[0];
10608                                 x *= mop[1] * tmpp[0];
10609                                 y *= mop[2] * tmpp[1];
10610                                 z *= mop[3] * tmpp[2];
10611                                 val += t + x + y + z;
10612                                 break;
10613                         }
10614                         case kGTOType_D: {
10615                                 Double xx, yy, zz, xy, xz, yz;
10616                                 xx = yy = zz = xy = xz = yz = 0;
10617                                 for (j = 0; j < sp->nprim; j++) {
10618                                         tval = exp(-pp->A * tmpp[3]);
10619                                         xx += *cnp++ * tval;
10620                                         yy += *cnp++ * tval;
10621                                         zz += *cnp++ * tval;
10622                                         xy += *cnp++ * tval;
10623                                         xz += *cnp++ * tval;
10624                                         yz += *cnp++ * tval;
10625                                         pp++;
10626                                 }
10627                                 xx *= mop[0] * tmpp[0] * tmpp[0];
10628                                 yy *= mop[1] * tmpp[1] * tmpp[1];
10629                                 zz *= mop[2] * tmpp[2] * tmpp[2];
10630                                 xy *= mop[3] * tmpp[0] * tmpp[1];
10631                                 xz *= mop[4] * tmpp[0] * tmpp[2];
10632                                 yz *= mop[5] * tmpp[1] * tmpp[2];
10633                                 val += xx + yy + zz + xy + xz + yz;
10634                                 break;
10635                         }
10636                         case kGTOType_D5: {
10637                                 Double d0, d1p, d1n, d2p, d2n;
10638                                 d0 = d1p = d1n = d2p = d2n = 0;
10639                                 for (j = 0; j < sp->nprim; j++) {
10640                                         tval = exp(-pp->A * tmpp[3]);
10641                                         d0 += *cnp++ * tval;
10642                                         d1p += *cnp++ * tval;
10643                                         d1n += *cnp++ * tval;
10644                                         d2p += *cnp++ * tval;
10645                                         d2n += *cnp++ * tval;
10646                                         pp++;
10647                                 }
10648                                 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
10649                                 d1p *= mop[1] * tmpp[0] * tmpp[2];
10650                                 d1n *= mop[2] * tmpp[1] * tmpp[2];
10651                                 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
10652                                 d2n *= mop[4] * tmpp[0] * tmpp[1];
10653                                 val += d0 + d1p + d1n + d2p + d2n;
10654                                 break;
10655                         }
10656                         /*  TODO: Support F/F7 and G/G9 type orbitals  */
10657                 }
10658         }
10659         return val;
10660 }
10661
10662 /*  Calculate one MO. The input vectors should be in bohr unit (angstrom * 1.889725989 = kAngstrom2Bohr).  */
10663 /*  mono is the MO number (1-based)  */
10664 int
10665 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)
10666 {
10667         int ix, iy, iz, n, nn;
10668         Cube *cp;
10669         Double *tmp;
10670         if (mp == NULL || mp->bset == NULL)
10671                 return -1;
10672         if (mp->bset->cns == NULL) {
10673                 if (sSetupGaussianCoefficients(mp->bset) != 0)
10674                         return -1;
10675         }
10676         cp = (Cube *)calloc(sizeof(Cube), 1);
10677         if (cp == NULL) {
10678                 return -1;
10679         }
10680         cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
10681         if (cp->dp == NULL) {
10682                 free(cp);
10683                 return -1;
10684         }
10685         cp->idn = mono;
10686         cp->origin = *op;
10687         cp->dx = *dxp;
10688         cp->dy = *dyp;
10689         cp->dz = *dzp;
10690         cp->nx = nx;
10691         cp->ny = ny;
10692         cp->nz = nz;
10693         
10694         /*  TODO: use multithread  */
10695         tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms * 4);
10696         if (tmp == NULL) {
10697                 free(cp->dp);
10698                 free(cp);
10699                 return -1;
10700         }
10701         n = nn = 0;
10702         for (ix = 0; ix < nx; ix++) {
10703                 Vector p;
10704                 for (iy = 0; iy < ny; iy++) {
10705                         for (iz = 0; iz < nz; iz++) {
10706                                 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
10707                                 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
10708                                 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
10709                                 cp->dp[n++] = sCalcMOPoint(mp->bset, mono, &p, tmp);
10710                         }
10711                         if (callback != NULL && n - nn > 100) {
10712                                 nn = n;
10713                                 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
10714                                         free(cp->dp);
10715                                         free(cp);
10716                                         free(tmp);
10717                                         return -2;  /*  User interrupt  */
10718                                 }
10719                         }
10720                 }
10721         }
10722         free(tmp);
10723
10724         AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
10725         return mp->bset->ncubes - 1;
10726 }
10727
10728 int
10729 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
10730 {
10731         int i;
10732         Vector rmin, rmax, *vp;
10733         Double dr, dx, dy, dz;
10734         if (mp == NULL || mp->bset == NULL || mp->bset->natoms == 0)
10735                 return -1;
10736         if (npoints <= 0)
10737                 npoints = 1000000;
10738         rmin.x = rmin.y = rmin.z = 1e10;
10739         rmax.x = rmax.y = rmax.z = -1e10;
10740         for (i = 0, vp = mp->bset->pos; i < mp->bset->natoms; i++, vp++) {
10741                 dr = RadiusForAtomicNumber(ATOM_AT_INDEX(mp->atoms, i)->atomicNumber);
10742                 if (dr == 0.0)
10743                         dr = 1.0;
10744                 dr = dr * kAngstrom2Bohr * 3.0 + 2.0;
10745                 if (rmin.x > vp->x - dr)
10746                         rmin.x = vp->x - dr;
10747                 if (rmin.y > vp->y - dr)
10748                         rmin.y = vp->y - dr;
10749                 if (rmin.z > vp->z - dr)
10750                         rmin.z = vp->z - dr;
10751                 if (rmax.x < vp->x + dr)
10752                         rmax.x = vp->x + dr;
10753                 if (rmax.y < vp->y + dr)
10754                         rmax.y = vp->y + dr;
10755                 if (rmax.z < vp->z + dr)
10756                         rmax.z = vp->z + dr;
10757         }
10758         dx = rmax.x - rmin.x;
10759         dy = rmax.y - rmin.y;
10760         dz = rmax.z - rmin.z;
10761         dr = pow(dx * dy * dz / npoints, 1.0/3.0);
10762         *nx = floor(dx / dr + 0.5);
10763         *ny = floor(dy / dr + 0.5);
10764         *nz = floor(dz / dr + 0.5);
10765         if (*nx == 0)
10766                 *nx = 1;
10767         if (*ny == 0)
10768                 *ny = 1;
10769         if (*nz == 0)
10770                 *nz = 1;
10771         *op = rmin;
10772         xp->x = yp->y = zp->z = dr;
10773         xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
10774         return 0;
10775 }
10776
10777 const Cube *
10778 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
10779 {
10780         if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
10781                 return NULL;
10782         return mp->bset->cubes[index];
10783 }
10784
10785 int
10786 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
10787 {
10788         int i;
10789         if (mp == NULL || mp->bset == NULL)
10790                 return -1;
10791         for (i = 0; i < mp->bset->ncubes; i++) {
10792                 if (mp->bset->cubes[i]->idn == mono)
10793                         return i;
10794         }
10795         return -1;
10796 }
10797
10798 int
10799 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
10800 {
10801         int n;
10802         if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
10803                 return -1;
10804         CubeRelease(mp->bset->cubes[index]);
10805         if (index < n - 1)
10806                 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
10807         if (--(mp->bset->ncubes) == 0) {
10808                 free(mp->bset->cubes);
10809                 mp->bset->cubes = NULL;
10810         }
10811         return mp->bset->ncubes;
10812 }
10813
10814 int
10815 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
10816 {
10817         const Cube *cp;
10818         int i, j, k, n;
10819         FILE *fp;
10820         if (mp == NULL || mp->bset == NULL)
10821                 return -1;  /*  Molecule or the basis set information is empty  */
10822         cp = MoleculeGetCubeAtIndex(mp, index);
10823         if (cp == NULL)
10824                 return -2;  /*  MO not yet calculated  */
10825         fp = fopen(fname, "wb");
10826         if (fp == NULL)
10827                 return -3;  /*  Cannot create file  */
10828
10829         /*  Comment lines  */
10830         fprintf(fp, "%s MO=%d\n", comment, cp->idn);
10831         fprintf(fp, " MO coefficients\n");
10832         
10833         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms), cp->origin.x, cp->origin.y, cp->origin.z);
10834         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx, cp->dx.x, cp->dx.y, cp->dx.z);
10835         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny, cp->dy.x, cp->dy.y, cp->dy.z);
10836         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz, cp->dz.x, cp->dz.y, cp->dz.z);
10837         
10838         /*  Atomic information  */
10839         for (i = 0; i < mp->bset->natoms; i++) {
10840                 Vector *vp = mp->bset->pos + i;
10841                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
10842                 /*  The second number should actually be the effective charge  */
10843                 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber, vp->x, vp->y, vp->z);
10844         }
10845         fprintf(fp, "%5d%5d\n", 1, 1);
10846         
10847         /*  3D data  */
10848         for (i = n = 0; i < cp->nx; i++) {
10849                 for (j = 0; j < cp->ny; j++) {
10850                         for (k = 0; k < cp->nz; k++) {
10851                                 fprintf(fp, " %12.5e", cp->dp[n++]);
10852                                 if (k == cp->nz - 1 || k % 6 == 5)
10853                                         fprintf(fp, "\n");
10854                         }
10855                 }
10856         }
10857         fclose(fp);
10858         return 0;
10859 }