OSDN Git Service

Importing PM3/GAMESS log is improved. (The atomic information was not recognized...
[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[16];
770         float mview_fbuf[8];
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 < 8; i++)
792                 mview_fbuf[i] = kUndefined;
793         for (i = 0; i < 16; 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, "!:mm_exclude") == 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 mm_exclude periodic_exclude */
887                                 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
888                                         s_append_asprintf(errbuf, "line %d: mm_exclude flags 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 mm_exclude flags\n", lineNumber);
893                                         goto err_exit;
894                                 }
895                                 ap = ATOM_AT_INDEX(mp->atoms, i);
896                                 ap->mm_exclude = (ibuf[1] != 0);
897                                 ap->periodic_exclude = (ibuf[2] != 0);
898                                 i++;
899                         }
900                         continue;
901                 } else if (strcmp(buf, "!:pi_anchor") == 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 count */
908                                 if ((j = sscanf(buf, "%d %d", &ibuf[0], &ibuf[1])) < 2) {
909                                         s_append_asprintf(errbuf, "line %d: bad format for pi_anchor", lineNumber);
910                                         goto err_exit;
911                                 }
912                                 i = ibuf[0];
913                                 ap = ATOM_AT_INDEX(mp->atoms, i);
914                                 if (ap->anchor != NULL) {
915                                         s_append_asprintf(errbuf, "line %d: warning: duplicate pi_anchor entry", lineNumber);
916                                         AtomConnectResize(&ap->anchor->connect, 0);
917                                         free(ap->anchor->coeffs);
918                                         free(ap->anchor);
919                                 }
920                                 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
921                                 if (ibuf[1] < 2 || ibuf[1] >= mp->natoms) {
922                                         s_append_asprintf(errbuf, "line %d: bad number of components for pi_anchor", lineNumber);
923                                         goto err_exit;
924                                 }
925                                 AtomConnectResize(&ap->anchor->connect, ibuf[1]);
926                                 ip = AtomConnectData(&ap->anchor->connect);
927                                 NewArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), ibuf[1]);
928                                 j = ibuf[1];
929                                 for (i = 0; i < j; i++) {
930                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
931                                                 s_append_asprintf(errbuf, "line %d: unexpected end of file while reading pi_anchors", lineNumber);
932                                                 goto err_exit;
933                                         }
934                                         if (sscanf(buf, "%d %lf", &ibuf[0], &dbuf[0]) < 2) {
935                                                 s_append_asprintf(errbuf, "line %d: bad format for pi_anchor", lineNumber);
936                                                 goto err_exit;
937                                         }
938                                         if (ibuf[0] < 0 || ibuf[0] >= mp->natoms) {
939                                                 s_append_asprintf(errbuf, "line %d: atom index out of range", lineNumber);
940                                                 goto err_exit;
941                                         }
942                                         if (dbuf[0] <= 0.0) {
943                                                 s_append_asprintf(errbuf, "line %d: the pi anchor weights should be positive", lineNumber);
944                                                 goto err_exit;
945                                         }
946                                         ip[i] = ibuf[0];
947                                         ap->anchor->coeffs[i] = dbuf[0];
948                                 }
949                         }
950                         continue;
951                 } else if (strcmp(buf, "!:positions") == 0) {
952                         i = 0;
953                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
954                                 if (buf[0] == '!')
955                                         continue;
956                                 if (buf[0] == '\n')
957                                         break;
958                                 /* idx x y z */
959                                 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) {
960                                         s_append_asprintf(errbuf, "line %d: atom position cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
961                                         goto err_exit;
962                                 }
963                                 if (j > 4 && nframes != 0) {
964                                         s_append_asprintf(errbuf, "line %d: atom position sigma can only be given for frame 0", lineNumber);
965                                         goto err_exit;
966                                 }
967                                 if (j > 4 && j != 7) {
968                                         s_append_asprintf(errbuf, "line %d: atom position sigma cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
969                                         goto err_exit;
970                                 }
971                                 if (i >= mp->natoms) {
972                                         s_append_asprintf(errbuf, "line %d: too many atom position records\n", lineNumber);
973                                         goto err_exit;
974                                 }
975                                 v.x = dbuf[0];
976                                 v.y = dbuf[1];
977                                 v.z = dbuf[2];
978                                 ap = ATOM_AT_INDEX(mp->atoms, i);
979                                 if (nframes > 0) {
980                                         AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes, &v);
981                                         if (nframes == 1)
982                                                 ap->frames[0] = ap->r;
983                                 }
984                                 ap->r = v;
985                                 if (j == 7) {
986                                         ap->sigma.x = dbuf[3];
987                                         ap->sigma.y = dbuf[4];
988                                         ap->sigma.z = dbuf[5];
989                                 }
990                                 i++;
991                         }
992                         nframes++;
993                         if (nframes >= 2) {
994                                 mp->nframes = nframes;
995                                 mp->cframe = nframes - 1;
996                         } else {
997                                 mp->nframes = mp->cframe = 0;
998                         }
999                         continue;
1000                 } else if (strcmp(buf, "!:bonds") == 0) {
1001                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1002                                 if (buf[0] == '!')
1003                                         continue;
1004                                 if (buf[0] == '\n')
1005                                         break;
1006                                 /* from1 to1 from2 to2 from3 to3 from4 to4 */ 
1007                                 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]);
1008                                 if (i < 2 || i % 2 != 0) {
1009                                         s_append_asprintf(errbuf, "line %d: bad bond format", lineNumber);
1010                                         goto err_exit;
1011                                 }
1012                                 for (j = 0; j < i; j += 2) {
1013                                         iibuf[0] = ibuf[j];
1014                                         iibuf[1] = ibuf[j + 1];
1015                                         if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[0] == iibuf[1]) {
1016                                                 s_append_asprintf(errbuf, "line %d: warning: bad bond specification (%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1]);
1017                                                 nwarnings++;
1018                                         } else if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[0])->connect), iibuf[1])) {
1019                                                 s_append_asprintf(errbuf, "line %d: warning: bond %d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1]);
1020                                                 nwarnings++;
1021                                         } else {
1022                                                 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, iibuf);
1023                                                 AtomConnectInsertEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[0])->connect), -1, iibuf[1]);
1024                                                 AtomConnectInsertEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[1])->connect), -1, iibuf[0]);
1025                                         }
1026                                 }
1027                         }
1028                         continue;
1029                 } else if (strcmp(buf, "!:angles") == 0) {
1030                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1031                                 if (buf[0] == '!')
1032                                         continue;
1033                                 if (buf[0] == '\n')
1034                                         break;
1035                                 /* a1 b1 c1 a2 b2 c2 a3 b3 c3 */ 
1036                                 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]);
1037                                 if (i == 0 || i % 3 != 0) {
1038                                         s_append_asprintf(errbuf, "line %d: bad angle format", lineNumber);
1039                                         goto err_exit;
1040                                 }
1041                                 for (j = 0; j < i; j += 3) {
1042                                         iibuf[0] = ibuf[j];
1043                                         iibuf[1] = ibuf[j + 1];
1044                                         iibuf[2] = ibuf[j + 2];
1045                                         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]) {
1046                                                 s_append_asprintf(errbuf, "line %d: warning: bad angle specification (%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1047                                                 nwarnings++;
1048                                         } else if (MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[2]) == 0) {
1049                                                 s_append_asprintf(errbuf, "line %d: warning: angle with non-bonded atoms (%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1050                                                 nwarnings++;                                            
1051                                         } else if (MoleculeLookupAngle(mp, iibuf[0], iibuf[1], iibuf[2]) >= 0) {
1052                                                 s_append_asprintf(errbuf, "line %d: warning: angle %d-%d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1053                                                 nwarnings++;
1054                                         } else {
1055                                                 AssignArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, mp->nangles, iibuf);
1056                                         }
1057                                 }
1058                         }
1059                         continue;
1060                 } else if (strcmp(buf, "!:dihedrals") == 0) {
1061                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1062                                 if (buf[0] == '!')
1063                                         continue;
1064                                 if (buf[0] == '\n')
1065                                         break;
1066                                 /* a1 b1 c1 d1 a2 b2 c2 d2 */ 
1067                                 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]);
1068                                 if (i == 0 || i % 4 != 0) {
1069                                         s_append_asprintf(errbuf, "line %d: bad dihedral format", lineNumber);
1070                                         goto err_exit;
1071                                 }
1072                                 for (j = 0; j < i; j += 4) {
1073                                         iibuf[0] = ibuf[j];
1074                                         iibuf[1] = ibuf[j + 1];
1075                                         iibuf[2] = ibuf[j + 2];
1076                                         iibuf[3] = ibuf[j + 3];
1077                                         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]) {
1078                                                 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]);
1079                                                 nwarnings++;
1080                                         } else if (MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[2]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[3]) == 0) {
1081                                                 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]);
1082                                                 nwarnings++;                                            
1083                                         } else if (MoleculeLookupDihedral(mp, iibuf[0], iibuf[1], iibuf[2], iibuf[3]) >= 0) {
1084                                                 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]);
1085                                                 nwarnings++;
1086                                         } else {
1087                                                 AssignArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, mp->ndihedrals, iibuf);
1088                                         }
1089                                 }
1090                         }
1091                         continue;
1092                 } else if (strcmp(buf, "!:impropers") == 0) {
1093                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1094                                 if (buf[0] == '!')
1095                                         continue;
1096                                 if (buf[0] == '\n')
1097                                         break;
1098                                 /* a1 b1 c1 d1 a2 b2 c2 d2 */ 
1099                                 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]);
1100                                 if (i == 0 || i % 4 != 0) {
1101                                         s_append_asprintf(errbuf, "line %d: bad improper format", lineNumber);
1102                                         goto err_exit;
1103                                 }
1104                                 for (j = 0; j < i; j += 4) {
1105                                         iibuf[0] = ibuf[j];
1106                                         iibuf[1] = ibuf[j + 1];
1107                                         iibuf[2] = ibuf[j + 2];
1108                                         iibuf[3] = ibuf[j + 3];
1109                                         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]) {
1110                                                 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]);
1111                                                 nwarnings++;
1112                                         } else if (MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[1]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[3]) == 0) {
1113                                                 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]);
1114                                                 nwarnings++;                                            
1115                                         } else if (MoleculeLookupImproper(mp, iibuf[0], iibuf[1], iibuf[2], iibuf[3]) >= 0) {
1116                                                 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]);
1117                                                 nwarnings++;
1118                                         } else {
1119                                                 AssignArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, mp->nimpropers, iibuf);
1120                                         }
1121                                 }
1122                         }
1123                         continue;
1124                 } else if (strcmp(buf, "!:xtalcell") == 0 && mp->cell == NULL) {
1125                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1126                                 if (buf[0] == '!')
1127                                         continue;
1128                                 if (buf[0] == '\n')
1129                                         break;
1130                                 /* a b c alpha beta gamma [sigmaflag] */ 
1131                                 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) {
1132                                         s_append_asprintf(errbuf, "line %d: bad xtalcell format", lineNumber);
1133                                         goto err_exit;
1134                                 }
1135                                 MoleculeSetCell(mp, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], 0);
1136                                 if (j == 7 && ibuf[0] != 0) {
1137                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1138                                                 s_append_asprintf(errbuf, "line %d: sigma for xtalcell are missing", lineNumber);
1139                                                 goto err_exit;
1140                                         }
1141                                         if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1142                                                 s_append_asprintf(errbuf,"line %d: bad xtalcell sigma format", lineNumber);
1143                                                 goto err_exit;
1144                                         }
1145                                         if (mp->cell != NULL) {
1146                                                 mp->cell->has_sigma = 1;
1147                                                 for (i = 0; i < 6; i++) {
1148                                                         mp->cell->cellsigma[i] = dbuf[i];
1149                                                 }
1150                                         } else {
1151                                                 s_append_asprintf(errbuf, "line %d: cell sigma are given while cell is not given", lineNumber);
1152                                         }
1153                                 }
1154                         }
1155                         continue;
1156                 } else if (strcmp(buf, "!:symmetry_operations") == 0) {
1157                         i = 0;
1158                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1159                                 Transform tr;
1160                                 if (buf[0] == '!')
1161                                         continue;
1162                                 if (buf[0] == '\n')
1163                                         break;
1164                                 /* a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3 */
1165                                 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1166                                         s_append_asprintf(errbuf, "line %d: bad symmetry_operation format", lineNumber);
1167                                         goto err_exit;
1168                                 }
1169                                 if (i < 3) {
1170                                         tr[i] = dbuf[0];
1171                                         tr[i + 3] = dbuf[1];
1172                                         tr[i + 6] = dbuf[2];
1173                                 } else {
1174                                         tr[9] = dbuf[0];
1175                                         tr[10] = dbuf[1];
1176                                         tr[11] = dbuf[2];
1177                                 }
1178                                 i++;
1179                                 if (i == 4) {
1180                                         AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1181                                         i = 0;
1182                                 }
1183                         }
1184                         continue;
1185                 } else if (strcmp(buf, "!:anisotropic_thermal_parameters") == 0) {
1186                         i = 0;
1187                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1188                                 if (buf[0] == '!')
1189                                         continue;
1190                                 if (buf[0] == '\n')
1191                                         break;
1192                                 /* b11 b22 b33 b12 b13 b23 [has_sigma] */
1193                                 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) {
1194                                         s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters cannot be read for atom %d", lineNumber, i + 1);
1195                                         goto err_exit;
1196                                 }
1197                                 if (i >= mp->natoms) {
1198                                         s_append_asprintf(errbuf, "line %d: too many anisotropic thermal parameters\n", lineNumber);
1199                                         goto err_exit;
1200                                 }
1201                                 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) {
1202                                         /*  Skip it  */
1203                                 } else {
1204                                         MoleculeSetAniso(mp, i, 0, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], NULL);
1205                                 }
1206                                 if (j == 7 && ibuf[0] != 0) {
1207                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1208                                                 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma missing", lineNumber);
1209                                                 goto err_exit;
1210                                         }
1211                                         if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1212                                                 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma cannot be read for atom %d", lineNumber, i + 1);
1213                                                 goto err_exit;
1214                                         }
1215                                         ap = ATOM_AT_INDEX(mp->atoms, i);
1216                                         if (ap->aniso == NULL) {
1217                                                 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma are given while the parameters are not given", lineNumber);
1218                                                 goto err_exit;
1219                                         }
1220                                         ap->aniso->has_bsig = 1;
1221                                         for (j = 0; j < 6; j++)
1222                                                 ap->aniso->bsig[j] = dbuf[j];
1223                                 }
1224                                 i++;
1225                         }
1226                         continue;
1227                 } else if (strcmp(buf, "!:periodic_box") == 0) {
1228                         Vector vs[5];
1229                         Byte has_sigma = 0;
1230                         i = 0;
1231                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1232                                 if (buf[0] == '!')
1233                                         continue;
1234                                 if (buf[0] == '\n')
1235                                         break;
1236                                 /* 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] */
1237                                 if (i < 4) {
1238                                         if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1239                                                 s_append_asprintf(errbuf, "line %d: bad periodic_box format", lineNumber);
1240                                                 goto err_exit;
1241                                         }
1242                                         vs[i].x = dbuf[0];
1243                                         vs[i].y = dbuf[1];
1244                                         vs[i].z = dbuf[2];
1245                                         i++;
1246                                         continue;
1247                                 }
1248                                 if ((j = sscanf(buf, "%d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3])) < 3) {
1249                                         s_append_asprintf(errbuf, "line %d: bad periodic_box format", lineNumber);
1250                                         goto err_exit;
1251                                 }
1252                                 if (j == 4 && ibuf[3] != 0)
1253                                         has_sigma = 1;
1254                                 cbuf[0][0] = ibuf[0];
1255                                 cbuf[0][1] = ibuf[1];
1256                                 cbuf[0][2] = ibuf[2];
1257                                 MoleculeSetPeriodicBox(mp, vs, vs + 1, vs + 2, vs + 3, cbuf[0], 0);
1258                                 if (has_sigma) {
1259                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1260                                                 s_append_asprintf(errbuf, "line %d: sigma for cell parameters are missing", lineNumber);
1261                                                 goto err_exit;
1262                                         }
1263                                         if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1264                                                 s_append_asprintf(errbuf, "line %d: bad periodic_box sigma format", lineNumber);
1265                                                 goto err_exit;
1266                                         }
1267                                         if (mp->cell != NULL) {
1268                                                 mp->cell->has_sigma = 1;
1269                                                 for (i = 0; i < 6; i++) {
1270                                                         mp->cell->cellsigma[i] = dbuf[i];
1271                                                 }
1272                                         } else {
1273                                                 s_append_asprintf(errbuf, "line %d: cell sigma are given while cell is not given", lineNumber);
1274                                         }
1275                                 }
1276                                 break;
1277                         }
1278                         continue;
1279                 } else if (strcmp(buf, "!:frame_periodic_boxes") == 0) {
1280                         Vector vs[5];
1281                         i = 0;
1282                 /*      mp->useFlexibleCell = 1;  *//*  The presence of this block causes asserting this flag  */
1283                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1284                                 if (buf[0] == '!')
1285                                         continue;
1286                                 if (buf[0] == '\n')
1287                                         break;
1288                                 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1289                                         s_append_asprintf(errbuf, "line %d: bad frame_periodic_box format", lineNumber);
1290                                         goto err_exit;
1291                                 }
1292                                 vs[i].x = dbuf[0];
1293                                 vs[i].y = dbuf[1];
1294                                 vs[i].z = dbuf[2];
1295                                 i++;
1296                                 if (i == 4) {
1297                                         AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells, vs);
1298                                         i = 0;
1299                                 }
1300                         }
1301                         if (mp->cframe < mp->nframe_cells) {
1302                                 /*  mp->cframe should already have been set when positions are read  */
1303                                 Vector *vp = &mp->frame_cells[mp->cframe * 4];
1304                                 static char defaultFlags[] = {1, 1, 1};
1305                                 char *flags = (mp->cell != NULL ? mp->cell->flags : defaultFlags);
1306                                 MoleculeSetPeriodicBox(mp, vp, vp + 1, vp + 2, vp + 3, flags, 0);
1307                         }
1308                         continue;
1309                 } else if (strcmp(buf, "!:md_parameters") == 0) {
1310                         MDArena *arena;
1311                         if (mp->arena == NULL)
1312                                 mp->arena = md_arena_new(NULL);
1313                         arena = mp->arena;
1314                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1315                                 if (buf[0] == '!')
1316                                         continue;
1317                                 if (buf[0] == '\n')
1318                                         break;
1319                                 bufp = buf;
1320                                 comp = strsep(&bufp, " \t");
1321                                 if (bufp != NULL) {
1322                                         while (*bufp == ' ' || *bufp == '\t')
1323                                                 bufp++;
1324                                         valp = strsep(&bufp, "\n");
1325                                 } else valp = NULL;
1326                                 if (strcmp(comp, "alchem_flags") == 0) {
1327                                         j = (valp == NULL ? 0 : atoi(valp));
1328                                         if (j > 0) {
1329                                                 valp = (char *)malloc(j);
1330                                                 i = 0;
1331                                                 while ((k = fgetc(fp)) >= 0) {
1332                                                         ungetc(k, fp);
1333                                                         if (k < '0' || k > '9') {
1334                                                                 s_append_asprintf(errbuf, "line %d: too few flags in alchem_flags block", lineNumber + 1);
1335                                                                 free(valp);
1336                                                                 goto err_exit;
1337                                                         }
1338                                                         ReadLine(buf, sizeof buf, fp, &lineNumber);
1339                                                         bufp = buf;
1340                                                         while (*bufp != 0) {
1341                                                                 if (*bufp >= '0' && *bufp <= '2') {
1342                                                                         if (i >= j) {
1343                                                                                 s_append_asprintf(errbuf, "line %d: too many flags in alchem_flags block", lineNumber);
1344                                                                                 free(valp);
1345                                                                                 goto err_exit;
1346                                                                         }
1347                                                                         valp[i++] = *bufp - '0';
1348                                                                 } else if (*bufp != ' ' && *bufp != '\t' && *bufp != '\n') {
1349                                                                         s_append_asprintf(errbuf, "line %d: strange character (0x%02x) in alchem_flags block", lineNumber, (int)*bufp);
1350                                                                         free(valp);
1351                                                                         goto err_exit;
1352                                                                 }
1353                                                                 bufp++;
1354                                                         }
1355                                                         if (i == j)
1356                                                                 break;
1357                                                 }
1358                                                 md_set_alchemical_flags(arena, j, valp);
1359                                                 free(valp);
1360                                         }
1361                                         continue;
1362                                 }
1363                                 /*  In the following, the redundant "!= NULL" is to suppress suprious warning  */
1364                                 if ((strcmp(comp, "log_file") == 0 && (pp = &arena->log_result_name) != NULL)
1365                                         || (strcmp(comp, "coord_file") == 0 && (pp = &arena->coord_result_name) != NULL)
1366                                         || (strcmp(comp, "vel_file") == 0 && (pp = &arena->vel_result_name) != NULL)
1367                                         || (strcmp(comp, "force_file") == 0 && (pp = &arena->force_result_name) != NULL)
1368                                         || (strcmp(comp, "debug_file") == 0 && (pp = &arena->debug_result_name) != NULL)) {
1369                                         if (*valp == 0 || strstr(valp, "(null)") == valp)
1370                                                 *pp = NULL;
1371                                         else {
1372                                                 valp = strdup(valp);
1373                                                 if (valp != NULL) {
1374                                                         char *valp1 = strchr(valp, '\n');
1375                                                         if (valp1 != NULL)
1376                                                                 *valp1 = 0;
1377                                                 }
1378                                                 *pp = valp;
1379                                         }
1380                                 } else if ((strcmp(comp, "debug_output_level") == 0 && (ip = &arena->debug_output_level) != NULL)
1381                                                    || (strcmp(comp, "coord_output_freq") == 0 && (ip = &arena->coord_output_freq) != NULL)
1382                                                    || (strcmp(comp, "energy_output_freq") == 0 && (ip = &arena->energy_output_freq) != NULL)
1383                                                    || (strcmp(comp, "coord_frame") == 0 && (ip = &arena->coord_result_frame) != NULL)
1384                                                    || (strcmp(comp, "andersen_freq") == 0 && (ip = &arena->andersen_thermo_freq) != NULL)
1385                                                    || (strcmp(comp, "random_seed") == 0 && (ip = &arena->random_seed) != NULL)
1386                                                    || (strcmp(comp, "use_xplor_shift") == 0 && (ip = &arena->use_xplor_shift) != NULL)
1387                                                    || (strcmp(comp, "relocate_center") == 0 && (ip = &arena->relocate_center) != NULL)
1388                                                    || (strcmp(comp, "surface_potential_freq") == 0 && (ip = &arena->surface_potential_freq) != NULL)
1389                                                    || (strcmp(comp, "use_graphite") == 0 && (ip = &arena->use_graphite) != NULL)) {
1390                                         *ip = (valp == NULL ? 0 : atoi(valp));
1391                                 } else if ((strcmp(comp, "timestep") == 0 && (dp = &arena->timestep) != NULL)
1392                                                    || (strcmp(comp, "cutoff") == 0 && (dp = &arena->cutoff) != NULL)
1393                                                    || (strcmp(comp, "electro_cutoff") == 0 && (dp = &arena->electro_cutoff) != NULL)
1394                                                    || (strcmp(comp, "pairlist_distance") == 0 && (dp = &arena->pairlist_distance) != NULL)
1395                                                    || (strcmp(comp, "switch_distance") == 0 && (dp = &arena->switch_distance) != NULL)
1396                                                    || (strcmp(comp, "temperature") == 0 && (dp = &arena->temperature) != NULL)
1397                                                    || (strcmp(comp, "andersen_coupling") == 0 && (dp = &arena->andersen_thermo_coupling) != NULL)
1398                                                    || (strcmp(comp, "dielectric") == 0 && (dp = &arena->dielectric) != NULL)
1399                                                    || (strcmp(comp, "gradient_convergence") == 0 && (dp = &arena->gradient_convergence) != NULL)
1400                                                    || (strcmp(comp, "coordinate_convergence") == 0 && (dp = &arena->coordinate_convergence) != NULL)
1401                                                    || (strcmp(comp, "scale14_vdw") == 0 && (dp = &arena->scale14_vdw) != NULL)
1402                                                    || (strcmp(comp, "scale14_elect") == 0 && (dp = &arena->scale14_elect) != NULL)
1403                                                    || (strcmp(comp, "surface_probe_radius") == 0 && (dp = &arena->probe_radius) != NULL)
1404                                                    || (strcmp(comp, "surface_tension") == 0 && (dp = &arena->surface_tension) != NULL)
1405                                                    || (strcmp(comp, "alchemical_lambda") == 0 && (dp = &arena->alchem_lambda) != NULL)
1406                                                    || (strcmp(comp, "alchemical_delta_lambda") == 0 && (dp = &arena->alchem_dlambda) != NULL)) {
1407                                         *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1408                                 }
1409                         }
1410                         continue;
1411                 } else if (strcmp(buf, "!:pressure_control_parameters") == 0) {
1412                         MDPressureArena *pressure;
1413                         if (mp->arena == NULL)
1414                                 mp->arena = md_arena_new(mp);
1415                         if (mp->arena->pressure == NULL)
1416                                 mp->arena->pressure = pressure_new();
1417                         pressure = mp->arena->pressure;
1418                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1419                                 if (buf[0] == '!')
1420                                         continue;
1421                                 if (buf[0] == '\n')
1422                                         break;
1423                                 bufp = buf;
1424                                 comp = strsep(&bufp, " \t");
1425                                 if (bufp != NULL) {
1426                                         while (*bufp == ' ' || *bufp == '\t')
1427                                                 bufp++;
1428                                         valp = strsep(&bufp, "\n");
1429                                 } else valp = NULL;
1430                                 if (strcmp(comp, "pressure") == 0) {
1431                                         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) {
1432                                                 s_append_asprintf(errbuf, "line %d: bad format", lineNumber);
1433                                                 goto err_exit;
1434                                         }
1435                                         for (i = 0; i < 9; i++)
1436                                                 pressure->apply[i] = dbuf[i];
1437                                 } else if (strcmp(comp, "pressure_cell_flexibility") == 0) {
1438                                         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) {
1439                                                 s_append_asprintf(errbuf, "line %d: bad format", lineNumber);
1440                                                 goto err_exit;
1441                                         }
1442                                         for (i = 0; i < 8; i++)
1443                                                 pressure->cell_flexibility[i] = dbuf[i];
1444                                 } else if ((strcmp(comp, "pressure_freq") == 0 && (ip = &pressure->freq) != NULL)) {
1445                                         *ip = (valp == NULL ? 0 : atoi(valp));
1446                                 } else if ((strcmp(comp, "pressure_coupling") == 0 && (dp = &pressure->coupling) != NULL)
1447                                                    || (strcmp(comp, "pressure_fluctuate_cell_origin") == 0 && (dp = &pressure->fluctuate_cell_origin) != NULL)
1448                                                    || (strcmp(comp, "pressure_fluctuate_cell_orientation") == 0 && (dp = &pressure->fluctuate_cell_orientation) != NULL)) {
1449                                         *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1450                                 }
1451                         }
1452                         continue;
1453                 } else if (strcmp(buf, "!:velocity") == 0) {
1454                         i = 0;
1455                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1456                                 if (buf[0] == '!')
1457                                         continue;
1458                                 if (buf[0] == '\n')
1459                                         break;
1460                                 /* idx vx vy vz */
1461                                 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1462                                         s_append_asprintf(errbuf, "line %d: atom velocity cannot be read for atom %d", lineNumber, i + 1);
1463                                         goto err_exit;
1464                                 }
1465                                 if (i >= mp->natoms) {
1466                                         s_append_asprintf(errbuf, "line %d: too many atom velocity records\n", lineNumber);
1467                                         goto err_exit;
1468                                 }
1469                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1470                                 ap->v.x = dbuf[0];
1471                                 ap->v.y = dbuf[1];
1472                                 ap->v.z = dbuf[2];
1473                                 i++;
1474                         }
1475                         continue;
1476                 } else if (strcmp(buf, "!:force") == 0) {
1477                         i = 0;
1478                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1479                                 if (buf[0] == '!')
1480                                         continue;
1481                                 if (buf[0] == '\n')
1482                                         break;
1483                                 /* idx fx fy fz */
1484                                 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1485                                         s_append_asprintf(errbuf, "line %d: atom force cannot be read for atom %d", lineNumber, i + 1);
1486                                         goto err_exit;
1487                                 }
1488                                 if (i >= mp->natoms) {
1489                                         s_append_asprintf(errbuf, "line %d: too many atom force records\n", lineNumber);
1490                                         goto err_exit;
1491                                 }
1492                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1493                                 ap->f.x = dbuf[0];
1494                                 ap->f.y = dbuf[1];
1495                                 ap->f.z = dbuf[2];
1496                                 i++;
1497                         }
1498                         continue;
1499                 } else if (strcmp(buf, "!:parameter") == 0 || strcmp(buf, "!:parameters") == 0) {
1500                         Parameter *par = mp->par;
1501                         if (par == NULL) {
1502                                 mp->par = ParameterNew();
1503                                 par = mp->par;
1504                         }
1505                         bufp = NULL;
1506                         i = 0;
1507                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1508                                 if (buf[0] == '!')
1509                                         continue;
1510                                 if (buf[0] == '\n')
1511                                         break;
1512                                 j = ParameterReadFromString(par, buf, &bufp, fname, lineNumber, 0);
1513                                 if (j < 0) {
1514                                         s_append_asprintf(errbuf, "%s", bufp);
1515                                         goto err_exit;
1516                                 }
1517                                 i += j;
1518                         }
1519                         if (bufp != NULL) {
1520                                 MyAppCallback_setConsoleColor(1);
1521                                 MyAppCallback_showScriptMessage("%s", bufp);
1522                                 MyAppCallback_setConsoleColor(0);
1523                                 free(bufp);
1524                         }
1525                         continue;
1526                 } else if (strcmp(buf, "!:trackball") == 0) {
1527                         i = 0;
1528                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1529                                 if (buf[0] == '!')
1530                                         continue;
1531                                 if (buf[0] == '\n')
1532                                         break;
1533                                 /* scale; trx try trz; theta_deg x y z */
1534                                 if ((i == 0 && sscanf(buf, "%f", &mview_fbuf[0]) < 1)
1535                                         || (i == 1 && sscanf(buf, "%f %f %f",
1536                                                                                  &mview_fbuf[1], &mview_fbuf[2], &mview_fbuf[3]) < 3)
1537                                         || (i == 2 && sscanf(buf, "%f %f %f %f",
1538                                                                                  &mview_fbuf[4], &mview_fbuf[5], &mview_fbuf[6], &mview_fbuf[7]) < 4)) {
1539                                         s_append_asprintf(errbuf, "line %d: bad trackball format", lineNumber);
1540                                         goto err_exit;
1541                                 }
1542                                 i++;
1543                         }
1544                         continue;
1545                 } else if (strcmp(buf, "!:view") == 0) {
1546                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1547                                 if (buf[0] == '!')
1548                                         continue;
1549                                 if (buf[0] == '\n')
1550                                         break;
1551                                 bufp = buf;
1552                                 comp = strsep(&bufp, " \t");
1553                                 if (bufp != NULL) {
1554                                         while (*bufp == ' ' || *bufp == '\t')
1555                                                 bufp++;
1556                                         valp = strsep(&bufp, "\n");
1557                                 } else valp = NULL;
1558                                 /*  In the following, the redundant "!= NULL" is to suppress suprious warning  */
1559                                 if ((strcmp(comp, "show_unit_cell") == 0 && (i = 1))
1560                                         || (strcmp(comp, "show_periodic_box") == 0 && (i = 2))
1561                                         || (strcmp(comp, "show_expanded_atoms") == 0 && (i = 3))
1562                                         || (strcmp(comp, "show_ellipsoids") == 0 && (i = 4))
1563                                         || (strcmp(comp, "show_hydrogens") == 0 && (i = 5))
1564                                         || (strcmp(comp, "show_dummy_atoms") == 0 && (i = 6))
1565                                         || (strcmp(comp, "show_rotation_center") == 0 && (i = 7))
1566                                         || (strcmp(comp, "show_graphite_flag") == 0 && (i = 8))
1567                                         || (strcmp(comp, "show_periodic_image_flag") == 0 && (i = 9))
1568                                         || (strcmp(comp, "show_graphite") == 0 && (i = 10))) {
1569                                         mview_ibuf[i - 1] = atoi(valp);
1570                                 } else if (strcmp(comp, "show_periodic_image") == 0) {
1571                                         sscanf(valp, "%d %d %d %d %d %d",
1572                                                    &mview_ibuf[10], &mview_ibuf[11], &mview_ibuf[12],
1573                                                    &mview_ibuf[13], &mview_ibuf[14], &mview_ibuf[15]);
1574                                 }
1575                         }
1576                         continue;
1577                 }
1578                 /*  Unknown sections are silently ignored  */
1579         }
1580
1581         MoleculeCleanUpResidueTable(mp);
1582         if (mp->arena != NULL)
1583                 md_arena_set_molecule(mp->arena, mp);
1584
1585         fclose(fp);
1586         if (mp->mview != NULL) {
1587                 if (mview_ibuf[0] != kUndefined)
1588                         mp->mview->showUnitCell = mview_ibuf[0];
1589                 if (mview_ibuf[1] != kUndefined)
1590                         mp->mview->showPeriodicBox = mview_ibuf[1];
1591                 if (mview_ibuf[2] != kUndefined)
1592                         mp->mview->showExpandedAtoms = mview_ibuf[2];
1593                 if (mview_ibuf[3] != kUndefined)
1594                         mp->mview->showEllipsoids = mview_ibuf[3];
1595                 if (mview_ibuf[4] != kUndefined)
1596                         mp->mview->showHydrogens = mview_ibuf[4];
1597                 if (mview_ibuf[5] != kUndefined)
1598                         mp->mview->showDummyAtoms = mview_ibuf[5];
1599                 if (mview_ibuf[6] != kUndefined)
1600                         mp->mview->showRotationCenter = mview_ibuf[6];
1601                 if (mview_ibuf[7] != kUndefined)
1602                         mp->mview->showGraphiteFlag = mview_ibuf[7];
1603                 if (mview_ibuf[8] != kUndefined)
1604                         mp->mview->showPeriodicImageFlag = mview_ibuf[8];
1605                 if (mview_ibuf[9] != kUndefined)
1606                         mp->mview->showGraphite = mview_ibuf[9];
1607                 for (i = 0; i < 6; i++) {
1608                         if (mview_ibuf[10 + i] != kUndefined)
1609                                 mp->mview->showPeriodicImage[i] = mview_ibuf[10 + i];
1610                 }
1611                 if (mp->mview->track != NULL) {
1612                         if (mview_fbuf[0] != kUndefined)
1613                                 TrackballSetScale(mp->mview->track, mview_fbuf[0]);
1614                         if (mview_fbuf[1] != kUndefined)
1615                                 TrackballSetTranslate(mp->mview->track, mview_fbuf + 1);
1616                         if (mview_fbuf[4] != kUndefined)
1617                                 TrackballSetRotate(mp->mview->track, mview_fbuf + 4);
1618                 }
1619         }
1620
1621         return 0;
1622
1623 err_exit:
1624         fclose(fp);
1625         /*  The content of mp may be broken, so make it empty  */
1626         MoleculeClear(mp);
1627         return -1;      
1628 }
1629
1630 int
1631 MoleculeLoadPsfFile(Molecule *mp, const char *fname, char **errbuf)
1632 {
1633         FILE *fp;
1634         char buf[1024];
1635         char *p;
1636         int section = -1;
1637         int i, j, err, fn;
1638         int lineNumber;
1639         Int ibuf[12];
1640         Vector *frames = NULL;
1641         Atom *ap;
1642         err = 0;
1643         *errbuf = NULL;
1644         if (mp == NULL)
1645                 mp = MoleculeNew();
1646         else MoleculeClear(mp);
1647         fp = fopen(fname, "rb");
1648         if (fp == NULL) {
1649                 s_append_asprintf(errbuf, "Cannot open file");
1650                 return 1;
1651         }
1652 /*      flockfile(fp); */
1653         lineNumber = 0;
1654         fn = 0;
1655         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1656                 if (strncmp(buf, "PSF", 3) == 0) {
1657                         section = 0;
1658                         continue;
1659                 } else {
1660                         for (p = buf; *p != 0 && isspace(*p); p++) {}
1661                         if (*p == 0) {
1662                                 section++;
1663                                 continue;
1664                         }
1665                 }
1666                 if (strstr(buf, "!COORD") != NULL) {
1667                         /*  Extended psf file with coordinates  */
1668                         if (fn > 0) {
1669                                 /*  Allocate a temporary storage for frames  */
1670                                 size_t size = sizeof(Vector) * mp->natoms * fn;
1671                                 if (frames == NULL)
1672                                         frames = (Vector *)malloc(size);
1673                                 else
1674                                         frames = (Vector *)realloc(frames, size);
1675                                 if (frames == NULL)
1676                                         goto panic;
1677                         #if 0
1678                                 if (fn == 1) {
1679                                         /*  Copy the coordinates of the first frame  */
1680                                         for (i = 0; i < mp->natoms; i++) {
1681                                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1682                                                 frames[i] = ap->r;
1683                                         }
1684                                 }
1685                                 /*  Copy the coordinates of the last frame to the newly created frame  */
1686                                 memmove(frames + sizeof(Vector) * mp->natoms * fn, frames + sizeof(Vector) * mp->natoms * (fn - 1), sizeof(Vector) * mp->natoms);
1687                         #endif
1688                         }
1689                         /*  Read coordinates  */
1690                         for (i = 0; i < mp->natoms; i++) {
1691                                 double dval[3];
1692                                 Vector r;
1693                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1694                                         err = 1;
1695                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading coordinates (frame %d)", lineNumber, fn);
1696                                         goto exit;
1697                                 }
1698                                 if (sscanf(buf, "%lg %lg %lg", dval, dval + 1, dval + 2) != 3) {
1699                                         err = 1;
1700                                         s_append_asprintf(errbuf, "line %d: coordinates cannot be read for atom %d", lineNumber, i + 1);
1701                                         goto exit;
1702                                 }
1703                                 r.x = dval[0];
1704                                 r.y = dval[1];
1705                                 r.z = dval[2];
1706                                 if (fn == 0)
1707                                         ATOM_AT_INDEX(mp->atoms, i)->r = r;
1708                                 else
1709                                         frames[mp->natoms * (fn - 1) + i] = r;
1710                         }
1711                         fn++;
1712                         continue;
1713                 }
1714                 
1715                 if (section == 2) {
1716                         /*  Atoms  */
1717                         Int natoms;
1718                         ReadFormat(buf, "I8", &natoms);
1719                         if (natoms == 0)
1720                                 continue;
1721                         if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
1722                                 goto panic;
1723                         mp->nresidues = 0;
1724                         for (i = 0; i < natoms; i++) {
1725                                 struct {
1726                                         char segName[5], resName[4], atomName[5], atomType[3], element[3];
1727                                         Int serial;
1728                                 } w;
1729                                 memset(&w, 0, sizeof(w));
1730                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1731                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1732                                         err = 1;
1733                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading atoms", lineNumber);
1734                                         goto exit;
1735                                 }
1736                                 ReadFormat(buf, "I8 x1 S4 I5 x1 S3 x2 S4 x1 S4 F16 F10",
1737                                         &w.serial, w.segName, &ap->resSeq, w.resName, w.atomName, 
1738                                         w.atomType, &ap->charge, &ap->weight);
1739                                 strncpy(ap->segName, w.segName, 4);
1740                                 strncpy(ap->resName, w.resName, 3);
1741                                 strncpy(ap->aname, w.atomName, 4);
1742                                 ap->type = AtomTypeEncodeToUInt(w.atomType);
1743                                 /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
1744                                 ap->atomicNumber = GuessAtomicNumber(w.atomName, ap->weight);
1745                                 ElementToString(ap->atomicNumber, w.element);
1746                                 strncpy(ap->element, w.element, 2);
1747                         /*      w.element[0] = 0;
1748                                 for (p = w.atomName; *p != 0; p++) {
1749                                         if (isalpha(*p) && *p != '_') {
1750                                                 w.element[0] = toupper(*p);
1751                                                 if (isalpha(p[1]) && p[1] != '_') {
1752                                                         w.element[1] = toupper(p[1]);
1753                                                         w.element[2] = 0;
1754                                                 } else {
1755                                                         w.element[1] = 0;
1756                                                 }
1757                                                 break;
1758                                         }
1759                                 }
1760                                 strncpy(ap->element, w.element, 2);
1761                                 ap->atomicNumber = ElementToInt(w.element); */
1762                                 if (w.resName[0] == 0)
1763                                         strncpy(ap->resName, "XXX", 3);
1764                                 if (ap->resSeq > mp->nresidues)
1765                                         mp->nresidues = ap->resSeq;
1766                         }
1767                         if (mp->residues != NULL)
1768                                 free(mp->residues);
1769                         if (NewArray(&mp->residues, &mp->nresidues, sizeof(char (*)[4]), mp->nresidues + 1) == 0)
1770                                 goto panic;
1771                         for (i = 0; i < mp->natoms; i++) {
1772                                 j = mp->atoms[i].resSeq;
1773                                 if (mp->residues[j][0] == 0)
1774                                         strncpy(mp->residues[j], mp->atoms[i].resName, 4);
1775                         }
1776                         continue;
1777                 } else if (section == 3) {
1778                         /*  Bonds  */
1779                         Int nbonds;
1780                         Int *bp;
1781                         ReadFormat(buf, "I8", &nbonds);
1782                         if (nbonds == 0)
1783                                 continue;
1784                         if (NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, nbonds) == NULL)
1785                                 goto panic;
1786                         bp = mp->bonds;
1787                         for (i = 0; i < nbonds; i += 4) {
1788                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1789                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading bonds", lineNumber);
1790                                         err = 1;
1791                                         goto exit;
1792                                 }
1793                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1794                                         ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1795                                 for (j = 0; j < 4 && i + j < nbonds; j++) {
1796                                         Int b1, b2;
1797                                         Atom *ap;
1798                                         b1 = ibuf[j * 2] - 1;    /* Internal atom number is 0-based */
1799                                         b2 = ibuf[j * 2 + 1] - 1;
1800                                         if (b1 < 0 || b1 >= mp->natoms || b2 < 0 || b2 >= mp->natoms) {
1801                                                 s_append_asprintf(errbuf, "line %d: The bond %d-%d includes non-existent atom", lineNumber, b1+1, b2+1);
1802                                                 err = 1;
1803                                                 goto exit;
1804                                         }
1805                                         *bp++ = b1;
1806                                         *bp++ = b2;
1807                                         ap = ATOM_AT_INDEX(mp->atoms, b1);
1808                                         AtomConnectInsertEntry(&ap->connect, -1, b2);
1809                                         ap = ATOM_AT_INDEX(mp->atoms, b2);
1810                                         AtomConnectInsertEntry(&ap->connect, -1, b1);
1811                                 }
1812                         }
1813                         continue;
1814                 } else if (section == 4) {
1815                         /*  Angles  */
1816                         Int nangles;
1817                         Int *gp;
1818                         ReadFormat(buf, "I8", &nangles);
1819                         if (nangles == 0)
1820                                 continue;
1821                         if (NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, nangles) == NULL)
1822                                 goto panic;
1823                         gp = mp->angles;
1824                         for (i = 0; i < nangles; i += 3) {
1825                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1826                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading angles", lineNumber);
1827                                         err = 1;
1828                                         goto exit;
1829                                 }
1830                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1831                                         ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7, ibuf + 8);
1832                                 for (j = 0; j < 3 && i + j < nangles; j++) {
1833                                         Int a1, a2, a3;
1834                                         a1 = ibuf[j * 3] - 1;   /* Internal atom number is 0-based */
1835                                         a2 = ibuf[j * 3 + 1] - 1;
1836                                         a3 = ibuf[j * 3 + 2] - 1;
1837                                         if (a1 < 0 || a1 >= mp->natoms || a2 < 0 || a2 >= mp->natoms || a3 < 0 || a3 >= mp->natoms) {
1838                                                 s_append_asprintf(errbuf, "line %d: The angle %d-%d-%d includes non-existent atom", lineNumber, a1+1, a2+1, a3+1);
1839                                                 err = 1;
1840                                                 goto exit;
1841                                         }
1842                                         *gp++ = a1;
1843                                         *gp++ = a2;
1844                                         *gp++ = a3;
1845                                 }
1846                         }
1847                         continue;
1848                 } else if (section == 5 || section == 6) {
1849                         /*  Dihedrals and Impropers  */
1850                         Int ndihedrals;
1851                         Int *dp;
1852                         ReadFormat(buf, "I8", &ndihedrals);
1853                         if (ndihedrals == 0)
1854                                 continue;
1855                         if (section == 5) {
1856                                 if (NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, ndihedrals) == NULL)
1857                                         goto panic;
1858                                 dp = mp->dihedrals;
1859                         } else {
1860                                 if (NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, ndihedrals) == NULL)
1861                                         goto panic;
1862                                 dp = mp->impropers;
1863                         }
1864                         for (i = 0; i < ndihedrals; i += 2) {
1865                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1866                                         fclose(fp);
1867                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading %s", lineNumber, (section == 5 ? "dihedral" : "improper"));
1868                                         err = 1;
1869                                         goto exit;
1870                                 }
1871                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3, ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1872                                 for (j = 0; j < 2 && i + j < ndihedrals; j++) {
1873                                         Int d1, d2, d3, d4;
1874                                         d1 = ibuf[j * 4] - 1;   /*  Internal atom number is 0-based  */
1875                                         d2 = ibuf[j * 4 + 1] - 1;
1876                                         d3 = ibuf[j * 4 + 2] - 1;
1877                                         d4 = ibuf[j * 4 + 3] - 1;
1878                                         if (d1 < 0 || d1 >= mp->natoms || d2 < 0 || d2 >= mp->natoms || d3 < 0 || d3 >= mp->natoms || d4 < 0 || d4 >= mp->natoms) {
1879                                                 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);
1880                                                 err = 1;
1881                                                 goto exit;
1882                                         }
1883                                         *dp++ = d1;
1884                                         *dp++ = d2;
1885                                         *dp++ = d3;
1886                                         *dp++ = d4;
1887                                 }
1888                         }
1889                         continue;
1890                 }
1891         }
1892         
1893         /*  Create frames for each atom if necessary  */
1894         if (fn > 1) {
1895                 for (i = 0; i < mp->natoms; i++) {
1896                         ap = ATOM_AT_INDEX(mp->atoms, i);
1897                         ap->frames = (Vector *)malloc(sizeof(Vector) * fn);
1898                         if (ap->frames == NULL)
1899                                 goto panic;
1900                         ap->nframes = fn;
1901                         for (j = 0; j < fn; j++)
1902                                 ap->frames[j] = frames[mp->natoms * j + i];
1903                 }
1904                 free(frames);
1905                 frames = NULL;
1906         }
1907
1908   exit:
1909 /*      funlockfile(fp); */
1910         fclose(fp);
1911         mp->nframes = -1;  /*  Should be recalculated later  */
1912         if (err)
1913                 return 1;
1914         else if (section == -1)
1915                 return -1;
1916         return 0;
1917   panic:
1918         Panic("low memory while reading structure file %s", fname);
1919         return 1; /* not reached */
1920 }
1921
1922 /* ("-x", "y", "-z+0.5") -> (-1,0,0,0,0,1,0,0,0,0,-1,0.5)  */
1923 static int
1924 sMoleculeSymopStringsToTransform(char **symops, Transform tr)
1925 {
1926         int i;
1927         char *symop;
1928         memset(tr, 0, sizeof(Transform));
1929         for (i = 0; i < 3; i++) {
1930                 symop = symops[i];
1931                 if (symop == NULL)
1932                         return 1;
1933                 while (*symop != 0) {
1934                         int sn = 1;
1935                         while (isspace(*symop))
1936                                 symop++;
1937                         if (*symop == 0 || *symop == '\r' || *symop == 'n')
1938                                 break;
1939                         if (*symop == '-') {
1940                                 sn = -1;
1941                                 symop++;
1942                         } else if (*symop == '+') {
1943                                 sn = 1;
1944                                 symop++;
1945                         }
1946                         while (isspace(*symop))
1947                                 symop++;
1948                         if (*symop == '.' || isdigit(*symop)) {
1949                                 /*  Numerical offset  */
1950                                 double d = strtod(symop, &symop);
1951                                 if (*symop == '/') {
1952                                         double dd = strtod(symop + 1, &symop);
1953                                         if (dd > 0)
1954                                                 d /= dd;
1955                                         else
1956                                                 return 1;  /*  Bad format  */
1957                                 }
1958                                 tr[9 + i] = d * sn;
1959                         } else if (*symop == 'x' || *symop == 'X') {
1960                                 tr[i] = sn;
1961                                 symop++;
1962                         } else if (*symop == 'y' || *symop == 'Y') {
1963                                 tr[i + 3] = sn;
1964                                 symop++;
1965                         } else if (*symop == 'z' || *symop == 'Z') {
1966                                 tr[i + 6] = sn;
1967                                 symop++;
1968                         } else return 1;  /*  Bad format  */
1969                 } /* end while (*symop != 0) */
1970         }
1971         return 0;
1972 }
1973
1974 static void
1975 sMoleculeGenerateSymopWithTransform(Molecule *mp, Transform gtr, int num)
1976 {
1977         int i, j;
1978         Transform tr;
1979         if (num <= 0)
1980                 num = mp->nsyms;
1981         for (i = 0; i < num; i++) {
1982                 memmove(tr, mp->syms[i], sizeof(Transform));
1983                 TransformMul(tr, gtr, tr);
1984                 for (j = 9; j < 12; j++) {
1985                         if (tr[j] >= 1.0)
1986                                 tr[j] -= 1.0;
1987                         else if (tr[j] <= 0.0)
1988                                 tr[j] += 1.0;
1989                 }
1990                 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1991         }
1992 }
1993
1994 static char *
1995 sChomp(char *buf)
1996 {
1997         char *p = buf + strlen(buf) - 1;
1998         if (p >= buf && (*p == '\n' || *p == '\r')) {
1999                 *p = 0;
2000                 if (--p >= buf && (*p == '\n' || *p == '\r')) {
2001                         *p = 0;
2002                 }
2003         }
2004         return buf;
2005 }
2006
2007 int
2008 MoleculeLoadTepFile(Molecule *mp, const char *fname, char **errbuf)
2009 {
2010         FILE *fp;
2011         char buf[1024];
2012         int section = -1;
2013         int lineNumber;
2014         int cellType;
2015         Int ibuf[12];
2016         Double fbuf[12];
2017         Int *bonds, nbonds;
2018         *errbuf = NULL;
2019         if (mp == NULL)
2020                 mp = MoleculeNew();
2021         fp = fopen(fname, "rb");
2022         if (fp == NULL) {
2023                 s_append_asprintf(errbuf, "Cannot open file");
2024                 return 1;
2025         }
2026         lineNumber = 0;
2027         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2028                 if (section == -1) {
2029                         /*  Title  */
2030                         section = 0;
2031                         continue;
2032                 }
2033                 if (section == 0) {
2034                         /*  XtalCell  */
2035                         ReadFormat(buf, "I1F8F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5);
2036                         cellType = ibuf[0];
2037                         MoleculeSetCell(mp, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], 0);
2038                         section = 1;
2039                         continue;
2040                 }
2041                 if (section == 1) {
2042                         /*  Symmetry  */
2043                         Transform tr;
2044                         if (cellType == 0) {
2045                                 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);
2046                                 tr[0] = fbuf[1];
2047                                 tr[3] = fbuf[2];
2048                                 tr[6] = fbuf[3];
2049                                 tr[1] = fbuf[5];
2050                                 tr[4] = fbuf[6];
2051                                 tr[7] = fbuf[7];
2052                                 tr[2] = fbuf[9];
2053                                 tr[5] = fbuf[10];
2054                                 tr[8] = fbuf[11];
2055                                 tr[9] = fbuf[0];
2056                                 tr[10] = fbuf[4];
2057                                 tr[11] = fbuf[8];
2058                         } else {
2059                                 char *symops[3], *brks;
2060                                 sChomp(buf);
2061                                 memset(tr, 0, sizeof(Transform));
2062                                 ReadFormat(buf, "I1", ibuf);
2063                                 symops[0] = strtok_r(buf + 1, ", ", &brks);
2064                                 symops[1] = strtok_r(NULL, ", ", &brks);
2065                                 symops[2] = strtok_r(NULL, ", ", &brks);
2066                                 if (sMoleculeSymopStringsToTransform(symops, tr)) {
2067                                         s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2068                                         return 1;
2069                                 }
2070                         }
2071                         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2072                                 goto panic;
2073                         if (ibuf[0] != 0)
2074                                 section = 2;
2075                         continue;
2076                 }
2077                 if (section == 2) {      /*  Atoms  */
2078                         char name[8];
2079                         Atom *ap;
2080                         int atomType;
2081                         int atomIndex = mp->natoms;
2082                         ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2083                         memset(ap, 0, gSizeOfAtomRecord);
2084                         ReadFormat(buf, "S6x3x9x9F9F9F9F9", name, fbuf, fbuf+1, fbuf+2, fbuf+3);
2085                         strncpy(ap->aname, name, 4);
2086                         ap->r.x = fbuf[0];
2087                         ap->r.y = fbuf[1];
2088                         ap->r.z = fbuf[2];
2089                         MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2090                 /*      ap->atomicNumber = AtomNameToElement(ap->name);
2091                         ElementToString(ap->atomicNumber, ap->element); */
2092                 /*      sAtomSetElement(ap, -1, ap->name); */
2093                         guessElement(ap);
2094                         atomType = fbuf[3];
2095                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2096                                 s_append_asprintf(errbuf, "unexpected end of file");
2097                                 return 1;
2098                         }
2099                         ReadFormat(buf, "I1F8F9F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2100                         atomType = fbuf[6];
2101                         if ((atomType >= 0 && atomType <= 5) || (atomType >= 8 && atomType <= 10)) { 
2102                                 /*  Anisotropic thermal parameters  */
2103                                 MoleculeSetAniso(mp, atomIndex, atomType, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[5], fbuf[4], NULL);
2104                         }
2105                         if (ibuf[0] != 0)
2106                                 section = 3;
2107                         continue;
2108                 }
2109         }
2110         fclose(fp);
2111         MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
2112         if (nbonds > 0) {
2113                 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2114                 free(bonds);
2115         }
2116         mp->nframes = -1;  /*  Should be recalculated later  */
2117         return 0;
2118   panic:
2119         Panic("low memory while reading structure file %s", fname);
2120         return -1; /* not reached */
2121 }
2122
2123 int
2124 MoleculeLoadShelxFile(Molecule *mp, const char *fname, char **errbuf)
2125 {
2126         FILE *fp;
2127         char buf[1024];
2128         char *p1, *p2;
2129         int n;
2130         int lineNumber;
2131         int latticeType;
2132         int currentResSeq = 0;
2133         char currentResName[6];
2134         Transform tr;
2135         int ibuf[12];
2136         float fbuf[12];
2137         Double dbuf[12];
2138         Int nsfacs = 0;
2139         Int nbonds, *bonds;
2140         char (*sfacs)[4] = NULL;
2141
2142         *errbuf = NULL;
2143         if (mp == NULL)
2144                 mp = MoleculeNew();
2145         currentResName[0] = 0;
2146         fp = fopen(fname, "rb");
2147         if (fp == NULL) {
2148                 s_append_asprintf(errbuf, "Cannot open file");
2149                 return 1;
2150         }
2151         lineNumber = 0;
2152         tr[0] = tr[4] = tr[8] = 1;
2153         tr[1] = tr[2] = tr[3] = tr[5] = tr[6] = tr[7] = tr[9] = tr[10] = tr[11] = 0;
2154         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), 0, tr) == 0)
2155                 goto panic;
2156         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2157                 if (strncmp(buf, "CELL", 4) == 0) {
2158                         /*  XtalCell  */
2159                         sscanf(buf + 4, " %f %f %f %f %f %f %f", fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2160                         MoleculeSetCell(mp, fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], fbuf[6], 0);
2161                         continue;
2162                 } else if (strncmp(buf, "SFAC", 4) == 0) {
2163                         sChomp(buf);
2164                         for (p1 = strtok_r(buf + 4, " ", &p2); p1 != NULL; p1 = strtok_r(NULL, " ", &p2)) {
2165                                 char *pp = (char *)AssignArray(&sfacs, &nsfacs, 4, nsfacs, NULL);
2166                                 if (pp == NULL)
2167                                         goto panic;
2168                                 strncpy(pp, p1, 3);
2169                                 pp[3] = 0;
2170                         }
2171                         continue;
2172                 } else if (strncmp(buf, "LATT", 4) == 0) {
2173                         sscanf(buf + 4, " %d", &latticeType);
2174                         continue;
2175                 } else if (strncmp(buf, "SYMM", 4) == 0) {
2176                         char *symops[3], *brks;
2177                         memset(tr, 0, sizeof(Transform));
2178                 //      ReadFormat(buf + 4, "I1", ibuf);
2179                         sChomp(buf);
2180                         symops[0] = strtok_r(buf + 4, ",", &brks);
2181                         symops[1] = strtok_r(NULL, ",", &brks);
2182                         symops[2] = strtok_r(NULL, ",", &brks);
2183                         if (sMoleculeSymopStringsToTransform(symops, tr)) {
2184                                 s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2185                                 return 1;
2186                         }
2187                         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2188                                 goto panic;
2189                         continue;
2190                 } else if (strncmp(buf, "RESI", 4) == 0) {
2191                         for (p1 = buf + 4; isspace(*p1); p1++);
2192                         if (isalpha(*p1)) {
2193                                 for (p2 = p1 + 1; isalnum(*p2); p2++);
2194                                 *p2 = 0;
2195                                 strncpy(currentResName, p1, 4);
2196                                 currentResName[4] = 0;
2197                                 p1 = p2 + 1;
2198                         } else currentResName[0] = 0;
2199                         sscanf(buf + 4, " %d", &currentResSeq);
2200                         continue;
2201                 } else {
2202                         /* Atom name: [A-Za-z]{1,2}[0-9]*  */
2203                         for (p1 = buf; p1 < buf + 2 && (isalpha(*p1) && *p1 != '_'); p1++);
2204                         if (p1 > buf) {
2205                                 while (isdigit(*p1))
2206                                         p1++;
2207                         }
2208                         if (p1 > buf && p1 <= buf + 4 && isspace(*p1)) {
2209                                 /*  Atom  */
2210                                 Atom *ap;
2211                                 char cont[4];
2212                                 int atomIndex = mp->natoms;
2213                                 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2214                                 memset(ap, 0, gSizeOfAtomRecord);
2215                                 strncpy(ap->aname, buf, 4);
2216                                 n = sscanf(p1, " %d %f %f %f %f %f %f %2s", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, cont);
2217                                 if (n == 8 && strcmp(cont, "=") == 0) {
2218                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2219                                                 s_append_asprintf(errbuf, "line %d: unexpected end of file within the atom cards", lineNumber);
2220                                                 return 1;
2221                                         }
2222                                         sscanf(buf, " %f %f %f %f", fbuf+6, fbuf+7, fbuf+8, fbuf+9);
2223                                         n = 10;   /*  Aniso  */
2224                                 } else n = 5; /*  Iso  */
2225                                 ap->r.x = fbuf[0];
2226                                 ap->r.y = fbuf[1];
2227                                 ap->r.z = fbuf[2];
2228                                 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2229                                 ap->occupancy = fbuf[3];
2230                                 if (ap->aname[0] != 'Q' && ibuf[0] >= 1 && ibuf[0] <= nsfacs) {
2231                                         strncpy(ap->element, sfacs[ibuf[0] - 1], 2);
2232                                         ap->element[2] = 0;
2233                                 /*      sAtomSetElement(ap, -1, sfacs[ibuf[0] - 1]); */
2234                                 /*      strncpy(ap->element, sfacs[ibuf[0] - 1], 4);
2235                                         ap->atomicNumber = ElementToInt(ap->element); */
2236                         /*      } else {
2237                                         sAtomSetElement(ap, -1, ap->name); */
2238                                 /*      ap->atomicNumber = AtomNameToElement(ap->name);
2239                                         ElementToString(ap->atomicNumber, ap->element); */
2240                                 }
2241                                 guessElement(ap);
2242                                 if (n == 10 || fbuf[4] >= 0.0) {
2243                                         int i, c, j;
2244                                         /*  Read in the standard deviations  */
2245                                         ReadLine(buf, sizeof buf, fp, &lineNumber);
2246                                         for (i = 0; i < 9; i++) {
2247                                                 j = 3 + i * 8;
2248                                                 c = buf[j + 8];
2249                                                 buf[j + 8] = 0;
2250                                                 dbuf[i] = strtod(buf + j, NULL);
2251                                                 buf[j + 8] = c;
2252                                         }
2253                                         ap->sigma.x = dbuf[0];
2254                                         ap->sigma.y = dbuf[1];
2255                                         ap->sigma.z = dbuf[2];
2256                                 }
2257                                 if (n == 5)
2258                                         ap->tempFactor = fbuf[4] * 78.9568352087147; /* 8*pi*pi */
2259                                 else
2260                                         MoleculeSetAniso(mp, atomIndex, 8, fbuf[4], fbuf[5], fbuf[6], fbuf[9], fbuf[7], fbuf[8], dbuf);
2261                                 ap->resSeq = currentResSeq;
2262                                 strncpy(ap->resName, currentResName, 4);
2263                         }
2264                         continue;
2265                 }
2266         }
2267         fclose(fp);
2268
2269         /*  Add symmetry operations according to the lattice type  */
2270         switch (latticeType < 0 ? -latticeType : latticeType) {
2271                 static Transform tr_i = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0.5};
2272                 static Transform tr_c = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0};
2273                 static Transform tr_a = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.5};
2274                 static Transform tr_b = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5};
2275                 static Transform tr_r1 = {0, 1, 0, -1, -1, 0, 0, 0, 1, 0, 0, 0};
2276                 static Transform tr_r2 = {-1, -1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0};
2277                 case 1:  /* P */
2278                         break;
2279                 case 2:  /* I */
2280                         sMoleculeGenerateSymopWithTransform(mp, tr_i, 0);
2281                         break;
2282                 case 3:  /* Rhombohedral obverse on hexagonal axes  */
2283                         n = mp->nsyms;
2284                         sMoleculeGenerateSymopWithTransform(mp, tr_r1, n);
2285                         sMoleculeGenerateSymopWithTransform(mp, tr_r2, n);
2286                         break;
2287                 case 4:  /* F */
2288                         n = mp->nsyms;
2289                         sMoleculeGenerateSymopWithTransform(mp, tr_a, n);
2290                         sMoleculeGenerateSymopWithTransform(mp, tr_b, n);
2291                         sMoleculeGenerateSymopWithTransform(mp, tr_c, n);
2292                         break;
2293                 case 5:  /* A */
2294                         sMoleculeGenerateSymopWithTransform(mp, tr_a, 0);
2295                         break;
2296                 case 6:  /* B */
2297                         sMoleculeGenerateSymopWithTransform(mp, tr_b, 0);
2298                         break;
2299                 case 7:  /* C */
2300                         sMoleculeGenerateSymopWithTransform(mp, tr_c, 0);
2301                         break;
2302         }
2303                 
2304         if (latticeType > 0) {
2305                 static Transform tr_inv = {-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0};
2306                 sMoleculeGenerateSymopWithTransform(mp, tr_inv, 0);
2307         }
2308         
2309         MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
2310         if (nbonds > 0) {
2311                 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2312                 free(bonds);
2313         }
2314         mp->nframes = -1;  /*  Should be recalculated later  */
2315         return 0;
2316   panic:
2317         Panic("low memory while reading structure file %s", fname);
2318         return -1; /* not reached */
2319 }
2320
2321 /*  Add one gaussian orbital shell information (not undoable)  */
2322 int
2323 MoleculeAddGaussianOrbitalShell(Molecule *mol, Int sym, Int nprims, Int a_idx)
2324 {
2325         BasisSet *bset;
2326         ShellInfo *shellp;
2327         if (mol == NULL)
2328                 return -1;  /*  Molecule is empty  */
2329         bset = mol->bset;
2330         if (bset == NULL) {
2331                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2332                 if (bset == NULL)
2333                         return -2;  /*  Low memory  */
2334         }
2335         shellp = AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), bset->nshells, NULL);
2336         if (shellp == NULL)
2337                 return -2;  /*  Low memory  */
2338         switch (sym) {
2339                 case 0:  shellp->sym = kGTOType_S;  shellp->ncomp = 1; break;
2340                 case 1:  shellp->sym = kGTOType_P;  shellp->ncomp = 3; break;
2341                 case -1: shellp->sym = kGTOType_SP; shellp->ncomp = 4; break;
2342                 case 2:  shellp->sym = kGTOType_D;  shellp->ncomp = 6; break;
2343                 case -2: shellp->sym = kGTOType_D5; shellp->ncomp = 5; break;
2344                 case 3:  shellp->sym = kGTOType_F;  shellp->ncomp = 10; break;
2345                 case -3: shellp->sym = kGTOType_F7; shellp->ncomp = 7; break;
2346                 case 4:  shellp->sym = kGTOType_G;  shellp->ncomp = 15; break;
2347                 case -4: shellp->sym = kGTOType_G9; shellp->ncomp = 9; break;
2348                 default:
2349                         return -3;  /* Unsupported shell type  */
2350         }
2351         shellp->nprim = nprims;
2352         shellp->a_idx = a_idx;
2353         if (bset->shells < shellp) {
2354                 shellp->m_idx = shellp[-1].m_idx + shellp[-1].ncomp;
2355                 shellp->p_idx = shellp[-1].p_idx + shellp[-1].nprim;
2356         } else {
2357                 shellp->m_idx = 0;
2358                 shellp->p_idx = 0;
2359         }
2360         return 0;
2361 }
2362
2363 /*  Add a set of gaussian primitive coefficients (not undoable)  */
2364 int
2365 MoleculeAddGaussianPrimitiveCoefficients(Molecule *mol, Double exponent, Double contraction, Double contraction_sp)
2366 {
2367         BasisSet *bset;
2368         PrimInfo *primp;
2369         if (mol == NULL)
2370                 return -1;  /*  Molecule is empty  */
2371         bset = mol->bset;
2372         if (bset == NULL) {
2373                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2374                 if (bset == NULL)
2375                         return -2;  /*  Low memory  */
2376         }
2377         primp = AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), bset->npriminfos, NULL);
2378         if (primp == NULL)
2379                 return -2;  /*  Low memory  */
2380         primp->A = exponent;
2381         primp->C = contraction;
2382         primp->Csp = contraction_sp;
2383         return 0;
2384 }
2385
2386 /*  Set MO coefficients for idx-th MO  */
2387 int
2388 MoleculeSetMOCoefficients(Molecule *mol, Int idx, Double energy, Int ncomps, Double *coeffs)
2389 {
2390         BasisSet *bset;
2391         int i, n;
2392         if (mol == NULL)
2393                 return -1;  /*  Molecule is empty  */
2394         bset = mol->bset;
2395         if (bset == NULL) {
2396                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2397                 if (bset == NULL)
2398                         return -2;  /*  Low memory  */
2399         }
2400         if (bset->nmos == 0) {
2401                 if (bset->nshells > 0) {
2402                         /*  Shell info is already set: calculate the number of MOs from there  */
2403                         for (i = n = 0; i < bset->nshells; i++)
2404                                 n += bset->shells[i].ncomp;
2405                         bset->ncomps = n;
2406                 } else if (ncomps > 0) {
2407                         bset->ncomps = ncomps;
2408                 }
2409                 if (bset->rflag == 0)
2410                         bset->nmos = bset->ncomps * 2;
2411                 else
2412                         bset->nmos = bset->ncomps;
2413                 if (bset->nmos <= 0)
2414                         return -3;  /*  Bad or inconsistent number of MOs  */
2415                 bset->mo = (Double *)calloc(sizeof(Double), bset->nmos * bset->ncomps);
2416                 bset->moenergies = (Double *)calloc(sizeof(Double), bset->nmos);
2417                 if (bset->mo == NULL || bset->moenergies == NULL) {
2418                         if (bset->mo != NULL)
2419                                 free(bset->mo);
2420                         if (bset->moenergies != NULL)
2421                                 free(bset->moenergies);
2422                         bset->mo = NULL;
2423                         bset->moenergies = NULL;
2424                         bset->nmos = 0;
2425                         return -2;  /*  Low memory  */
2426                 }
2427         }
2428         if (idx < 0 || idx >= bset->nmos)
2429                 return -4;  /*  Bad MO index  */
2430         if (energy != -1000000)
2431                 bset->moenergies[idx] = energy;
2432         if (ncomps < bset->ncomps)
2433                 return -5;  /*  Insufficient number of data provided  */
2434         memmove(bset->mo + (idx * bset->ncomps), coeffs, sizeof(Double) * bset->ncomps);
2435         if (bset->cns != NULL) {
2436                 /*  Clear the cached values  */
2437                 free(bset->cns);
2438                 bset->cns = NULL;
2439                 bset->ncns = 0;
2440         }
2441         return 0;
2442 }
2443
2444 /*  Allocate BasisSet record. rflag: UHF, 0; RHF, 1; ROHF, 2
2445     ne_alpha: number of alpha electrons, ne_beta: number of beta electrons
2446     The natoms and pos are copied from mol.  */
2447 int
2448 MoleculeAllocateBasisSetRecord(Molecule *mol, Int rflag, Int ne_alpha, Int ne_beta)
2449 {
2450         BasisSet *bset;
2451         int i;
2452         Atom *ap;
2453         if (mol == NULL || mol->natoms == 0)
2454                 return -1;  /*  Molecule is empty  */
2455         bset = mol->bset;
2456         if (bset == NULL) {
2457                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2458                 if (bset == NULL)
2459                         return -2;  /*  Low memory  */
2460         }
2461         if (bset->pos != NULL) {
2462                 free(bset->pos);
2463                 bset->pos = NULL;
2464         }
2465         bset->natoms = mol->natoms;
2466         bset->pos = (Vector *)calloc(sizeof(Vector), bset->natoms);
2467         if (bset->pos == NULL)
2468                 return -2;  /*  Low memory  */
2469         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
2470                 bset->pos[i].x = ap->r.x * kAngstrom2Bohr;
2471                 bset->pos[i].y = ap->r.y * kAngstrom2Bohr;
2472                 bset->pos[i].z = ap->r.z * kAngstrom2Bohr;
2473         }
2474         bset->ne_alpha = ne_alpha;
2475         bset->ne_beta = ne_beta;
2476         bset->rflag = rflag;
2477         return 0;
2478 }
2479
2480 static void
2481 sSeparateTokens(char *inString, char **outPtr, int size)
2482 {
2483         char *p;
2484         int i;
2485         for (i = 0; i < size; i++) {
2486                 p = strtok((i == 0 ? inString : NULL), " \r\n");
2487                 if (p == NULL)
2488                         break;
2489                 outPtr[i] = p;
2490         }
2491         while (i < size) {
2492                 outPtr[i++] = NULL;
2493         }
2494 }
2495
2496 static int
2497 sReadNumberArray(void *basep, Int *countp, Int size, Int num, FILE *fp, int *lnp)
2498 {
2499         char buf[256];
2500         Int i, n;
2501         *((void **)basep) = NULL;
2502         *countp = 0;
2503         if (AssignArray(basep, countp, size, num - 1, NULL) == NULL)
2504                 return 4;  /*  Out of memory  */
2505         n = 0;
2506         while (ReadLine(buf, sizeof buf, fp, lnp) > 0) {
2507                 char *tokens[16], *p;
2508                 sSeparateTokens(buf, tokens, 16);
2509                 for (i = 0; i < 16; i++) {
2510                         if (tokens[i] == NULL)
2511                                 break;
2512                         if (size == sizeof(Int)) {
2513                                 (*((Int **)basep))[n] = strtol(tokens[i], &p, 0);
2514                         } else if (size == sizeof(Double)) {
2515                                 (*((Double **)basep))[n] = strtod(tokens[i], &p);
2516                         } else return -1;  /*  Internal error  */
2517                         if (tokens[i] == p || *p != 0)
2518                                 return 1;  /*  Non-digit character  */
2519                         if (++n == num) {
2520                                 if (i < 15 && tokens[i + 1] != NULL)
2521                                         return 2;  /*  Too many data  */
2522                                 return 0;  /*  All data are successfully read  */
2523                         }
2524                 }
2525         }
2526         return 3;  /*  Unexpected EOF  */                       
2527 }
2528
2529 static int
2530 sSetupGaussianCoefficients(BasisSet *bset)
2531 {
2532         ShellInfo *sp;
2533         PrimInfo *pp;
2534         int i, j, k;
2535         Double *dp, d;
2536         
2537         /*  Cache the contraction coefficients for efficient calculation  */
2538         /*  Sum up the number of components for all primitives  */
2539         for (i = k = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2540                 sp->cn_idx = k;
2541                 k += sp->nprim * sp->ncomp;
2542         }
2543         /*  Allocate memory for the cached values  */
2544         if (AssignArray(&bset->cns, &bset->ncns, sizeof(Double), k - 1, NULL) == NULL)
2545                 return 1;
2546         /*  Iterate over all primitives  */
2547         dp = bset->cns;
2548         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2549                 for (j = 0, pp = bset->priminfos + sp->p_idx; j < sp->nprim; j++, pp++) {
2550                         switch (sp->sym) {
2551                                 case kGTOType_S:
2552                                         // (8 alpha^3/pi^3)^0.25 exp(-alpha r^2)
2553                                         *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2554                                         break;
2555                                 case kGTOType_P:
2556                                         // (128 alpha^5/pi^3)^0.25 [x|y|z]exp(-alpha r^2)
2557                                         d = pp->C * pow(pp->A, 1.25) * 1.425410941;
2558                                         *dp++ = d;
2559                                         *dp++ = d;
2560                                         *dp++ = d;
2561                                         break;
2562                                 case kGTOType_SP:
2563                                         *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2564                                         d = pp->Csp * pow(pp->A, 1.25) * 1.425410941;
2565                                         *dp++ = d;
2566                                         *dp++ = d;
2567                                         *dp++ = d;
2568                                         break;
2569                                 case kGTOType_D:
2570                                         //  xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2)
2571                                         //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2572                                         d = pp->C * pow(pp->A, 1.75);
2573                                         dp[0] = dp[1] = dp[2] = d * 1.645922781;
2574                                         dp[3] = dp[4] = dp[5] = d * 2.850821881;
2575                                         dp += 6;
2576                                         break;
2577                                 case kGTOType_D5:
2578                                         //  3zz-rr:   (128 alpha^7/9pi^3)^0.25 (3zz-rr)exp(-alpha r^2)
2579                                         //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2580                                         //  xx-yy:    (128 alpha^7/pi^3)^0.25 (xx-yy)exp(-alpha r^2)
2581                                         d = pp->C * pow(pp->A, 1.75);
2582                                         dp[0] = d * 0.822961390;
2583                                         dp[1] = dp[2] = dp[4] = d * 2.850821881;
2584                                         dp[3] = d * 1.425410941;
2585                                         dp += 5;
2586                                         break;
2587                                 /*  TODO: Support F/F7 and G/G9 type orbitals  */
2588                         }
2589                 }
2590         }
2591         return 0;
2592 }
2593
2594 int
2595 MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char **errbuf)
2596 {
2597         FILE *fp;
2598         char buf[1024];
2599         int lineNumber;
2600         int natoms, nbasis, i, j, k, n, mxbond, retval, ncomps, nprims, nelec;
2601         BasisSet *bset;
2602         ShellInfo *sp;
2603         PrimInfo *pp;
2604         Int nary;
2605         Int *iary;
2606         Double *dary;
2607         Atom *ap;
2608         Vector *vp;
2609         Double w;
2610
2611         *errbuf = NULL;
2612         if (mp == NULL)
2613                 mp = MoleculeNew();
2614         bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2615         if (bset == NULL)
2616                 goto panic;
2617         mp->bset = bset;
2618         fp = fopen(fname, "rb");
2619         if (fp == NULL) {
2620                 s_append_asprintf(errbuf, "Cannot open file");
2621                 return 1;
2622         }
2623         lineNumber = 0;
2624         natoms = nbasis = -1;
2625         mxbond = 0;
2626         ncomps = 0;
2627         nelec = 0;
2628         nprims = 0;
2629         nary = 0;
2630         iary = NULL;
2631         dary = NULL;
2632         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2633                 char *tokens[16];
2634                 char *p = buf + 41;
2635                 if (lineNumber == 2) {
2636                         /*  job info line  */
2637                         if (buf[10] == 'U')
2638                                 bset->rflag = 0;  /*  UHF  */
2639                         else if (buf[11] == 'O')
2640                                 bset->rflag = 2;  /*  ROHF  */
2641                         else bset->rflag = 1; /*  RHF  */
2642                         continue;
2643                 }
2644                 while (p > buf && *p == ' ')
2645                         p--;
2646                 p[1] = 0;
2647                 sSeparateTokens(buf + 42, tokens, 16);
2648                 if (strcmp(buf, "Number of atoms") == 0) {
2649                         if (tokens[1] == NULL || (natoms = atoi(tokens[1])) <= 0) {
2650                                 s_append_asprintf(errbuf, "Line %d: strange number of atoms: %s", lineNumber, tokens[1]);
2651                                 retval = 2;
2652                                 goto cleanup;
2653                         }
2654                         /*  Allocate atom records (all are empty for now)  */
2655                         AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms - 1, NULL);
2656                         /*  Also allocate atom position array for MO calculations  */
2657                         AssignArray(&bset->pos, &bset->natoms, sizeof(Vector), natoms - 1, NULL);
2658                         /*  Also allocate nuclear charge array  */
2659                         bset->nuccharges = (Double *)calloc(sizeof(Double), natoms);
2660                 } else if (strcmp(buf, "Number of electrons") == 0) {
2661                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2662                                 s_append_asprintf(errbuf, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
2663                                 retval = 2;
2664                                 goto cleanup;
2665                         }
2666                         nelec = i;
2667                 } else if (strcmp(buf, "Number of alpha electrons") == 0) {
2668                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2669                                 s_append_asprintf(errbuf, "Line %d: strange number of alpha electrons: %s", lineNumber, tokens[1]);
2670                                 retval = 2;
2671                                 goto cleanup;
2672                         }
2673                         bset->ne_alpha = i;
2674                 } else if (strcmp(buf, "Number of beta electrons") == 0) {
2675                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2676                                 s_append_asprintf(errbuf, "Line %d: strange number of beta electrons: %s", lineNumber, tokens[1]);
2677                                 retval = 2;
2678                                 goto cleanup;
2679                         }
2680                         bset->ne_beta = i;
2681                         if (bset->ne_alpha + bset->ne_beta != nelec) {
2682                                 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);
2683                                 retval = 2;
2684                                 goto cleanup;
2685                         }
2686                 } else if (strcmp(buf, "Number of basis functions") == 0) {
2687                         if (tokens[1] == NULL || (nbasis = atoi(tokens[1])) <= 0) {
2688                                 s_append_asprintf(errbuf, "Line %d: strange number of basis functions: %s", lineNumber, tokens[1]);
2689                                 retval = 2;
2690                                 goto cleanup;
2691                         }
2692                 } else if (strcmp(buf, "Atomic numbers") == 0) {
2693                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2694                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2695                                 retval = 2;
2696                                 goto cleanup;
2697                         }
2698                         if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms, fp, &lineNumber) != 0) {
2699                                 s_append_asprintf(errbuf, "Line %d: cannot read atomic numbers", lineNumber);
2700                                 retval = 2;
2701                                 goto cleanup;
2702                         }
2703                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2704                                 ap->atomicNumber = iary[i];
2705                                 bset->nuccharges[i] = iary[i];
2706                                 ElementToString(ap->atomicNumber, ap->element);
2707                                 memmove(ap->aname, ap->element, 4);
2708                                 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
2709                                         ap->weight = w;
2710                         }
2711                         free(iary);
2712                         iary = NULL;
2713                 } else if (strcmp(buf, "Nuclear charges") == 0) {
2714                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2715                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2716                                 retval = 2;
2717                                 goto cleanup;
2718                         }
2719                         if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms, fp, &lineNumber) != 0) {
2720                                 s_append_asprintf(errbuf, "Line %d: cannot read nuclear charges", lineNumber);
2721                                 retval = 2;
2722                                 goto cleanup;
2723                         }
2724                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2725                                 bset->nuccharges[i] = dary[i];
2726                         }
2727                         free(iary);
2728                         iary = NULL;
2729                 } else if (strcmp(buf, "Current cartesian coordinates") == 0) {
2730                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * 3) {
2731                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of cartesian coordinates: %s", lineNumber, tokens[2]);
2732                                 retval = 2;
2733                                 goto cleanup;
2734                         }
2735                         if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms * 3, fp, &lineNumber) != 0) {
2736                                 s_append_asprintf(errbuf, "Line %d: cannot read cartesian coordinates", lineNumber);
2737                                 retval = 2;
2738                                 goto cleanup;
2739                         }
2740                         for (i = 0, ap = mp->atoms, vp = bset->pos; i < natoms; i++, ap = ATOM_NEXT(ap), vp++) {
2741                                 vp->x = dary[i * 3];
2742                                 vp->y = dary[i * 3 + 1];
2743                                 vp->z = dary[i * 3 + 2];
2744                                 ap->r.x = vp->x * kBohr2Angstrom;
2745                                 ap->r.y = vp->y * kBohr2Angstrom;
2746                                 ap->r.z = vp->z * kBohr2Angstrom;
2747                         }
2748                         free(dary);
2749                         dary = NULL;
2750                 } else if (strcmp(buf, "MxBond") == 0) {
2751                         if (tokens[1] == NULL || (mxbond = atoi(tokens[1])) <= 0) {
2752                                 s_append_asprintf(errbuf, "Line %d: strange number of bonds per atom: %s", lineNumber, tokens[1]);
2753                                 retval = 2;
2754                                 goto cleanup;
2755                         }
2756                 } else if (strcmp(buf, "IBond") == 0) {
2757                         Int *bonds;
2758                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * mxbond) {
2759                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of bonds: %s", lineNumber, tokens[2]);
2760                                 retval = 2;
2761                                 goto cleanup;
2762                         }
2763                         if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms * mxbond, fp, &lineNumber) != 0) {
2764                                 s_append_asprintf(errbuf, "Line %d: cannot read bond information", lineNumber);
2765                                 retval = 2;
2766                                 goto cleanup;
2767                         }
2768                         bonds = (Int *)malloc(sizeof(Int) * (mxbond * 2 + 1));
2769                         for (i = 0; i < natoms; i++) {
2770                                 for (j = k = 0; j < mxbond; j++) {
2771                                         n = iary[i * mxbond + j] - 1;
2772                                         if (n > i) {
2773                                                 /*  Connect atom i and atom n  */
2774                                                 bonds[k++] = i;
2775                                                 bonds[k++] = n;
2776                                         }
2777                                 }
2778                                 if (k > 0) {
2779                                         bonds[k] = kInvalidIndex;
2780                                         MoleculeAddBonds(mp, k / 2, bonds, NULL, 1);
2781                                 }
2782                         }
2783                         free(iary);
2784                         free(bonds);
2785                         iary = NULL;
2786                 } else if (strcmp(buf, "Shell types") == 0) {
2787                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0) {
2788                                 s_append_asprintf(errbuf, "Line %d: wrong number of shell types: %s", lineNumber, tokens[2]);
2789                                 retval = 2;
2790                                 goto cleanup;
2791                         }
2792                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2793                                 s_append_asprintf(errbuf, "Line %d: cannot read shell types", lineNumber);
2794                                 retval = 2;
2795                                 goto cleanup;
2796                         }
2797                         /*  Allocate ShellInfo table and store shell type information  */
2798                         AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), nary - 1, NULL);
2799                         for (i = n = 0, sp = bset->shells; i < nary; i++, sp++) {
2800                                 switch (iary[i]) {
2801                                         case 0:  sp->sym = kGTOType_S;  sp->ncomp = 1; break;
2802                                         case 1:  sp->sym = kGTOType_P;  sp->ncomp = 3; break;
2803                                         case -1: sp->sym = kGTOType_SP; sp->ncomp = 4; break;
2804                                         case 2:  sp->sym = kGTOType_D;  sp->ncomp = 6; break;
2805                                         case -2: sp->sym = kGTOType_D5; sp->ncomp = 5; break;
2806                                         case 3:  sp->sym = kGTOType_F;  sp->ncomp = 10; break;
2807                                         case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break;
2808                                         case 4:  sp->sym = kGTOType_G;  sp->ncomp = 15; break;
2809                                         case -4: sp->sym = kGTOType_G9; sp->ncomp = 9; break;
2810                                         default:
2811                                                 s_append_asprintf(errbuf, "Line %d: unsupported shell type %d", lineNumber, iary[i]);
2812                                                 retval = 2;
2813                                                 goto cleanup;
2814                                 }
2815                                 sp->m_idx = n;
2816                                 n += sp->ncomp;
2817                         }
2818                         bset->ncomps = ncomps = n;
2819                         free(iary);
2820                         iary = NULL;
2821                 } else if (strcmp(buf, "Number of primitives per shell") == 0) {
2822                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2823                                 s_append_asprintf(errbuf, "Line %d: wrong size of the primitive table: %s", lineNumber, tokens[2]);
2824                                 retval = 2;
2825                                 goto cleanup;
2826                         }
2827                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2828                                 s_append_asprintf(errbuf, "Line %d: cannot read primitive table", lineNumber);
2829                                 retval = 2;
2830                                 goto cleanup;
2831                         }
2832                         for (i = n = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2833                                 sp->nprim = iary[i];
2834                                 sp->p_idx = n;
2835                                 n += sp->nprim;
2836                         }
2837                         nprims = n;
2838                         free(iary);
2839                         iary = NULL;
2840                 } else if (strcmp(buf, "Shell to atom map") == 0) {
2841                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2842                                 s_append_asprintf(errbuf, "Line %d: wrong size of the shell-to-atom map: %s", lineNumber, tokens[2]);
2843                                 retval = 2;
2844                                 goto cleanup;
2845                         }
2846                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2847                                 s_append_asprintf(errbuf, "Line %d: cannot read shell-to-atom table", lineNumber);
2848                                 retval = 2;
2849                                 goto cleanup;
2850                         }
2851                         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2852                                 sp->a_idx = iary[i] - 1;
2853                         }
2854                         free(iary);
2855                         iary = NULL;
2856                 } else if (strcmp(buf, "Primitive exponents") == 0) {
2857                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nprims) {
2858                                 s_append_asprintf(errbuf, "Line %d: wrong number of primitive exponents: %s", lineNumber, tokens[2]);
2859                                 retval = 2;
2860                                 goto cleanup;
2861                         }
2862                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2863                                 s_append_asprintf(errbuf, "Line %d: cannot read primitive exponents", lineNumber);
2864                                 retval = 2;
2865                                 goto cleanup;
2866                         }
2867                         /*  Allocate PrimInfo table  */
2868                         AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), nprims - 1, NULL);
2869                         for (i = 0, pp = bset->priminfos; i < nprims; i++, pp++) {
2870                                 pp->A = dary[i];
2871                         }
2872                         free(dary);
2873                         dary = NULL;
2874                 } else if (strcmp(buf, "Contraction coefficients") == 0) {
2875                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2876                                 s_append_asprintf(errbuf, "Line %d: wrong number of contraction coefficients: %s", lineNumber, tokens[2]);
2877                                 retval = 2;
2878                                 goto cleanup;
2879                         }
2880                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2881                                 s_append_asprintf(errbuf, "Line %d: cannot read contraction coefficients", lineNumber);
2882                                 retval = 2;
2883                                 goto cleanup;
2884                         }
2885                         for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2886                                 pp->C = dary[i];
2887                         }
2888                         free(dary);
2889                         dary = NULL;
2890                 } else if (strcmp(buf, "P(S=P) Contraction coefficients") == 0) {
2891                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2892                                 s_append_asprintf(errbuf, "Line %d: wrong number of P(S=P) contraction coefficients: %s", lineNumber, tokens[2]);
2893                                 retval = 2;
2894                                 goto cleanup;
2895                         }
2896                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2897                                 s_append_asprintf(errbuf, "Line %d: cannot read P(S=P) contraction coefficients", lineNumber);
2898                                 retval = 2;
2899                                 goto cleanup;
2900                         }
2901                         for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2902                                 pp->Csp = dary[i];
2903                         }
2904                         free(dary);
2905                         dary = NULL;
2906                 } else if (strcmp(buf, "Alpha Orbital Energies") == 0) {
2907                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2908                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha orbitals: %s", lineNumber, tokens[2]);
2909                                 retval = 2;
2910                                 goto cleanup;
2911                         }
2912                         if (sReadNumberArray(&bset->moenergies, &bset->nmos, sizeof(Double), i, fp, &lineNumber) != 0) {
2913                                 s_append_asprintf(errbuf, "Line %d: cannot read alpha orbital energies", lineNumber);
2914                                 retval = 2;
2915                                 goto cleanup;
2916                         }
2917                 } else if (strcmp(buf, "Alpha MO coefficients") == 0) {
2918                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2919                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha MO coefficients: %s", lineNumber, tokens[2]);
2920                                 retval = 2;
2921                                 goto cleanup;
2922                         }
2923                         if (sReadNumberArray(&bset->mo, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2924                                 s_append_asprintf(errbuf, "Line %d: cannot read MO coefficients", lineNumber);
2925                                 retval = 2;
2926                                 goto cleanup;
2927                         }
2928                 } else if (strcmp(buf, "Beta Orbital Energies") == 0) {
2929                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2930                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta orbitals: %s", lineNumber, tokens[2]);
2931                                 retval = 2;
2932                                 goto cleanup;
2933                         }
2934                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2935                                 s_append_asprintf(errbuf, "Line %d: cannot read beta orbital energies", lineNumber);
2936                                 retval = 2;
2937                                 goto cleanup;
2938                         }
2939                         bset->moenergies = (Double *)realloc(bset->moenergies, sizeof(Double) * 2 * ncomps);
2940                         bset->nmos = ncomps * 2;
2941                         bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);
2942                         memmove(bset->moenergies + ncomps, dary, sizeof(Double) * ncomps);
2943                         memset(bset->mo + ncomps * ncomps, 0, sizeof(Double) * ncomps * ncomps);
2944                         free(dary);
2945                         dary = NULL;
2946                 } else if (strcmp(buf, "Beta MO coefficients") == 0) {
2947                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2948                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta MO coefficients: %s", lineNumber, tokens[2]);
2949                                 retval = 2;
2950                                 goto cleanup;
2951                         }
2952                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2953                                 s_append_asprintf(errbuf, "Line %d: cannot read alpha MO coefficients", lineNumber);
2954                                 retval = 2;
2955                                 goto cleanup;
2956                         }
2957                         bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);  /*  Should be unnecessary, just in case  */
2958                         memmove(bset->mo + ncomps * ncomps, dary, sizeof(Double) * ncomps * ncomps);
2959                         free(dary);
2960                         dary = NULL;
2961                 } else if (strcmp(buf, "Total SCF Density") == 0) {
2962                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * (ncomps + 1) / 2) {
2963                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of SCF densities: %s", lineNumber, tokens[2]);
2964                                 retval = 2;
2965                                 goto cleanup;
2966                         }
2967                         if (sReadNumberArray(&bset->scfdensities, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2968                                 s_append_asprintf(errbuf, "Line %d: cannot read SCF densities", lineNumber);
2969                                 retval = 2;
2970                                 goto cleanup;
2971                         }
2972                 }
2973         }
2974         if (mp->natoms == 0) {
2975                 s_append_asprintf(errbuf, "Atom information is missing");
2976                 retval = 2;
2977                 goto cleanup;
2978         }
2979         if (bset->shells == NULL || bset->priminfos == NULL) {
2980                 s_append_asprintf(errbuf, "Gaussian primitive information is missing");
2981                 retval = 2;
2982                 goto cleanup;
2983         }
2984         if (bset->mo == NULL) {
2985                 s_append_asprintf(errbuf, "MO coefficients were not found");
2986                 retval = 2;
2987                 goto cleanup;
2988         }
2989         if (sSetupGaussianCoefficients(bset) != 0) {
2990                 s_append_asprintf(errbuf, "Internal error during setup MO calculation");
2991                 retval = 2;
2992                 goto cleanup;
2993         }
2994         mp->nframes = -1;
2995         retval = 0;
2996 cleanup:
2997         fclose(fp);
2998         if (iary != NULL)
2999                 free(iary);
3000         if (dary != NULL)
3001                 free(dary);
3002         if (retval != 0) {
3003                 if (mp->bset != NULL) {
3004                         BasisSetRelease(mp->bset);
3005                         mp->bset = NULL;
3006                 }
3007         }
3008         return retval;
3009 panic:
3010         Panic("low memory while reading fchk file %s", fname);
3011         return -1; /* not reached */    
3012 }
3013
3014 int
3015 MoleculeLoadGamessDatFile(Molecule *mol, const char *fname, char **errbuf)
3016 {
3017         FILE *fp;
3018         int newmol = 0;
3019         char buf[1024];
3020         int lineNumber, i, j, k, len, natoms = 0;
3021         int nframes = 0;
3022         int n1;
3023         int retval = 0;
3024         int ival[8];
3025         double dval[8];
3026         char sval[16];
3027         Vector *vbuf = NULL;
3028         IntGroup *ig;
3029         int optimizing = 0, status = 0;
3030         
3031         *errbuf = NULL;
3032         if (mol == NULL) {
3033                 mol = MoleculeNew();
3034         }
3035         if (mol->natoms == 0)
3036                 newmol = 1;
3037
3038         fp = fopen(fname, "rb");
3039         if (fp == NULL) {
3040                 s_append_asprintf(errbuf, "Cannot open file");
3041                 return 1;
3042         }
3043         
3044         /*  ESP is cleared (not undoable!)  */
3045         if (mol->elpots != NULL) {
3046                 free(mol->elpots);
3047                 mol->elpots = NULL;
3048                 mol->nelpots = 0;
3049         }
3050         
3051         lineNumber = 0;
3052         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3053         redo:
3054                 n1 = 0;
3055                 if (strncmp(buf, " $DATA", 6) == 0) {
3056                         /*  Initial geometry  */
3057                         if (!newmol) {
3058                                 vbuf = (Vector *)calloc(sizeof(Vector), mol->natoms);
3059                         }
3060                         i = 0;
3061                         ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Title  */
3062                         ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Symmetry  */
3063                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3064                                 if (strncmp(buf, " $END", 5) == 0)
3065                                         break;
3066                                 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3067                                         s_append_asprintf(errbuf, "Line %d: bad format in $DATA section", lineNumber);
3068                                         retval = 2;
3069                                         goto exit_loop;
3070                                 }
3071                                 if (newmol) {
3072                                         Atom a;
3073                                         memset(&a, 0, sizeof(a));
3074                                         strncpy(a.aname, sval, 4);
3075                                         a.r.x = dval[1];
3076                                         a.r.y = dval[2];
3077                                         a.r.z = dval[3];
3078                                         a.atomicNumber = (Int)dval[0];
3079                                         strncpy(a.element, ElementToString(a.atomicNumber, sval), 3);
3080                                         a.type = AtomTypeEncodeToUInt(a.element);
3081                                         a.weight = WeightForAtomicNumber(a.atomicNumber);
3082                                         MoleculeCreateAnAtom(mol, &a, mol->natoms);
3083                                 } else {
3084                                         Atom *ap;
3085                                         if (i >= mol->natoms) {
3086                                                 s_append_asprintf(errbuf, "Line %d: too many atoms", lineNumber);
3087                                                 retval = 3;
3088                                                 goto exit_loop;
3089                                         }
3090                                         if ((ap = ATOM_AT_INDEX(mol->atoms, i))->atomicNumber != dval[0]) {
3091                                                 s_append_asprintf(errbuf, "Line %d: atomic number does not match", lineNumber);
3092                                                 retval = 4;
3093                                                 goto exit_loop;
3094                                         }
3095                                         vbuf[i].x = dval[1];
3096                                         vbuf[i].y = dval[2];
3097                                         vbuf[i].z = dval[3];
3098                                 }
3099                                 /*  Skip until a blank line is found  */
3100                                 /*  2013.6.11. Line including "PM3" is also recognized as the end of atom  */
3101                                 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3102                                         for (j = 0; buf[j] == ' '; j++);
3103                                         if (buf[j] == '\n' || strncmp(buf, "PM3", 3) == 0)
3104                                                 break;
3105                                 }
3106                                 i++;
3107                         }
3108                         natoms = i;
3109                         if (!newmol) {
3110                                 /*  Set atom positions  */
3111                                 IntGroup *ig;
3112                                 if (natoms < mol->natoms) {
3113                                         s_append_asprintf(errbuf, "Line %d: too few atoms", lineNumber);
3114                                         retval = 5;
3115                                         goto exit_loop;
3116                                 }
3117                                 ig = IntGroupNewWithPoints(0, natoms, -1);
3118                                 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, natoms, vbuf);
3119                                 IntGroupRelease(ig);
3120                         }
3121                         if (vbuf == NULL)
3122                                 vbuf = (Vector *)calloc(sizeof(Vector), natoms);
3123                         nframes = MoleculeGetNumberOfFrames(mol);
3124                         if (status < 0)
3125                                 break;
3126                         continue;
3127                 } else if (strstr(buf, "DATA FROM NSERCH") != NULL || (strstr(buf, "RESULTS FROM SUCCESSFUL") != NULL && (n1 = 1))) {
3128                         /*  Skip until the separator line is read (three or four lines)  */
3129                         i = 0;
3130                         do {
3131                                 if (i++ >= 4) {
3132                                         s_append_asprintf(errbuf, "Line %d: the separator line at the top of the coordinates is not found: bad format?", lineNumber);
3133                                         retval = 6;
3134                                         goto exit_loop;
3135                                 }
3136                                 ReadLine(buf, sizeof buf, fp, &lineNumber);
3137                         } while (strstr(buf, "----------------------------") == NULL);
3138                         for (i = 0; i < natoms; i++) {
3139                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3140                                         s_append_asprintf(errbuf, "Unexpected end of file in reading NSERCH data");
3141                                         retval = 6;
3142                                         goto exit_loop;
3143                                 }
3144                                 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3145                                         s_append_asprintf(errbuf, "Line %d: bad format in NSERCH coordinate data", lineNumber);
3146                                         retval = 6;
3147                                         goto exit_loop;
3148                                 }
3149                                 vbuf[i].x = dval[1];
3150                                 vbuf[i].y = dval[2];
3151                                 vbuf[i].z = dval[3];
3152                         }
3153                         ig = IntGroupNewWithPoints(nframes, 1, -1);
3154                         MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, natoms, vbuf, 0, NULL);
3155                         IntGroupRelease(ig);
3156                         nframes++;
3157                         if (n1 == 0)
3158                                 optimizing = 1;  /*  Flag to skip reading the VEC group  */
3159                         else
3160                                 optimizing = 0;
3161                         continue;
3162                 } else if (strstr(buf, "E(UHF)") != NULL || (strstr(buf, "E(RHF)") != NULL && (n1 = 1)) || (strstr(buf, "E(ROHF)") != NULL && (n1 = 2))) {
3163                         if (mol->bset == NULL) {
3164                                 i = MoleculeAllocateBasisSetRecord(mol, n1, 0, 0);
3165                                 if (i != 0) {
3166                                         s_append_asprintf(errbuf, "Line %d: cannot allocate basis set internal buffer", lineNumber);
3167                                         retval = 8;
3168                                         goto exit_loop;
3169                                 }
3170                         }
3171                 } else if (strncmp(buf, " $VEC", 5) == 0) {
3172                         Double *coeffs;
3173                         /*  Read the vec group  */
3174                         if (mol->bset == NULL || mol->bset->ncomps == 0)
3175                                 continue;  /*  Just ignore  */
3176                         if (optimizing)
3177                                 continue;  /*  Ignore VEC group during optimization  */
3178                         coeffs = (Double *)calloc(sizeof(Double), mol->bset->ncomps);
3179                         if (coeffs == NULL) {
3180                                 s_append_asprintf(errbuf, "Line %d: low memory during $VEC", lineNumber);
3181                                 retval = 9;
3182                                 goto exit_loop;
3183                         }
3184                         i = k = 0;
3185                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3186                                 len = strlen(buf);
3187                                 if (strncmp(buf, " $END", 5) == 0)
3188                                         break;
3189                                 while ((j = 5 + (k % 5) * 15) <= len && buf[j] != 0 && buf[j] != '\n') {
3190                                         strncpy(sval, buf + j, 15);
3191                                         sval[15] = 0;
3192                                         coeffs[k] = strtod(sval, NULL);
3193                                         k++;
3194                                         if ((k % 5) == 0)
3195                                                 break;
3196                                 }
3197                                 if (k < mol->bset->ncomps)
3198                                         continue;
3199                                 j = MoleculeSetMOCoefficients(mol, i, -1000000, k, coeffs);
3200                                 if (j != 0) {
3201                                         s_append_asprintf(errbuf, "Line %d: cannot set coefficients for MO %d", lineNumber, i + 1);
3202                                         free(coeffs);
3203                                         retval = 10;
3204                                         goto exit_loop;
3205                                 }
3206                                 i++;
3207                                 k = 0;
3208                         }
3209                         if (status < 0)
3210                                 break;
3211                         continue;
3212                 } else if ((strstr(buf, "ELECTRIC POTENTIAL") != NULL || strstr(buf, "ELECTROSTATIC POTENTIAL") != NULL) && strstr(buf, "ELPOTT") != NULL) {
3213                         i = 0;
3214                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3215                                 Elpot *ep;
3216                                 if (strstr(buf, "TOTAL NUMBER OF GRID POINTS") != NULL)
3217                                         continue;
3218                                 if (sscanf(buf, "%d %lf %lf %lf %lf", &ival[0], &dval[0], &dval[1], &dval[2], &dval[3]) < 5)
3219                                         break;
3220                                 ep = AssignArray(&mol->elpots, &mol->nelpots, sizeof(Elpot), i, NULL);
3221                                 ep->pos.x = dval[0];
3222                                 ep->pos.y = dval[1];
3223                                 ep->pos.z = dval[2];
3224                                 ep->esp = dval[3];
3225                                 i++;
3226                         }
3227                         if (status > 0)
3228                                 goto redo;  /*  This section has no end line, so the last line should be processed again  */
3229                         else break;    /*  End of file encountered or interrupted */
3230                 }  /*  TODO: read MOLPLT info if present  */
3231         }
3232         if (status < 0) {
3233                 s_append_asprintf(errbuf, "User interrupt at line %d", lineNumber);
3234                 retval = 11;
3235         }
3236 exit_loop:
3237         if (vbuf != NULL)
3238                 free(vbuf);
3239         if (mol->natoms > 0)
3240                 retval = 0;  /*  Return the partially constructed molecule  */
3241         if (newmol && mol->nbonds == 0) {
3242                 /*  Guess bonds  */
3243                 Int nbonds, *bonds;
3244                 MoleculeGuessBonds(mol, 1.2, &nbonds, &bonds);
3245                 if (nbonds > 0) {
3246                         MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
3247                         free(bonds);
3248                 }
3249         }
3250         return 0;
3251 }
3252
3253 int
3254 MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
3255 {
3256         int retval;
3257         if (ftype == NULL || *ftype == 0) {
3258                 const char *cp;
3259                 cp = strrchr(fname, '.');
3260                 if (cp != NULL)
3261                         ftype = cp + 1;
3262                 else {
3263                         cp = guessMoleculeType(fname);
3264                         if (strcmp(cp, "???") != 0)
3265                                 ftype = cp;
3266                 }
3267         }
3268         if (strcasecmp(ftype, "pdb") == 0) {
3269                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3270         }
3271         if (retval != 0) {
3272                 /*  Try all formats once again  */
3273                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3274         }
3275         return retval;
3276 }
3277
3278 int
3279 MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char **errbuf)
3280 {
3281         FILE *fp;
3282         char buf[1024];
3283         char *p;
3284         int lineNumber;
3285         int i, j, new_unit, retval;
3286         Atom *ap;
3287         IntGroup *ig;
3288         Vector *vp = NULL;
3289         Int ibuf[12];
3290         Int entries = 0;
3291         retval = 0;
3292         *errbuf = NULL;
3293         fp = fopen(fname, "rb");
3294         if (fp == NULL) {
3295                 s_append_asprintf(errbuf, "Cannot open file");
3296                 return -1;
3297         }
3298 /*      flockfile(fp); */
3299         if (mp->natoms == 0)
3300                 new_unit = 1;
3301         else {
3302                 /*  Allocate buffer for undo-capable modification  */
3303                 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
3304                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3305                         /*  Retain current position if the atom info is missing in the input file  */
3306                         vp[i] = ap->r;
3307                 }
3308                 new_unit = 0;
3309         }
3310         lineNumber = 0;
3311         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3312                 if (strncmp(buf, "END", 3) == 0)
3313                         break;
3314                 if (strncmp(buf, "HETATM", 6) == 0 || strncmp(buf, "ATOM", 4) == 0) {
3315                         struct {
3316                                 Int serial, intCharge, resSeq;
3317                                 Vector r;
3318                                 Double occ, temp;
3319                                 char segName[5], resName[4], atomName[5], resSeqStr[5], atomType[3], element[3], occStr[6];
3320                         } w;
3321                         memset(&w, 0, sizeof(w));
3322                         ReadFormat(buf, "x6 I5 x1 S4 x1 S3 x1 x1 S4 x1 x3 F8 F8 F8 S6 F6 x6 S4 S2 I2",
3323                                 &w.serial, w.atomName, w.resName, w.resSeqStr, &w.r.x, &w.r.y, &w.r.z,
3324                                 w.occStr, &w.temp, w.segName, w.element, &w.intCharge);
3325                         if (w.atomName[0] == 0) {
3326                                 continue;  /*  Atom name is empty  */
3327                         }
3328                         /*  A workaround for residue number >= 10000 (XPLOR style)  */
3329                         if (w.resSeqStr[0] >= 'A' && w.resSeqStr[0] <= 'Z') {
3330                                 w.resSeq = (w.resSeqStr[0] - 'A' + 10) * 1000 + atoi(w.resSeqStr + 1);
3331                         } else {
3332                                 w.resSeq = atoi(w.resSeqStr);
3333                         }
3334                         if (w.element[0] == 0) {
3335                                 /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
3336                                 for (p = w.atomName; *p != 0; p++) {
3337                                         if (isalpha(*p) && *p != '_') {
3338                                                 w.element[0] = toupper(*p);
3339                                                 if (isalpha(p[1]) && p[1] != '_') {
3340                                                         w.element[1] = toupper(p[1]);
3341                                                         w.element[2] = 0;
3342                                                 } else {
3343                                                         w.element[1] = 0;
3344                                                 }
3345                                                 break;
3346                                         }
3347                                 }
3348                         }
3349                         if (w.occStr[0] == 0)
3350                                 w.occ = 1.0;
3351                         else
3352                                 w.occ = atof(w.occStr);
3353                         if (w.serial <= 0) {
3354                                 s_append_asprintf(errbuf, "line %d: non-positive atom number %d", lineNumber, w.serial);
3355                                 retval = 1;
3356                                 goto abort;
3357                         }
3358                         w.serial--;  /*  The internal atom number is 0-based  */
3359                         if (w.serial >= mp->natoms) {
3360                                 if (new_unit) {
3361                                         /*  Create a new atom entry  */
3362                                         ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, w.serial, NULL);
3363                                 } else {
3364                                         s_append_asprintf(errbuf, "line %d: the atom number %d does not exist in the structure file", lineNumber, w.serial+1);
3365                                         retval = 1;
3366                                         goto abort;
3367                                 }
3368                         }
3369                         if (new_unit) {
3370                                 ap = ATOM_AT_INDEX(mp->atoms, w.serial);
3371                                 ap->r = w.r;
3372                                 ap->occupancy = w.occ;
3373                                 ap->tempFactor = w.temp;
3374                                 if (w.segName[0] == 0)
3375                                         strncpy(w.segName, "MAIN", 4);
3376                                 strncpy(ap->segName, w.segName, 4);
3377                                 ap->resSeq = w.resSeq;
3378                                 strncpy(ap->resName, w.resName, 4);
3379                                 strncpy(ap->aname, w.atomName, 4);
3380                                 strncpy(ap->element, w.element, 2);
3381                                 ap->element[2] = 0;
3382                                 ap->atomicNumber = ElementToInt(ap->element);
3383                                 ap->type = AtomTypeEncodeToUInt(ap->element);
3384                                 ap->weight = WeightForAtomicNumber(ap->atomicNumber);
3385                                 ap->intCharge = w.intCharge;
3386                                 if (ap->resSeq > 0) {
3387                                         if (ap->resSeq < mp->nresidues) {
3388                                                 /*  Update the resName according to residues[]  */
3389                                                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
3390                                         } else {
3391                                                 /*  Register the resName to residues[]  */
3392                                                 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, w.resName);
3393                                         }
3394                                 } else {
3395                                         ap->resSeq = 0;
3396                                         strcpy(ap->resName, "XXX");
3397                                         if (mp->nresidues == 0)
3398                                                 AssignArray(&mp->residues, &mp->nresidues, 4, 0, ap->resName);
3399                                 }
3400                                 i = ElementToInt(ap->element);
3401                                 if (i >= 0)
3402                                         ap->weight = gElementParameters[i].weight;
3403                         } else {
3404                                 /*  Not a new unit: only the atom position is updated  */
3405                                 vp[w.serial] = w.r;
3406                         }
3407                         entries++;
3408                 } else if (strncmp(buf, "CONECT", 6) == 0 && new_unit) {
3409                         i = ReadFormat(buf, "x6 I5I5I5I5I5I5I5I5I5I5I5I5",
3410                                 ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
3411                                 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7,
3412                                 ibuf + 8, ibuf + 9, ibuf + 10, ibuf + 11);
3413                         if (i >= 2) {
3414                                 Int bbuf[25];
3415                                 int bi;
3416                                 for (j = 0; j < i; j++) {
3417                                         if (ibuf[j] < 0 || ibuf[j] > mp->natoms) {
3418                                                 s_append_asprintf(errbuf, "line %d: The CONECT record contains non-existent atom %d", lineNumber, ibuf[j]);
3419                                                 retval = 1;
3420                                                 goto abort;
3421                                         } else if (ibuf[j] == 0)
3422                                                 break;
3423                                 }
3424                                 i = j;
3425                                 if (i < 2)
3426                                         continue;
3427                                 for (j = 1, bi = 0; j < i; j++) {
3428                                         if (ibuf[0] < ibuf[j]) {
3429                                                 if (MoleculeLookupBond(mp, ibuf[0], ibuf[j]) >= 0) {
3430                                                         s_append_asprintf(errbuf, "line %d: warning: duplicate bond %d-%d\n", lineNumber, ibuf[0], ibuf[j]);
3431                                                 } else {
3432                                                         bbuf[bi * 2] = ibuf[0] - 1;
3433                                                         bbuf[bi * 2 + 1] = ibuf[j] - 1;
3434                                                         bi++;
3435                                                 }
3436                                         }
3437                                 }
3438                                 if (bi == 0)
3439                                         continue;
3440                                 bbuf[bi * 2] = -1;
3441                                 retval = MoleculeAddBonds(mp, bi, bbuf, NULL, 1);
3442                                 if (retval < 0) {
3443                                         s_append_asprintf(errbuf, "line %d: bad bond specification", lineNumber);
3444                                         retval = 1;
3445                                         goto abort;
3446                                 }
3447                         }
3448                 }
3449         }
3450 /*      funlockfile(fp); */
3451         fclose(fp);
3452         if (new_unit) {
3453                 /*  Renumber atoms if some atom number is unoccupied  */
3454                 int *old2new, oldidx, newidx;
3455                 old2new = (int *)calloc(sizeof(int), mp->natoms);
3456                 if (old2new == NULL) {
3457                         s_append_asprintf(errbuf, "Out of memory");
3458                         retval = 1;
3459                         goto abort;
3460                 }
3461                 for (oldidx = newidx = 0; oldidx < mp->natoms; oldidx++) {
3462                         ap = ATOM_AT_INDEX(mp->atoms, oldidx);
3463                         if (ap->aname[0] != 0) {
3464                                 old2new[oldidx] = newidx;
3465                                 if (oldidx > newidx)
3466                                         memmove(ATOM_AT_INDEX(mp->atoms, newidx), ap, gSizeOfAtomRecord);
3467                                 newidx++;
3468                         }
3469                 }
3470                 mp->natoms = newidx;
3471                 if (oldidx > newidx) {
3472                         /*  Renumber the connects and bonds  */
3473                         Int *cp;
3474                         for (i = 0; i < mp->natoms; i++) {
3475                                 ap = ATOM_AT_INDEX(mp->atoms, i);
3476                                 cp = AtomConnectData(&ap->connect);
3477                                 for (j = 0; j < ap->connect.count; j++) {
3478                                         cp[j] = old2new[cp[j]];
3479                                 }
3480                         }
3481                         for (i = 0; i < mp->nbonds * 2; i++) {
3482                                 mp->bonds[i] = old2new[mp->bonds[i]];
3483                         }
3484                 }
3485                 retval = MoleculeRebuildTablesFromConnects(mp);
3486                 if (retval != 0) {
3487                         /*  This error may not happen  */
3488                         s_append_asprintf(errbuf, "Cannot build angle/dihedral/improper tables");
3489                         retval = 1;
3490                         goto abort;
3491                 }
3492                 /*  Undo action: delete all atoms  */
3493                 {
3494                         MolAction *act;
3495                         ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3496                         act = MolActionNew(gMolActionUnmergeMolecule, ig);
3497                         act->frame = mp->cframe;
3498                         MolActionCallback_registerUndo(mp, act);
3499                         MolActionRelease(act);
3500                         IntGroupRelease(ig);
3501                 }
3502         } else {
3503                 /*  Set the new atom positions  */
3504                 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3505                 MolActionCreateAndPerform(mp, gMolActionSetAtomPositions, ig, mp->natoms, vp);
3506                 IntGroupRelease(ig);
3507                 free(vp);
3508                 vp = NULL;
3509         }
3510         mp->nframes = -1;  /*  Should be recalculated later  */
3511         if (entries == 0)
3512                 return 1;  /*  No atoms  */
3513         return 0;
3514         abort:
3515         if (fp != NULL) {
3516         /*      funlockfile(fp); */
3517                 fclose(fp);
3518         }
3519         if (vp != NULL)
3520                 free(vp);
3521         if (entries == 0)
3522                 return 1;  /*  Maybe different format?  */
3523         return retval;
3524 }
3525
3526 int
3527 MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char **errbuf)
3528 {
3529         DcdRecord dcd;
3530         SFloat32 *xp, *yp, *zp;
3531         Vector *vp, *cp;
3532         IntGroup *ig;
3533         int n, errcount = 0;
3534         *errbuf = NULL;
3535         if (mp == NULL || mp->natoms == 0) {
3536                 s_append_asprintf(errbuf, "Molecule is empty");
3537                 return 1;
3538         }
3539         n = DcdOpen(fname, &dcd);
3540         if (n != 0) {
3541                 switch (n) {
3542                         case -2: s_append_asprintf(errbuf, "Cannot open file"); break;
3543                         case 1:  s_append_asprintf(errbuf, "Premature EOF encountered"); break;
3544                         case 2:  s_append_asprintf(errbuf, "Bad block length of the first section"); break;
3545                         case 3:  s_append_asprintf(errbuf, "\"CORD\" signature is missing"); break;
3546                         case 4:  s_append_asprintf(errbuf, "Bad termination of the first section"); break;
3547                         case 5:  s_append_asprintf(errbuf, "The title section is not correct"); break;
3548                         case 6:  s_append_asprintf(errbuf, "The atom number section is not correct"); break;
3549                         default: s_append_asprintf(errbuf, "Read error in dcd file"); break;
3550                 }
3551                 errcount++;
3552         } else {
3553                 if (dcd.natoms == 0) {
3554                         s_append_asprintf(errbuf, "No atoms were found in the dcd file");
3555                         errcount++;
3556                 } else if (dcd.nframes == 0) {
3557                         s_append_asprintf(errbuf, "No frames were found in the dcd file");
3558                         errcount++;
3559                 }
3560         }
3561         if (errcount > 0) {
3562                 if (n == 0)
3563                         DcdClose(&dcd);
3564                 return 1;
3565         }
3566
3567         vp = (Vector *)calloc(sizeof(Vector), mp->natoms * dcd.nframes);
3568         if (dcd.nextra)
3569                 cp = (Vector *)calloc(sizeof(Vector), dcd.nframes * 4);
3570         else cp = NULL;
3571         xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3572         yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3573         zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3574         ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mp), dcd.nframes, -1);
3575         if (vp == NULL || xp == NULL || yp == NULL || zp == NULL || ig == NULL) {
3576                 s_append_asprintf(errbuf, "Cannot allocate memory");
3577                 if (vp) free(vp);
3578                 if (cp) free(cp);
3579                 if (xp) free(xp);
3580                 if (yp) free(yp);
3581                 if (zp) free(zp);
3582                 if (ig) IntGroupRelease(ig);
3583                 return 1;
3584         }
3585         for (n = 0; n < dcd.nframes; n++) {
3586                 int i;
3587                 Vector *vpp;
3588                 SFloat32 dcdcell[6];
3589                 if (DcdReadFrame(&dcd, n, xp, yp, zp, dcdcell)) {
3590                         s_append_asprintf(errbuf, "Read error in dcd file");
3591                         goto exit;
3592                 }
3593                 for (i = 0, vpp = &vp[n * mp->natoms]; i < dcd.natoms && i < mp->natoms; i++, vpp++) {
3594                         vpp->x = xp[i];
3595                         vpp->y = yp[i];
3596                         vpp->z = zp[i];
3597                 }
3598                 if (cp != NULL) {
3599                         Double sing;
3600                         vpp = &cp[n * 4];
3601                         /*  dcdcell = {a, gamma, b, beta, alpha, c} */
3602                         /*  angles are described either in cosines (Charmm and NAMD > 2.5) or degrees (NAMD 2.5)  */
3603                         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) {
3604                                 dcdcell[4] = cos(dcdcell[4] * kDeg2Rad);  /*  cos(alpha)  */
3605                                 dcdcell[3] = cos(dcdcell[3] * kDeg2Rad);  /*  cos(beta)  */
3606                                 dcdcell[1] = cos(dcdcell[1] * kDeg2Rad);  /*  cos(gamma)  */
3607                         }
3608                         /*  a axis lies along the cartesian x axis  */
3609                         sing = sqrt(1 - dcdcell[1] * dcdcell[1]);
3610                         vpp[0].x = dcdcell[0];
3611                         vpp[0].y = 0;
3612                         vpp[0].z = 0;
3613                         vpp[1].x = dcdcell[2] * dcdcell[1];
3614                         vpp[1].y = dcdcell[2] * sing;
3615                         vpp[1].z = 0;
3616                         vpp[2].x = dcdcell[5] * dcdcell[3];
3617                         vpp[2].y = dcdcell[5] * (dcdcell[4] - dcdcell[3] * dcdcell[1]) / sing;
3618                         vpp[2].z = sqrt(dcdcell[5] * dcdcell[5] - vpp[2].x * vpp[2].x - vpp[2].y * vpp[2].y);
3619                         vpp[3].x = vpp[3].y = vpp[3].z = 0.0;
3620                         if (mp->cell == NULL) {
3621                                 /*  Create periodicity if not present  */
3622                                 MolActionCreateAndPerform(mp, gMolActionSetBox, &vpp[0], &vpp[1], &vpp[2], &vpp[3], 7, 0);
3623                         }
3624                 }
3625         }
3626         if (MolActionCreateAndPerform(mp, gMolActionInsertFrames, ig, mp->natoms * dcd.nframes, vp, (cp == NULL ? 0 : dcd.nframes * 4), cp) != 0)
3627                 s_append_asprintf(errbuf, "Cannot insert frames");
3628         mp->startStep = dcd.nstart;
3629         mp->stepsPerFrame = dcd.ninterval;
3630         mp->psPerStep = dcd.delta;
3631 exit:
3632         DcdClose(&dcd);
3633         if (cp != NULL)
3634                 free(cp);
3635         free(vp);
3636         free(xp);
3637         free(yp);
3638         free(zp);
3639         IntGroupRelease(ig);
3640         if (errcount == 0)
3641                 return 0;
3642         else return 1;
3643 }
3644
3645 int
3646 MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
3647 {
3648         FILE *fp;
3649         char buf[1024];
3650         int lineNumber;
3651         int i, retval;
3652         Vector v[3], vv;
3653         double d[3];
3654         int n, flag;
3655         char flags[3];
3656         *errbuf = NULL;
3657         fp = fopen(fname, "rb");
3658         if (fp == NULL) {
3659                 s_append_asprintf(errbuf, "Cannot open file");
3660                 return -1;
3661         }
3662         errbuf[0] = 0;
3663         lineNumber = 0;
3664         retval = 0;
3665         flags[0] = flags[1] = flags[2] = 0;
3666         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3667                 if (strncmp(buf, "Bounding box:", 13) == 0) {
3668                         for (i = 0; i < 3; i++) {
3669                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3670                                         s_append_asprintf(errbuf, "line %d: missing %d component of the bounding box", lineNumber, i + 1);
3671                                         retval = 1;
3672                                         goto abort;
3673                                 }
3674                                 n = sscanf(buf, "%lf %lf %lf %d", &d[0], &d[1], &d[2], &flag);
3675                                 if (n < 3) {
3676                                         vv.x = vv.y = vv.z = 0.0;
3677                                         switch (i) {
3678                                                 case 0: vv.x = d[0]; break;
3679                                                 case 1: vv.y = d[0]; break;
3680                                                 case 2: vv.z = d[0]; break;
3681                                         }
3682                                         if (n == 1 || (n == 2 && d[1] != 0.0))
3683                                                 flags[i] = 1;
3684                                 } else {
3685                                         vv.x = d[0];
3686                                         vv.y = d[1];
3687                                         vv.z = d[2];
3688                                         if (n == 4)
3689                                                 flags[i] = (flag != 0);
3690                                         else
3691                                                 flags[i] = (VecLength2(vv) != 0);
3692                                 }
3693                                 v[i] = vv;
3694                         }
3695                         if (mp->cell != NULL)
3696                                 vv = mp->cell->origin;
3697                         else
3698                                 vv.x = vv.y = vv.z = 0.0;
3699                         MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
3700                 } else if (strncmp(buf, "Bounding box origin:", 20) == 0) {
3701                         if (mp->cell != NULL) {
3702                                 v[0] = mp->cell->axes[0];
3703                                 v[1] = mp->cell->axes[1];
3704                                 v[2] = mp->cell->axes[2];
3705                                 memmove(flags, mp->cell->flags, 3);
3706                         } else {
3707                                 v[0].x = 1.0; v[0].y = v[0].z = 0.0;
3708                                 v[1].y = 1.0; v[1].x = v[1].z = 0.0;
3709                                 v[2].z = 1.0; v[2].x = v[2].y = 0.0;
3710                                 flags[0] = flags[1] = flags[2] = 1.0;
3711                         }
3712                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0 || (n = sscanf(buf, "%lf %lf %lf", &d[0], &d[1], &d[2]) < 3)) {
3713                                 s_append_asprintf(errbuf, "line %d: wrong format for the bounding box origin", lineNumber);
3714                                 retval = 1;
3715                                 goto abort;
3716                         }
3717                         vv.x = d[0];
3718                         vv.y = d[1];
3719                         vv.z = d[2];
3720                         MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
3721                 }
3722         }
3723         fclose(fp);
3724         return 0;
3725 abort:
3726         if (fp != NULL)
3727                 fclose(fp);
3728         return retval;
3729 }
3730                         
3731 int
3732 MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
3733 {
3734         int retval;
3735         *errbuf = NULL;
3736         if (ftype == NULL || *ftype == 0) {
3737                 const char *cp;
3738                 cp = strrchr(fname, '.');
3739                 if (cp != NULL)
3740                         ftype = cp + 1;
3741                 else {
3742                         cp = guessMoleculeType(fname);
3743                         if (strcmp(cp, "???") != 0)
3744                                 ftype = cp;
3745                 }
3746         }
3747         if (strcasecmp(ftype, "psf") == 0) {
3748                 retval = MoleculeWriteToPsfFile(mp, fname, errbuf);
3749         } else if (strcasecmp(ftype, "pdb") == 0) {
3750                 retval = MoleculeWriteToPdbFile(mp, fname, errbuf);
3751         } else if (strcasecmp(ftype, "tep") == 0) {
3752                 retval = MoleculeWriteToTepFile(mp, fname, errbuf);
3753         } else {
3754                 s_append_asprintf(errbuf, "The file format should be specified");
3755                 retval = 1;
3756         }
3757         if (retval == 0)
3758                 MoleculeSetPath(mp, fname);
3759         return retval;
3760 }
3761
3762 int
3763 MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char **errbuf)
3764 {
3765         FILE *fp;
3766         Int i, j, k, n1, n2, n3, n_aniso, nframes, nanchors;
3767         Atom *ap;
3768         char bufs[6][8];
3769
3770         *errbuf = NULL;
3771         fp = fopen(fname, "wb");
3772         if (fp == NULL) {
3773                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
3774                 return 1;
3775         }
3776         errbuf[0] = 0;
3777
3778         nframes = MoleculeFlushFrames(mp);
3779
3780         fprintf(fp, "!:atoms\n");
3781         fprintf(fp, "! idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge\n");
3782         n1 = n2 = n3 = n_aniso = nanchors = 0;
3783         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3784                 strncpy(bufs[0], ap->segName, 4);
3785                 bufs[0][4] = 0;
3786                 strncpy(bufs[1], ap->resName, 4);
3787                 bufs[1][4] = 0;
3788                 strncpy(bufs[2], ap->aname, 4);
3789                 bufs[2][4] = 0;
3790                 AtomTypeDecodeToString(ap->type, bufs[3]);
3791                 bufs[3][6] = 0;
3792                 strncpy(bufs[4], ap->element, 4);
3793                 bufs[4][2] = 0;
3794                 for (j = 0; j < 5; j++) {
3795                         if (bufs[j][0] == 0) {
3796                                 bufs[j][0] = '_';
3797                                 bufs[j][1] = 0;
3798                         }
3799                         for (k = 0; k < 6; k++) {
3800                                 if (bufs[j][k] == 0)
3801                                         break;
3802                                 if (bufs[j][k] > 0 && bufs[j][k] < ' ')
3803                                         bufs[j][k] = '_';
3804                         }
3805                 }
3806                 if (SYMOP_ALIVE(ap->symop))
3807                         n1++;
3808                 if (ap->fix_force != 0)
3809                         n2++;
3810                 if (ap->mm_exclude || ap->periodic_exclude)
3811                         n3++;
3812                 if (ap->aniso != NULL)
3813                         n_aniso++;
3814                 if (ap->anchor != NULL)
3815                         nanchors++;
3816                 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);
3817         }
3818         fprintf(fp, "\n");
3819         
3820         if (n1 > 0) {
3821                 fprintf(fp, "!:atoms_symop\n");
3822                 fprintf(fp, "! idx symop symbase\n");
3823                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3824                         int n;
3825                         n = ap->symop.sym * 1000000 + ap->symop.dx * 10000 + ap->symop.dy * 100 + ap->symop.dz;
3826                         fprintf(fp, "%d %d %d\n", i, n, ap->symbase);
3827                 }
3828                 fprintf(fp, "\n");
3829         }
3830         
3831         if (n2 > 0) {
3832                 fprintf(fp, "!:atoms_fix\n");
3833                 fprintf(fp, "! idx fix_force fix_pos\n");
3834                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3835                         fprintf(fp, "%d %f %f %f %f\n", i, ap->fix_force, ap->fix_pos.x, ap->fix_pos.y, ap->fix_pos.z);
3836                 }
3837                 fprintf(fp, "\n");
3838         }
3839         
3840         if (n3 > 0) {
3841                 fprintf(fp, "!:mm_exclude\n");
3842                 fprintf(fp, "! idx mm_exclude periodic_exclude\n");
3843                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3844                         fprintf(fp, "%d %d %d\n", i, ap->mm_exclude, ap->periodic_exclude);
3845                 }
3846                 fprintf(fp, "\n");
3847         }
3848         
3849         if (nanchors > 0) {
3850                 fprintf(fp, "!:pi_anchor\n");
3851                 fprintf(fp, "! idx count; n1 weight1; n2 weight2; ...; nN weightN\n");
3852                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3853                         Int *ip;
3854                         if (ap->anchor == NULL)
3855                                 continue;
3856                         k = ap->anchor->connect.count;
3857                         ip = AtomConnectData(&ap->anchor->connect);
3858                         fprintf(fp, "%d %d\n", i, k);
3859                         for (j = 0; j < k; j++) {
3860                                 fprintf(fp, "%d %f\n", ip[j], ap->anchor->coeffs[j]);
3861                         }
3862                 }
3863                 fprintf(fp, "\n");
3864         }
3865                                 
3866         n1 = nframes;
3867         if (n1 > 0)
3868                 n2 = mp->cframe;
3869         else
3870                 n2 = 0;
3871         for (i = 0; (i == n2 || i < n1); i++) {
3872                 fprintf(fp, "!:positions ; frame %d\n", i);
3873                 fprintf(fp, "! idx x y z [sx sy sz]\n");
3874                 for (j = 0, ap = mp->atoms; j < mp->natoms; j++, ap = ATOM_NEXT(ap)) {
3875                         Vector *vp;
3876                         Byte sig_flag = 0;
3877                         if (i != n2 && i < ap->nframes)
3878                                 vp = ap->frames + i;
3879                         else {
3880                                 vp = &(ap->r);
3881                                 if (ap->sigma.x != 0.0 || ap->sigma.y != 0.0 || ap->sigma.z != 0.0)
3882                                         sig_flag = 1;
3883                         }
3884                         fprintf(fp, "%d %.8f %.8f %.8f", j, vp->x, vp->y, vp->z);
3885                         if (sig_flag) {
3886                                 fprintf(fp, " %.8f %.8f %.8f", ap->sigma.x, ap->sigma.y, ap->sigma.z);
3887                         }
3888                         fprintf(fp, "\n");
3889                 }
3890                 fprintf(fp, "\n");
3891         }
3892         
3893         if (mp->nbonds > 0) {
3894                 fprintf(fp, "!:bonds\n");
3895                 fprintf(fp, "! from1 to1 from2 to2 from3 to3 from4 to4\n");
3896                 for (i = 0; i < mp->nbonds; i++) {
3897                         fprintf(fp, "%d %d%c", mp->bonds[i * 2], mp->bonds[i * 2 + 1], (i % 4 == 3 || i == mp->nbonds - 1 ? '\n' : ' '));
3898                 }
3899                 fprintf(fp, "\n");
3900         }
3901         
3902         if (mp->nangles > 0) {
3903                 fprintf(fp, "!:angles\n");
3904                 fprintf(fp, "! a1 b1 c1 a2 b2 c2 a3 b3 c3\n");
3905                 for (i = 0; i < mp->nangles; i++) {
3906                         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' : ' '));
3907                 }
3908                 fprintf(fp, "\n");
3909         }
3910         
3911         if (mp->ndihedrals > 0) {
3912                 fprintf(fp, "!:dihedrals\n");
3913                 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3914                 for (i = 0; i < mp->ndihedrals; i++) {
3915                         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' : ' '));
3916                 }
3917                 fprintf(fp, "\n");
3918         }
3919         
3920         if (mp->nimpropers > 0) {
3921                 fprintf(fp, "!:impropers\n");
3922                 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3923                 for (i = 0; i < mp->nimpropers; i++) {
3924                         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' : ' '));
3925                 }
3926                 fprintf(fp, "\n");
3927         }
3928         
3929         if (mp->cell != NULL) {
3930                 fprintf(fp, "!:xtalcell\n");
3931                 fprintf(fp, "! a b c alpha beta gamma\n");
3932                 fprintf(fp, "! This information is redundant and overridden by the following periodic_box info\n");
3933                 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]);
3934                 fprintf(fp, "\n");
3935
3936                 fprintf(fp, "!:periodic_box\n");
3937                 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");
3938                 for (i = 0; i < 3; i++)
3939                         fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->axes[i].x, mp->cell->axes[i].y, mp->cell->axes[i].z);
3940                 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->origin.x, mp->cell->origin.y, mp->cell->origin.z);
3941                 fprintf(fp, "%d %d %d%s\n", mp->cell->flags[0], mp->cell->flags[1], mp->cell->flags[2], (mp->cell->has_sigma ? " 1" : ""));
3942                 if (mp->cell->has_sigma) {
3943                         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]);
3944                 }
3945                 fprintf(fp, "\n");
3946         }
3947         
3948         if (mp->nframe_cells > 0) {
3949                 fprintf(fp, "!:frame_periodic_boxes\n");
3950                 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz\n");
3951                 for (i = 0; i < mp->nframe_cells * 4; i++) {
3952                         fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->frame_cells[i].x, mp->frame_cells[i].y, mp->frame_cells[i].z);
3953                 }
3954                 fprintf(fp, "\n");
3955         }
3956         
3957         if (mp->nsyms > 0) {
3958                 fprintf(fp, "!:symmetry_operations\n");
3959                 fprintf(fp, "! a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3\n");
3960                 for (i = 0; i < mp->nsyms; i++) {
3961                         Transform *tp = mp->syms + i;
3962                         const unsigned char s_index_order[12] = {0, 3, 6, 1, 4, 7, 2, 5, 8, 9, 10, 11};
3963                         for (j = 0; j < 12; j++)
3964                                 fprintf(fp, "%11.6f%c", (*tp)[s_index_order[j]], (j % 3 == 2 ? '\n' : ' '));
3965                 }
3966                 fprintf(fp, "\n");
3967         }
3968         
3969         if (n_aniso > 0) {
3970                 fprintf(fp, "!:anisotropic_thermal_parameters\n");
3971                 fprintf(fp, "! b11 b22 b33 b12 b13 b23 [sigma; sb11 sb22 sb33 sb12 sb13 sb23]\n");
3972                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3973                         if (ap->aniso != NULL) {
3974                                 Double *bp = ap->aniso->bij;
3975                                 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" : ""));
3976                                 if (ap->aniso->has_bsig) {
3977                                         bp = ap->aniso->bsig;
3978                                         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]);
3979                                 }
3980                         } else {
3981                                 fprintf(fp, "0 0 0 0 0 0\n");
3982                         }
3983                 }
3984                 fprintf(fp, "\n");              
3985         }
3986         
3987         if (mp->arena != NULL) {
3988                 MDArena *arena = mp->arena;
3989                 fprintf(fp, "!:md_parameters\n");
3990                 fprintf(fp, "log_file %s\n", arena->log_result_name);
3991                 fprintf(fp, "coord_file %s\n", arena->coord_result_name);
3992                 fprintf(fp, "vel_file %s\n", arena->vel_result_name);
3993                 fprintf(fp, "force_file %s\n", arena->force_result_name);
3994                 fprintf(fp, "debug_file %s\n", arena->debug_result_name);
3995                 fprintf(fp, "debug_output_level %d\n", arena->debug_output_level);
3996                 fprintf(fp, "step %d\n", arena->step);
3997                 fprintf(fp, "coord_output_freq %d\n", arena->coord_output_freq);
3998                 fprintf(fp, "energy_output_freq %d\n", arena->energy_output_freq);
3999                 fprintf(fp, "coord_frame %d\n", arena->coord_result_frame);
4000                 fprintf(fp, "timestep %g\n", arena->timestep);
4001                 fprintf(fp, "cutoff %g\n", arena->cutoff);
4002                 fprintf(fp, "electro_cutoff %g\n", arena->electro_cutoff);
4003                 fprintf(fp, "pairlist_distance %g\n", arena->pairlist_distance);
4004                 fprintf(fp, "switch_distance %g\n", arena->switch_distance);
4005                 fprintf(fp, "temperature %g\n", arena->temperature);
4006                 fprintf(fp, "andersen_freq %d\n", arena->andersen_thermo_freq);
4007                 fprintf(fp, "andersen_coupling %g\n", arena->andersen_thermo_coupling);
4008                 fprintf(fp, "random_seed %d\n", arena->random_seed);
4009                 fprintf(fp, "dielectric %g\n", arena->dielectric);
4010                 fprintf(fp, "gradient_convergence %g\n", arena->gradient_convergence);
4011                 fprintf(fp, "coordinate_convergence %g\n", arena->coordinate_convergence);
4012                 fprintf(fp, "use_xplor_shift %d\n", arena->use_xplor_shift);
4013                 fprintf(fp, "scale14_vdw %g\n", arena->scale14_vdw);
4014                 fprintf(fp, "scale14_elect %g\n", arena->scale14_elect);
4015                 fprintf(fp, "relocate_center %d\n", arena->relocate_center);
4016                 fprintf(fp, "surface_probe_radius %g\n", arena->probe_radius);
4017                 fprintf(fp, "surface_tension %g\n", arena->surface_tension);
4018                 fprintf(fp, "surface_potential_freq %d\n", arena->surface_potential_freq);
4019                 fprintf(fp, "use_graphite %d\n", arena->use_graphite);
4020                 fprintf(fp, "alchemical_lambda %g\n", arena->alchem_lambda);
4021                 fprintf(fp, "alchemical_delta_lambda %g\n", arena->alchem_dlambda);
4022                 if (arena->nalchem_flags > 0) {
4023                         fprintf(fp, "alchem_flags %d", arena->nalchem_flags);
4024                         for (i = 0; i < arena->nalchem_flags; i++) {
4025                                 if (i % 60 == 0)
4026                                         fputc('\n', fp);
4027                                 else if (i % 10 == 0)
4028                                         fputc(' ', fp);
4029                                 fputc('0' + arena->alchem_flags[i], fp);
4030                         }
4031                         fputc('\n', fp);
4032                 }
4033                 if (arena->pressure != NULL) {
4034                         Double *dp;
4035                         fprintf(fp, "pressure_freq %d\n", arena->pressure->freq);
4036                         fprintf(fp, "pressure_coupling %g\n", arena->pressure->coupling);
4037                         dp = arena->pressure->apply;
4038                         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]);
4039                         dp = arena->pressure->cell_flexibility;
4040                         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]);
4041                         fprintf(fp, "pressure_fluctuate_cell_origin %g\n", arena->pressure->fluctuate_cell_origin);
4042                         fprintf(fp, "pressure_fluctuate_cell_orientation %g\n", arena->pressure->fluctuate_cell_orientation);
4043                 }
4044                 fprintf(fp, "\n");
4045
4046                 if (mp->par != NULL) {
4047                         Parameter *par = mp->par;
4048                         fprintf(fp, "!:parameters\n");
4049                         ParameterAppendToFile(par, fp);
4050                         fprintf(fp, "\n");
4051                 }
4052                 
4053                 fprintf(fp, "!:velocity\n");
4054                 fprintf(fp, "! idx vx vy vz\n");
4055                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4056                         fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->v.x, ap->v.y, ap->v.z);
4057                 }
4058                 fprintf(fp, "\n");
4059
4060                 fprintf(fp, "!:force\n");
4061                 fprintf(fp, "! idx fx fy fz\n");
4062                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4063                         fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->f.x, ap->f.y, ap->f.z);
4064                 }
4065                 fprintf(fp, "\n");
4066         }
4067         
4068         if (mp->mview != NULL) {
4069                 float f[4];
4070                 if (mp->mview->track != NULL) {
4071                         fprintf(fp, "!:trackball\n");
4072                         fprintf(fp, "! scale; trx try trz; theta_deg x y z\n");
4073                         f[0] = TrackballGetScale(mp->mview->track);
4074                         fprintf(fp, "%f\n", f[0]);
4075                         TrackballGetTranslate(mp->mview->track, f);
4076                         fprintf(fp, "%f %f %f\n", f[0], f[1], f[2]);
4077                         TrackballGetRotate(mp->mview->track, f);
4078                         fprintf(fp, "%f %f %f %f\n", f[0], f[1], f[2], f[3]);
4079                         fprintf(fp, "\n");
4080                 }
4081                 fprintf(fp, "!:view\n");
4082                 fprintf(fp, "show_unit_cell %d\n", mp->mview->showUnitCell);
4083                 fprintf(fp, "show_periodic_box %d\n", mp->mview->showPeriodicBox);
4084                 fprintf(fp, "show_expanded_atoms %d\n", mp->mview->showExpandedAtoms);
4085                 fprintf(fp, "show_ellipsoids %d\n", mp->mview->showEllipsoids);
4086                 fprintf(fp, "show_hydrogens %d\n", mp->mview->showHydrogens);
4087                 fprintf(fp, "show_dummy_atoms %d\n", mp->mview->showDummyAtoms);
4088                 fprintf(fp, "show_rotation_center %d\n", mp->mview->showRotationCenter);
4089                 fprintf(fp, "show_graphite_flag %d\n", mp->mview->showGraphiteFlag);
4090                 fprintf(fp, "show_graphite %d\n", mp->mview->showGraphite);
4091                 fprintf(fp, "show_periodic_image_flag %d\n", mp->mview->showPeriodicImageFlag);
4092                 fprintf(fp, "show_periodic_image %d %d %d %d %d %d\n",
4093                                 mp->mview->showPeriodicImage[0], mp->mview->showPeriodicImage[1],
4094                                 mp->mview->showPeriodicImage[2], mp->mview->showPeriodicImage[3],
4095                                 mp->mview->showPeriodicImage[4], mp->mview->showPeriodicImage[5]);
4096                 fprintf(fp, "\n");
4097         }
4098
4099         fclose(fp);
4100         return 0;
4101 }
4102
4103 int
4104 MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char **errbuf)
4105 {
4106         FILE *fp;
4107         int i;
4108         Atom *ap;
4109         *errbuf = NULL;
4110         fp = fopen(fname, "wb");
4111         if (fp == NULL) {
4112                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4113                 return 1;
4114         }
4115         fprintf(fp, "PSF\n\n");
4116         fprintf(fp, "       1 !NTITLE\n");
4117         fprintf(fp, " REMARKS FILENAME=\n");
4118         fprintf(fp, "\n");
4119         
4120         /*  Atoms  */
4121         fprintf(fp, "%8d !NATOM\n", mp->natoms);
4122         for (i = 0; i < mp->natoms; i++) {
4123                 const char *fmt;
4124                 ap = ATOM_AT_INDEX(mp->atoms, i);
4125                 fprintf(fp, "%8d ", i + 1);
4126                 if (ap->resSeq >= 10000) {
4127                         fmt = "%-3.3s %-5d ";
4128                 } else {
4129                         fmt = "%-4.4s %-4d ";
4130                 }
4131                 fprintf(fp, fmt, ap->segName, ap->resSeq);
4132                 fprintf(fp, "%-3.3s  %-4.4s %-4.4s   %12.6f  %8.4f           0\n",
4133                         ap->resName, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->charge, ap->weight);
4134         }
4135         fprintf(fp, "\n");
4136         
4137         /*  Bonds  */
4138         fprintf(fp, "%8d !NBOND: bonds\n", mp->nbonds);
4139         for (i = 0; i < mp->nbonds * 2; i++) {
4140                 fprintf(fp, "%8d", mp->bonds[i] + 1);
4141                 if (i % 8 == 7)
4142                         fprintf(fp, "\n");
4143         }
4144         if (i % 8 != 0)
4145                 fprintf(fp, "\n");
4146         fprintf(fp, "\n");
4147         
4148         /*  Angles  */
4149         fprintf(fp, "%8d !NTHETA: angles\n", mp->nangles);
4150         for (i = 0; i < mp->nangles * 3; i++) {
4151                 fprintf(fp, "%8d", mp->angles[i] + 1);
4152                 if (i % 9 == 8)
4153                         fprintf(fp, "\n");
4154         }
4155         if (i % 9 != 0)
4156                 fprintf(fp, "\n");
4157         fprintf(fp, "\n");
4158         
4159         /*  Dihedrals  */
4160         fprintf(fp, "%8d !NPHI: dihedrals\n", mp->ndihedrals);
4161         for (i = 0; i < mp->ndihedrals * 4; i++) {
4162                 fprintf(fp, "%8d", mp->dihedrals[i] + 1);
4163                 if (i % 8 == 7)
4164                         fprintf(fp, "\n");
4165         }
4166         if (i % 8 != 0)
4167                 fprintf(fp, "\n");
4168         fprintf(fp, "\n");
4169         
4170         /*  Dihedrals  */
4171         fprintf(fp, "%8d !NIMPHI: impropers\n", mp->nimpropers);
4172         for (i = 0; i < mp->nimpropers * 4; i++) {
4173                 fprintf(fp, "%8d", mp->impropers[i] + 1);
4174                 if (i % 8 == 7)
4175                         fprintf(fp, "\n");
4176         }
4177         if (i % 8 != 0)
4178                 fprintf(fp, "\n");
4179         fprintf(fp, "\n");
4180         
4181         fprintf(fp, "%8d !NDON: donors\n\n", 0);
4182         fprintf(fp, "%8d !NACC: acceptors\n\n", 0);
4183         fprintf(fp, "%8d !NNB: non-bonding exclusions\n\n", 0);
4184         for (i = 0; i < mp->natoms; i++) {
4185                 fprintf(fp, "%8d", 0);
4186                 if (i % 8 == 7)
4187                         fprintf(fp, "\n");
4188         }
4189         if (i % 8 != 0)
4190                 fprintf(fp, "\n");
4191         fprintf(fp, "\n");
4192         fprintf(fp, "%8d !NGRP: groups\n", 1);
4193         fprintf(fp, "       0       0       0\n");
4194         fprintf(fp, "\n");
4195         
4196         i = strlen(fname);
4197         if (i > 5 && strcmp(fname + i - 5, ".psfx") == 0) {
4198                 /*  Extended psf (with coordinates and other info)  */
4199                 fprintf(fp, "%8d !COORD: coordinates\n", mp->natoms);
4200                 for (i = 0; i < mp->natoms; i++) {
4201                         Vector r;
4202                         ap = ATOM_AT_INDEX(mp->atoms, i);
4203                         r = ap->r;
4204                         fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->aname);
4205                 }
4206                 fprintf(fp, "\n");
4207 #if 0
4208                 if (mp->nframes > 0) {
4209                         int fn;  /*  Frame number  */
4210                         for (fn = 0; fn < ap->nframes; fn++) {
4211                                 fprintf(fp, "%8d !COORD: coordinates for frame %d\n", mp->natoms, fn);
4212                                 for (i = 0; i < mp->natoms; i++) {
4213                                         Vector r;
4214                                         ap = ATOM_AT_INDEX(mp->atoms, i);
4215                                         if (ap->frames == NULL || fn >= ap->nframes)
4216                                                 r = ap->r;
4217                                         else
4218                                                 r = ap->frames[fn];
4219                                         fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->name);
4220                                 }
4221                                 fprintf(fp, "\n");
4222                         }
4223                 }
4224 #endif
4225         }
4226                 
4227         fclose(fp);
4228         return 0;
4229 }
4230
4231 int
4232 MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char **errbuf)
4233 {
4234         FILE *fp;
4235         int i, j;
4236         Atom *ap;
4237         *errbuf = NULL;
4238         fp = fopen(fname, "wb");
4239         if (fp == NULL) {
4240                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4241                 return 1;
4242         }
4243         for (i = 0; i < mp->natoms; i++) {
4244                 char buf[6];
4245                 ap = ATOM_AT_INDEX(mp->atoms, i);
4246                 if (ap->resSeq >= 10000) {
4247                         snprintf(buf, sizeof buf, "%c%03d", 'A' + (ap->resSeq - 10000) / 1000, ap->resSeq % 1000);
4248                 } else {
4249                         snprintf(buf, sizeof buf, "%4d", ap->resSeq);
4250                 }
4251                 fprintf(fp, "ATOM  %5d %-4.4s%1.1s%-3.3s %1.1s%4.4s%1.1s   "
4252                                         "%8.3f%8.3f%8.3f %5.2f %5.2f      "
4253                                         "%-4.4s%-2.2s%-2d\n",
4254                         i + 1, ap->aname, " ", ap->resName, " ", buf, " ",
4255                         ap->r.x, ap->r.y, ap->r.z, ap->occupancy, ap->tempFactor,
4256                         ap->segName, ap->element, ap->intCharge);
4257         }
4258         for (i = 0; i < mp->natoms; i++) {
4259                 Int *cp;
4260                 ap = ATOM_AT_INDEX(mp->atoms, i);
4261                 cp = AtomConnectData(&ap->connect);
4262                 for (j = 0; j < ap->connect.count; j++) {
4263                         if (j % 4 == 0) {
4264                                 if (j > 0)
4265                                         fprintf(fp, "\n");
4266                                 fprintf(fp, "CONECT%5d", i + 1);
4267                         }
4268                         fprintf(fp, "%5d", cp[j] + 1);
4269                 }
4270                 if (j > 0)
4271                         fprintf(fp, "\n");
4272         }
4273         fprintf(fp, "END\n");
4274         fclose(fp);
4275         return 0;
4276 }
4277
4278 int
4279 MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char **errbuf)
4280 {
4281         DcdRecord dcd;
4282         SFloat32 *xp, *yp, *zp;
4283         int n;
4284         *errbuf = NULL;
4285         if (mp == NULL || mp->natoms == 0) {
4286                 s_append_asprintf(errbuf, "Molecule is empty");
4287                 return 1;
4288         }
4289         memset(&dcd, 0, sizeof(dcd));
4290         dcd.natoms = mp->natoms;
4291         dcd.nframes = MoleculeGetNumberOfFrames(mp);
4292         if (dcd.nframes == 0) {
4293                 s_append_asprintf(errbuf, "no frame is present");
4294                 return 1;
4295         }
4296         dcd.nstart = mp->startStep;
4297         dcd.ninterval = mp->stepsPerFrame;
4298         if (dcd.ninterval == 0)
4299                 dcd.ninterval = 1;
4300         dcd.nend = dcd.nstart + (dcd.nframes - 1) * dcd.ninterval;
4301         if (mp->cell != NULL)
4302                 dcd.nextra = 1;
4303         dcd.delta = mp->psPerStep;
4304         if (dcd.delta == 0.0)
4305                 dcd.delta = 1.0;
4306         dcd.ncharmver = 24;
4307         n = DcdCreate(fname, &dcd);
4308         if (n != 0) {
4309                 if (n < 0)
4310                         s_append_asprintf(errbuf, "Cannot create dcd file");
4311                 else
4312                         s_append_asprintf(errbuf, "Cannot write dcd header");
4313                 DcdClose(&dcd);
4314                 return 1;
4315         }
4316         
4317         xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4318         yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4319         zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4320         if (xp == NULL || yp == NULL || zp == NULL) {
4321                 s_append_asprintf(errbuf, "Cannot allocate memory");
4322                 if (xp) free(xp);
4323                 if (yp) free(yp);
4324                 if (zp) free(zp);
4325                 DcdClose(&dcd);
4326                 return 1;
4327         }
4328         for (n = 0; n < dcd.nframes; n++) {
4329                 int i;
4330                 Atom *ap;
4331                 for (i = 0, ap = mp->atoms; i < dcd.natoms && mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4332                         Vector r;
4333                         if (ap->frames == NULL || n >= ap->nframes)
4334                                 r = ap->r;
4335                         else
4336                                 r = ap->frames[n];
4337                         xp[i] = r.x;
4338                         yp[i] = r.y;
4339                         zp[i] = r.z;
4340                 }
4341                 if (i < dcd.natoms) {
4342                         size_t sz = (dcd.natoms - i) * sizeof(SFloat32);
4343                         memset(xp + i, 0, sz);
4344                         memset(yp + i, 0, sz);
4345                         memset(zp + i, 0, sz);
4346                 }
4347                 if (n < mp->nframe_cells && mp->frame_cells != NULL) {
4348                         Vector *cp = &(mp->frame_cells[n * 4]);
4349                         dcd.globalcell[0] = VecLength(cp[0]);
4350                         dcd.globalcell[2] = VecLength(cp[1]);
4351                         dcd.globalcell[5] = VecLength(cp[2]);
4352                         dcd.globalcell[1] = VecDot(cp[0], cp[1]) / (dcd.globalcell[0] * dcd.globalcell[2]);
4353                         dcd.globalcell[3] = VecDot(cp[0], cp[2]) / (dcd.globalcell[0] * dcd.globalcell[5]);
4354                         dcd.globalcell[4] = VecDot(cp[1], cp[2]) / (dcd.globalcell[2] * dcd.globalcell[5]);                     
4355                 }                       
4356                 if (DcdWriteFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
4357                         s_append_asprintf(errbuf, "Write error in dcd file");
4358                         goto exit;
4359                 }
4360         }
4361         
4362 exit:
4363         DcdClose(&dcd);
4364         free(xp);
4365         free(yp);
4366         free(zp);
4367         if (errbuf[0] == 0)
4368                 return 0;
4369         else return 1;
4370 }
4371
4372 int
4373 MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
4374 {
4375         FILE *fp;
4376         int i;
4377         Vector v;
4378         *errbuf = NULL;
4379         fp = fopen(fname, "wb");
4380         if (fp == NULL) {
4381                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4382                 return 1;
4383         }
4384         if (mp->cell != NULL) {
4385                 fprintf(fp, "Bounding box:\n");
4386                 for (i = 0; i < 3; i++) {
4387                         v = mp->cell->axes[i];
4388                         fprintf(fp, "%.3f %.3f %.3f %d\n", v.x, v.y, v.z, mp->cell->flags[i]);
4389                 }
4390                 fprintf(fp, "Bounding box origin:\n");
4391                 v = mp->cell->origin;
4392                 fprintf(fp, "%.3f %.3f %.3f\n", v.x, v.y, v.z);
4393         }
4394         fclose(fp);
4395         return 0;
4396 }
4397                 
4398  static int
4399 sCompareByElement(const void *ap, const void *bp)
4400 {
4401         return ((*(Atom **)bp)->atomicNumber - (*(Atom **)ap)->atomicNumber);
4402 }
4403
4404 static int
4405 sMakeAdc(int n, int base, Symop symop)
4406 {
4407         int an, sym;
4408         if (SYMOP_ALIVE(symop)) {
4409                 an = base;
4410                 sym = (symop.dx + 5) * 10000 + (symop.dy + 5) * 1000 + (symop.dz + 5) * 100 + symop.sym + 1;
4411         } else {
4412                 an = n;
4413                 sym = 55501;
4414         }
4415         return (an + 1) * 100000 + sym;
4416 }
4417
4418 static int
4419 sCompareAdc(const void *ap, const void *bp)
4420 {
4421         int n = *((Int *)ap) % 100000 - *((Int *)bp) % 100000;
4422         if (n == 0)
4423                 n = *((Int *)ap) / 100000 - *((Int *)bp) / 100000;
4424         return n;
4425 }
4426
4427 static void
4428 sOutputAtomListInstructions(FILE *fp, int natoms, Atom *atoms)
4429 {
4430         int i, j, k, an, sym;
4431         Atom *ap;
4432         Int *adc;
4433         adc = (Int *)malloc(sizeof(Int) * natoms);
4434         if (adc == NULL)
4435                 return;
4436         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4437                 if (ap->exflags & kAtomHiddenFlag)
4438                         continue;
4439                 adc[i] = sMakeAdc(i, ap->symbase, ap->symop);
4440         }
4441         mergesort(adc, natoms, sizeof(Int), sCompareAdc);
4442         
4443         /*  Create the atom list  */
4444         an = sym = -1;
4445         for (i = j = k = 0; i < natoms; i++) {
4446                 int an1 = adc[i] / 100000;
4447                 int sym1 = adc[i] % 100000;
4448                 if (sym == sym1 && an1 == an + 1) {
4449                         /*  Continuous  */
4450                         an = an1;
4451                         k++;
4452                         continue;
4453                 }
4454                 if (k > 0)
4455                         /*  Output the last atom with a minus sign  */
4456                         adc[j++] = -(an * 100000 + sym);
4457                 /*  Output this atom  */
4458                 adc[j++] = adc[i];
4459                 an = an1;
4460                 sym = sym1;
4461                 k = 0;
4462         }
4463         if (k > 0)
4464                 adc[j++] = -(an * 100000 + sym);
4465         
4466         /*  Create the instruction cards  */
4467         for (i = k = 0; i < j; i++) {
4468                 if (k == 0)
4469                         fprintf(fp, "      401");
4470                 fprintf(fp, "%9d", adc[i]);
4471                 k++;
4472                 if (i == j - 1 || k == 6 || (k == 5 && i < j - 2 && adc[i + 2] < 0)) {
4473                         fprintf(fp, "\n");
4474                         k = 0;
4475                 }
4476         }
4477         free(adc);
4478 }
4479
4480 static int
4481 sEllipsoidType(int an)
4482 {
4483         return (an >= 18 ? 3 : (an >= 2 && an != 6 ? 2 : (an > 0 ? 1 : 0)));
4484 }
4485
4486 static void
4487 sOutputAtomTypeInstructions(FILE *fp, int natoms, Atom *atoms)
4488 {
4489         int i;
4490         Atom *ap;
4491         int etype, elast, istart, ilast, n1, n2;
4492         elast = istart = ilast = -1;
4493         for (i = 0, ap = atoms; i <= natoms; i++, ap++) {
4494                 if (i < natoms) {
4495                         if (SYMOP_ALIVE(ap->symop))
4496                                 continue;
4497                         if (ap->exflags & kAtomHiddenFlag)
4498                                 continue;
4499                         etype = sEllipsoidType(ap->atomicNumber);
4500                         if (elast < 0) {
4501                                 istart = ilast = i;
4502                                 elast = etype;
4503                                 continue;
4504                         } else if (elast == etype && ilast == i - 1) {
4505                                 ilast++;
4506                                 continue;
4507                         }
4508                 }
4509                 /*  Output the instruction card for the 'last' block of atoms  */
4510                 switch (etype) {
4511                         case 2:
4512                                 n1 = 4; n2 = 0; break;
4513                         case 3:
4514                                 n1 = 4; n2 = 5; break;
4515                         default:
4516                                 n1 = 1; n2 = 0; break;
4517                 }
4518                 fprintf(fp, "  1   715 %8d        0 %8d        0    0.100    0.000    0.000\n", n1, n2);
4519                 fprintf(fp, "                           %9d%9d\n", istart + 1, ilast + 1);
4520                 elast = etype;
4521                 ilast = istart = i;
4522         }
4523 }
4524
4525 static int
4526 sCompareBondType(const void *ap, const void *bp)
4527 {
4528         /*  Descending order  */
4529         return *((int *)bp) - *((int *)ap);
4530 }
4531
4532 static void
4533 sOutputBondInstructions(FILE *fp, int natoms, Atom *atoms, int overlap_correction)
4534 {
4535         Atom *ap, *ap2;
4536         char buf[96];
4537         int i, j, n[5], an, count, n1, n2, k;
4538         Int *cp;
4539         Int nexbonds;
4540         Int *exbonds;
4541         static const float sBondRad[4] = {0.060, 0.060, 0.060, 0.040};
4542         static const int sBondShade[4] = {5, 3, 1, 1};
4543
4544         n[0] = n[1] = n[2] = n[3] = 0;  /*  Start index of 3rd row atoms (and higher), 2nd row, 1st row, and H */
4545         n[4] = natoms;
4546         for (i = natoms - 1, ap = atoms + i; i >= 0; i--, ap--) {
4547                 an = ap->atomicNumber;
4548                 if (an < 2)
4549                         n[3] = i;
4550                 if (an < 10)
4551                         n[2] = i;
4552                 if (an < 18)
4553                         n[1] = i;
4554         }
4555         nexbonds = 0;
4556         exbonds = NULL;
4557         count = 0;
4558
4559         if (overlap_correction)
4560                 strcpy(buf, "  2  1001    0.000\n");
4561         else
4562                 strcpy(buf, "  2   812\n");
4563         
4564         for (i = 0; i < 4; i++) {
4565                 for (j = i; j < 4; j++) {
4566                         /*  Examine bonds between "group i" and "group j"  */
4567                         Vector dr;
4568                         double d;
4569                         double min_bond = 10000.0;     /*  Minimum distance between bound atoms  */
4570                         double min_nonbond = 10000.0;  /*  Minimum distance between non-bound atoms  */
4571                         double max_bond = -10000.0;    /*  Maximum distance between bound atoms  */
4572                         int count_exbond = 0;          /*  Number of explicit bonds in this group  */
4573                         for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
4574                                 for (n2 = n[j], ap2 = atoms + n2; n2 < n[j + 1]; n2++, ap2++) {
4575                                         if (n1 == n2)
4576                                                 continue;
4577                                         VecSub(dr, ap->r, ap2->r);
4578                                         d = VecLength(dr);
4579                                         cp = AtomConnectData(&ap->connect);
4580                                         for (k = ap->connect.count - 1; k >= 0; k--) {
4581                                                 if (cp[k] == n2)
4582                                                         break;
4583                                         }
4584                                         if (k >= 0) {
4585                                                 /*  n1 and n2 are bound  */
4586                                                 if (d < min_bond)
4587                                                         min_bond = d;
4588                                                 if (d > max_bond)
4589                                                         max_bond = d;
4590                                         } else {
4591                                                 /*  n1 and n2 are not bound  */
4592                                                 if (d < min_nonbond)
4593                                                         min_nonbond = d;
4594                                         }
4595                                 }
4596                         }
4597                         if (min_bond == 10000.0)
4598                                 continue;  /*  No bonds between these groups  */
4599                         min_bond *= 0.9;
4600                         if (max_bond + 0.002 < min_nonbond)
4601                                 max_bond += 0.002;
4602                         else {
4603                                 max_bond = min_nonbond - 0.002;
4604                                 /*  Some bonds may be omitted, so scan all bonds again  */
4605                                 for (n1 = n[i], ap = ATOM_AT_INDEX(atoms, n1); n1 < n[i + 1]; n1++, ap = ATOM_NEXT(ap)) {
4606                                         cp = AtomConnectData(&ap->connect);
4607                                         for (k = ap->connect.count - 1; k >= 0; k--) {
4608                                                 n2 = cp[k];
4609                                                 if (n2 < n[j] || n2 >= n[j + 1])
4610                                                         continue;
4611                                                 ap2 = atoms + n2;
4612                                                 VecSub(dr, ap->r, ap2->r);
4613                                                 d = VecLength(dr);
4614                                                 if (d > max_bond) {
4615                                                         /*  This bond should be explicitly defined  */
4616                                                         Int adc1, adc2;
4617                                                         if (count_exbond == 0) {
4618                                                                 adc1 = -(i + 1);  /*  Bond type  */
4619                                                                 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4620                                                         }
4621                                                         adc1 = sMakeAdc(n1, ap->symbase, ap->symop);
4622                                                         adc2 = sMakeAdc(n2, ap2->symbase, ap2->symop);
4623                                                         AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4624                                                         AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc2);
4625                                                         count_exbond++;
4626                                                 }
4627                                         }
4628                                 }
4629                         }
4630                         /*  Output the last instruction card  */
4631                         fputs(buf, fp);
4632                         /*  Make a new trailer card  */
4633                         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]);
4634                         count++;
4635                 }
4636         }
4637         if (count > 0) {
4638                 /*  Output the last trailer card  */
4639                 buf[2] = ' ';
4640                 fputs(buf, fp);
4641         }
4642         if (nexbonds > 0) {
4643                 if (count == 0 && overlap_correction) {
4644                         /*  1001 card is not yet written, so write it  */
4645                         buf[2] = ' ';
4646                         fputs(buf, fp);
4647                 }
4648                 snprintf(buf, sizeof(buf), "  1   %3d", (overlap_correction ? 821 : 811));
4649                 k = -exbonds[0] - 1;  /*  Bond type for the first block  */
4650                 i = 1;  /*  Index for exbonds[]  */
4651                 j = 0;  /*  Count in this block  */
4652                 while (i <= nexbonds) {
4653                         if (j >= 29 || i == nexbonds || exbonds[i] < 0) {
4654                                 /*  End of block  */
4655                                 buf[2] = '2';
4656                                 fputs(buf, fp);
4657                                 /*  The trailer card  */
4658                                 fprintf(fp, "                     %3d            %6.3f\n", sBondShade[k], sBondRad[k]);
4659                                 if (i == nexbonds)
4660                                         break;
4661                                 if (exbonds[i] < 0)
4662                                         k = -exbonds[i++] - 1;  /*  The new bond type  */
4663                                 j = 0;
4664                         } else if (j > 0 && j % 3 == 0) {
4665                                 buf[2] = '1';
4666                                 fputs(buf, fp);
4667                         }
4668                         n1 = exbonds[i++];
4669                         n2 = exbonds[i++];
4670                         snprintf(buf + 9 + (j % 3) * 18, sizeof(buf) - 9 - (j % 3) * 18, "%9d%9d\n", n1, n2);
4671                         j++;
4672                 }
4673                 free(exbonds);
4674         }
4675 }
4676         
4677 #if 0
4678 {
4679         /*  Explicit bond table, sorted by bond type  */
4680         for (i = j = 0; i < mp->nbonds; i++) {
4681                 n1 = mp->bonds[i * 2];
4682                 n2 = mp->bonds[i * 2 + 1];
4683                 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
4684                 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
4685                 if ((ap1->exflags & kAtomHiddenFlag) || (ap2->exflags & kAtomHiddenFlag))
4686                         continue;
4687                 if (ap1->atomicNumber > 18 || ap2->atomicNumber > 18) {
4688                         type = 3;
4689                 } else if (ap1->atomicNumber > 1 && ap1->atomicNumber > 1) {
4690                         type = 2;
4691                 } else {
4692                         type = 1;
4693                 }
4694                 ip[j * 3] = type;
4695                 ip[j * 3 + 1] = sMakeAdc(n1, ap1->symbase, ap1->symop);
4696                 ip[j * 3 + 2] = sMakeAdc(n2, ap2->symbase, ap2->symop);
4697                 j++;
4698         }
4699         mergesort(ip, j, sizeof(int) * 3, sCompareBondType);
4700         
4701         /*  Output instruction cards  */
4702         strcpy(buf, "  1   811");
4703         for (i = n1 = 0; i < j; i++) {
4704                 n2 = (n1 % 3) * 18 + 9;
4705                 snprintf(buf + n2, 80 - n2, "%9d%9d\n", ip[i * 3 + 1], ip[i * 3 + 2]);
4706                 if (i == j - 1 || n1 >= 29 || ip[i * 3] != ip[i * 3 + 3]) {
4707                         /*  End of this instruction  */
4708                         buf[2] = '2';
4709                         fputs(buf, fp);
4710                         switch (ip[i * 3]) {
4711                                 case 3: rad = 0.06; nshades = 5; break;
4712                                 case 2: rad = 0.06; nshades = 1; break;
4713                                 default: rad = 0.04; nshades = 1; break;
4714                         }
4715                         fprintf(fp, "                     %3d            %6.3f\n", nshades, rad);
4716                         strcpy(buf, "  1   811");
4717                         n1 = 0;
4718                         continue;
4719                 } else if (n1 % 3 == 2) {
4720                         fputs(buf, fp);
4721                         strcpy(buf, "  1      ");
4722                 }
4723                 n1++;
4724         }
4725         free(ip);
4726 }
4727 #endif
4728
4729 int
4730 MoleculeWriteToTepFile(Molecule *mp, const char *fname, char **errbuf)
4731 {
4732         FILE *fp;
4733         int i, j, natoms, *ip;
4734         Int *cp;
4735         Atom *ap, *atoms, **app;
4736         Double *dp;
4737         static Double sUnit[] = {1, 1, 1, 90, 90, 90};
4738         
4739         *errbuf = NULL;
4740
4741         /*  Create sorted array of atoms  */
4742         natoms = mp->natoms;
4743         atoms = (Atom *)calloc(sizeof(Atom), natoms);
4744         app = (Atom **)calloc(sizeof(Atom *), natoms);
4745         ip = (int *)calloc(sizeof(int), natoms);
4746         if (atoms == NULL || app == NULL || ip == NULL) {
4747                 s_append_asprintf(errbuf, "Cannot allocate memory");
4748                 return 1;
4749         }
4750         /*  Sort the atom pointer by atomic number  */
4751         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
4752                 app[i] = ap;
4753         mergesort(app, natoms, sizeof(Atom *), sCompareByElement);
4754         for (i = 0; i < natoms; i++) {
4755                 /*  ip[old_index] is new_index  */
4756                 ip[app[i] - mp->atoms] = i;
4757         }
4758         /*  Copy the atom record to atoms[]  */
4759         /*  The 'v' member contains crystallographic coordinates  */
4760         /*  The connection table and symbase are renumbered  */
4761         /*  Hidden flags are modified to reflect the visibility in the MainView  */
4762         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4763                 AtomDuplicateNoFrame(ap, app[i]);
4764         /*      memmove(ap, app[i], gSizeOfAtomRecord); */
4765                 MoleculeCartesianToXtal(mp, &(ap->v), &(ap->r));
4766                 cp = AtomConnectData(&ap->connect);
4767                 for (j = ap->connect.count - 1; j >= 0; j--) {
4768                         cp[j] = ip[cp[j]];
4769                 }
4770                 if (SYMOP_ALIVE(ap->symop))
4771                         ap->symbase = ip[ap->symbase];
4772                 if (MainView_isAtomHidden(mp->mview, i)) {
4773                         ap->exflags |= kAtomHiddenFlag;
4774                 } else {
4775                         ap->exflags &= ~kAtomHiddenFlag;
4776                 }
4777         }
4778         free(ip);
4779         free(app);
4780         
4781         fp = fopen(fname, "wb");
4782         if (fp == NULL) {
4783                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4784                 return 1;
4785         }
4786
4787         /*  Title line  */
4788         fprintf(fp, "Generated by Molby\n");
4789         
4790         /*  XtalCell  */
4791         if (mp->cell != NULL) {
4792                 dp = mp->cell->cell;
4793         } else {
4794                 dp = sUnit;
4795         }
4796         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]);
4797         
4798         /*  Symmetry operations  */
4799         if (mp->nsyms > 0) {
4800                 for (i = 0; i < mp->nsyms; i++) {
4801                         dp = mp->syms[i];
4802                         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]);
4803                 }
4804         } else {
4805                 fprintf(fp, "1             0  1  0  0              0  0  1  0              0  0  0  1\n");
4806         }
4807         
4808         /*  Atoms  */
4809         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4810                 /*  The 'v' field contains crystallographic coordinates  */
4811                 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);
4812                 if (ap->aniso != NULL) {
4813                         dp = ap->aniso->bij;
4814                         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);
4815                 } else {
4816                         Double temp = ap->tempFactor;
4817                         if (temp <= 0)
4818                                 temp = 1.2;
4819                         fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", temp, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4820                 }
4821         }
4822         /*  Special points  */
4823         {
4824                 Vector camera, lookat, up, xvec, yvec, zvec;
4825                 MainView_getCamera(mp->mview, &camera, &lookat, &up);
4826                 VecSub(zvec, lookat, camera);
4827                 VecCross(xvec, zvec, up);
4828                 NormalizeVec(&xvec, &xvec);
4829                 NormalizeVec(&yvec, &up);
4830                 VecInc(xvec, lookat);
4831                 VecInc(yvec, lookat);
4832                 MoleculeCartesianToXtal(mp, &lookat, &lookat);
4833                 MoleculeCartesianToXtal(mp, &xvec, &xvec);
4834                 MoleculeCartesianToXtal(mp, &yvec, &yvec);
4835                 fprintf(fp, " ORGN                      %9g%9g%9g        0\n", 0.0, 0.0, 0.0);
4836                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4837                 fprintf(fp, " CNTR                      %9g%9g%9g        0\n", lookat.x, lookat.y, lookat.z);
4838                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4839                 fprintf(fp, " X                         %9g%9g%9g        0\n", xvec.x, xvec.y, xvec.z);
4840                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4841                 fprintf(fp, " Y                         %9g%9g%9g        0\n", yvec.x, yvec.y, yvec.z);
4842                 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);
4843         }
4844         
4845         /*  Instructions  */
4846         fprintf(fp, "      201\n");
4847         fprintf(fp, "      205       12\n");
4848         fprintf(fp, "      301      6.6      6.6        0      0.8\n");
4849         sOutputAtomListInstructions(fp, natoms, atoms);
4850         fprintf(fp, "      501%4d55501%4d55501%4d55501%4d55501%4d55501                 1\n", natoms + 2, natoms + 2, natoms + 3, natoms + 2, natoms + 4);
4851         fprintf(fp, "      502        1      0.0        2      0.0        3      0.0\n");
4852         fprintf(fp, "      604                               1.538\n");
4853
4854         sOutputBondInstructions(fp, natoms, atoms, 1);
4855         sOutputAtomTypeInstructions(fp, natoms, atoms);
4856         sOutputBondInstructions(fp, natoms, atoms, 0);
4857
4858         for (i = 0; i < natoms; i++) {
4859                 AtomClean(atoms + i);
4860         }
4861         free(atoms);
4862
4863         fprintf(fp, "      202\n");
4864         fprintf(fp, "  0    -1\n");
4865         fclose(fp);
4866         return 0;
4867 }
4868
4869 void
4870 MoleculeDump(Molecule *mol)
4871 {
4872         int i, j;
4873         Int *cp;
4874         Atom *ap;
4875         for (i = 0; i < mol->natoms; i++) {
4876                 char buf1[8];
4877                 ap = ATOM_AT_INDEX(mol->atoms, i);
4878                 snprintf(buf1, sizeof buf1, "%3.4s.%d", ap->resName, ap->resSeq);
4879                 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);
4880                 cp = AtomConnectData(&ap->connect);
4881                 for (j = 0; j < ap->connect.count; j++) {
4882                         fprintf(stderr, "%s%d", (j > 0 ? "," : ""), cp[j]);
4883                 }
4884                 fprintf(stderr, "]\n");
4885         }
4886 }
4887
4888 #pragma mark ====== MD support (including modification of Molecule) ======
4889
4890 /*  Call md_prepare for the MDArena. If MDArena has not been created, a new arena is created.
4891         If something goes wrong, returns 1 (for missing parameters) or -1 (more serious error).
4892     If retmsg is not NULL, a message describing the problem is returned there. This message
4893     must be free'd by the caller.  */
4894 int
4895 MoleculePrepareMDArena(Molecule *mol, int check_only, char **retmsg)
4896 {
4897         const char *msg;
4898         Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
4899         Int missing = 0;
4900         IntGroup *ig1, *ig2, *ig3;
4901         MDArena *arena = mol->arena;
4902
4903         if (arena == NULL) {
4904                 md_arena_new(mol);
4905                 arena = mol->arena;
4906         } else if (arena->xmol != mol)
4907                 md_arena_set_molecule(arena, mol);
4908
4909         arena->is_initialized = 0;
4910         
4911         /*  Rebuild the tables  */
4912         ig1 = ig2 = ig3 = NULL;
4913         nangles = MoleculeFindMissingAngles(mol, &angles);
4914         ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
4915         nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
4916         if (nangles > 0) {
4917                 ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
4918                 MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
4919                 free(angles);
4920                 IntGroupRelease(ig1);
4921         }
4922         if (ndihedrals > 0) {
4923                 ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
4924                 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
4925                 free(dihedrals);
4926                 IntGroupRelease(ig2);
4927         }
4928         if (nimpropers > 0) {
4929                 ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
4930                 MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
4931                 free(impropers);
4932                 IntGroupRelease(ig3);
4933         }
4934         
4935         {
4936                 /*  Update the path information of the molecule before MD setup  */
4937                 char *buf = (char *)malloc(4096);
4938                 MoleculeCallback_pathName(mol, buf, sizeof buf);
4939                 MoleculeSetPath(mol, buf);
4940                 free(buf);
4941         }
4942                 
4943         /*  Prepare parameters and internal information  */
4944         msg = md_prepare(arena, check_only);
4945         
4946         /*  Some parameters are missing?  */
4947         if (msg != NULL) {
4948                 if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
4949                         missing = 1;
4950                 else {
4951                         if (retmsg != NULL)
4952                                 asprintf(retmsg, "cannot initialize for MD: %s", msg);
4953                         return -1;
4954                 }
4955         }
4956         
4957         /*  The local parameter list is updated  */
4958         {
4959                 Int parType, idx;
4960                 if (mol->par == NULL)
4961                         mol->par = ParameterNew();
4962                 for (parType = kFirstParType; parType <= kLastParType; parType++) {
4963                         /*  Delete global and undefined parameters  */
4964                         UnionPar *up, *upbuf;
4965                         Int nparams, count;
4966                         ig1 = IntGroupNew();
4967                         for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
4968                                 if (up->bond.src != 0)
4969                                         IntGroupAdd(ig1, idx, 1);
4970                         }
4971                         if (IntGroupGetCount(ig1) > 0)
4972                                 MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
4973                         IntGroupRelease(ig1);
4974                         /*  Copy global and undefined parameters from arena and insert to mol->par  */
4975                         nparams = ParameterGetCountForType(arena->par, parType);
4976                         if (nparams == 0)
4977                                 continue;
4978                         upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
4979                         ig1 = IntGroupNew();
4980                         ig2 = IntGroupNew();
4981                         for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
4982                                 if (up->bond.src > 0)
4983                                         IntGroupAdd(ig1, idx, 1); /* Global parameter */
4984                                 else if (up->bond.src < 0)
4985                                         IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
4986                         }
4987                         if ((count = IntGroupGetCount(ig1)) > 0) {
4988                                 /*  Insert global parameters (at the top)  */
4989                                 ParameterCopy(arena->par, parType, upbuf, ig1);
4990                                 ig3 = IntGroupNewWithPoints(0, count, -1);
4991                                 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
4992                                 IntGroupRelease(ig3);
4993                         }
4994                         if ((count = IntGroupGetCount(ig2)) > 0) {
4995                                 /*  Insert undefined parameters (at the bottom)  */
4996                                 ParameterCopy(arena->par, parType, upbuf, ig2);
4997                                 idx = ParameterGetCountForType(mol->par, parType);
4998                                 ig3 = IntGroupNewWithPoints(idx, count, -1);
4999                                 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5000                                 IntGroupRelease(ig3);
5001                         }
5002                         IntGroupRelease(ig2);
5003                         IntGroupRelease(ig1);
5004                         free(upbuf);
5005                 }
5006                 mol->needsMDRebuild = 0;  /*  We know the "modified" parameters are consistent with the MDArena  */
5007         }
5008         
5009         if (missing) {
5010                 if (retmsg != NULL)
5011                         *retmsg = strdup(msg);
5012                 return 1;
5013         } else return 0;
5014 }
5015
5016 #pragma mark ====== Serialize ======
5017
5018 Molecule *
5019 MoleculeDeserialize(const char *data, Int length, Int *timep)
5020 {
5021         Molecule *mp;
5022         Parameter *par;
5023         Atom *ap;
5024 /*      int result; */
5025
5026         mp = MoleculeNew();
5027         if (mp == NULL)
5028                 goto out_of_memory;
5029         par = ParameterNew();
5030         if (par == NULL)
5031                 goto out_of_memory;
5032
5033         while (length >= 12) {
5034                 const char *ptr = data + 8 + sizeof(Int);
5035                 int len = *((const Int *)(data + 8));
5036                 int i, j, n;
5037                 if (strcmp(data, "ATOM") == 0) {
5038                         n = len / gSizeOfAtomRecord;
5039                         NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, n);
5040                         memmove(mp->atoms, ptr, len);
5041                 } else if (strcmp(data, "ANISO") == 0) {
5042                         n = len / (sizeof(Int) + sizeof(Aniso));
5043                         for (i = 0; i < n; i++) {
5044                                 j = *((const Int *)ptr);
5045                                 if (j < 0 || j >= mp->natoms)
5046                                         goto bad_format;
5047                                 ap = ATOM_AT_INDEX(mp->atoms, j);
5048                                 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
5049                                 if (ap->aniso == NULL)
5050                                         goto out_of_memory;
5051                                 *(ap->aniso) = *((Aniso *)(ptr + sizeof(Int)));
5052                                 ptr += sizeof(Int) + sizeof(Aniso);
5053                         }
5054                 } else if (strcmp(data, "FRAME") == 0) {
5055                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5056                                 if (ap->nframes == 0)
5057                                         continue;
5058                                 ap->frames = (Vector *)malloc(sizeof(Vector) * ap->nframes);
5059                                 if (ap->frames == NULL)
5060                                         goto out_of_memory;
5061                                 memmove(ap->frames, ptr, sizeof(Vector) * ap->nframes);
5062                                 ptr += sizeof(Vector) * ap->nframes;
5063                         }
5064                 } else if (strcmp(data, "EXTCON") == 0) {
5065                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5066                                 if (ap->connect.count <= ATOM_CONNECT_LIMIT)
5067                                         continue;
5068                                 n = ap->connect.count;
5069                                 ap->connect.count = 0;
5070                                 ap->connect.u.ptr = NULL;
5071                                 NewArray(&(ap->connect.u.ptr), &(ap->connect.count), sizeof(Int), n);
5072                                 memmove(ap->connect.u.ptr, ptr, sizeof(Int) * n);
5073                                 ptr += sizeof(Int) * n;
5074                         }
5075                 } else if (strcmp(data, "BOND") == 0) {
5076                         n = len / (sizeof(Int) * 2);
5077                         NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, n);
5078                         memmove(mp->bonds, ptr, len);
5079                 } else if (strcmp(data, "ANGLE") == 0) {
5080                         n = len / (sizeof(Int) * 3);
5081                         NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, n);
5082                         memmove(mp->angles, ptr, len);
5083                 } else if (strcmp(data, "DIHED") == 0) {
5084                         n = len / (sizeof(Int) * 4);
5085                         NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, n);
5086                         memmove(mp->dihedrals, ptr, len);
5087                 } else if (strcmp(data, "IMPROP") == 0) {
5088                         n = len / (sizeof(Int) * 4);
5089                         NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, n);
5090                         memmove(mp->impropers, ptr, len);
5091                 } else if (strcmp(data, "RESIDUE") == 0) {
5092                         n = len / 4;
5093                         NewArray(&mp->residues, &mp->nresidues, 4, n);
5094                         memmove(mp->residues, ptr, len);
5095                 } else if (strcmp(data, "CELL") == 0) {
5096                         mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
5097                         if (mp->cell == NULL)
5098                                 goto out_of_memory;
5099                         memmove(mp->cell, ptr, sizeof(XtalCell));
5100                 } else if (strcmp(data, "SYMOP") == 0) {
5101                         n = len / sizeof(Transform);
5102                         NewArray(&mp->syms, &mp->nsyms, sizeof(Transform), n);
5103                         memmove(mp->syms, ptr, len);
5104                 } else if (strcmp(data, "ANCHOR") == 0) {
5105                         const char *ptr2 = ptr + len;
5106                         while (ptr < ptr2) {
5107                                 PiAnchor an;
5108                                 memset(&an, 0, sizeof(an));
5109                                 i = *((Int *)ptr);
5110                                 if (i >= 0 && i < mp->natoms) {
5111                                         n = *((Int *)(ptr + sizeof(Int)));
5112                                         AtomConnectResize(&(an.connect), n);
5113                                         memmove(AtomConnectData(&(an.connect)), ptr + sizeof(Int) * 2, sizeof(Int) * n);
5114                                         NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), n);
5115                                         memmove(an.coeffs, ptr + sizeof(Int) * (2 + n), sizeof(Double) * n);
5116                                         ap = ATOM_AT_INDEX(mp->atoms, i);
5117                                         ap->anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
5118                                         memmove(ap->anchor, &an, sizeof(PiAnchor));
5119                                 }
5120                                 ptr += sizeof(Int) * (2 + n) + sizeof(Double) * n;
5121                         }
5122                 } else if (strcmp(data, "TIME") == 0) {
5123                         if (timep != NULL)
5124                                 *timep = *((Int *)ptr);
5125                 } else if (strcmp(data, "BONDPAR") == 0) {
5126                         mp->par = par;
5127                         n = len / sizeof(BondPar);
5128                         NewArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), n);
5129                         memmove(par->bondPars, ptr, len);
5130                 } else if (strcmp(data, "ANGPAR") == 0) {
5131                         mp->par = par;
5132                         n = len / sizeof(AnglePar);
5133                         NewArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), n);
5134                         memmove(par->anglePars, ptr, len);
5135                 } else if (strcmp(data, "DIHEPAR") == 0) {
5136                         mp->par = par;
5137                         n = len / sizeof(TorsionPar);
5138                         NewArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), n);
5139                         memmove(par->dihedralPars, ptr, len);
5140                 } else if (strcmp(data, "IMPRPAR") == 0) {
5141                         mp->par = par;
5142                         n = len / sizeof(TorsionPar);
5143                         NewArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), n);
5144                         memmove(par->improperPars, ptr, len);
5145                 } else if (strcmp(data, "VDWPAR") == 0) {
5146                         mp->par = par;
5147                         n = len / sizeof(VdwPar);
5148                         NewArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), n);
5149                         memmove(par->vdwPars, ptr, len);
5150                 } else if (strcmp(data, "VDWPPAR") == 0) {
5151                         mp->par = par;
5152                         n = len / sizeof(VdwPairPar);
5153                         NewArray(&par->vdwpPars, &par->nvdwpPars, sizeof(VdwPairPar), n);
5154                         memmove(par->vdwpPars, ptr, len);
5155                 } else if (strcmp(data, "VCUTPAR") == 0) {
5156                         mp->par = par;
5157                         n = len / sizeof(VdwCutoffPar);
5158                         NewArray(&par->vdwCutoffPars, &par->nvdwCutoffPars, sizeof(VdwCutoffPar), n);
5159                         memmove(par->vdwCutoffPars, ptr, len);
5160                 }
5161                 len += 8 + sizeof(Int);
5162                 data += len;
5163                 length -= len;
5164         }
5165         if (mp->par == NULL)
5166                 ParameterRelease(par);
5167 /*      result = MoleculeRebuildTablesFromConnects(mp);
5168         if (result != 0)
5169                 goto bad_format; */
5170         return mp;
5171         
5172   out_of_memory:
5173         Panic("Low memory while deserializing molecule data");
5174         return NULL; /* Not reached */
5175
5176   bad_format:
5177         Panic("internal error: bad format during deserializing molecule data");
5178         return NULL; /* Not reached */
5179 }
5180
5181 char *
5182 MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep)
5183 {
5184         char *ptr, *p;
5185         int len, len_all, i, naniso, nframes, nconnects, nanchors;
5186         Atom *ap;
5187
5188         /*  Array of atoms  */
5189         len = 8 + sizeof(Int) + gSizeOfAtomRecord * mp->natoms;
5190         ptr = (char *)malloc(len);
5191         if (ptr == NULL)
5192                 goto out_of_memory;
5193         memmove(ptr, "ATOM\0\0\0\0", 8);
5194         *((Int *)(ptr + 8)) = gSizeOfAtomRecord * mp->natoms;
5195         p = ptr + 8 + sizeof(Int);
5196         memmove(p, mp->atoms, gSizeOfAtomRecord * mp->natoms);
5197         naniso = nframes = nconnects = nanchors = 0;
5198         for (i = 0; i < mp->natoms; i++) {
5199                 ap = ATOM_AT_INDEX(p, i);
5200                 if (ap->aniso != NULL) {
5201                         naniso++;
5202                         ap->aniso = NULL;
5203                 }
5204                 if (ap->frames != NULL) {
5205                         nframes += ap->nframes;
5206                         ap->frames = NULL;
5207                 }
5208                 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5209                         nconnects += ap->connect.count;
5210                         ap->connect.u.ptr = NULL;
5211                 }
5212                 if (ap->anchor != NULL) {
5213                         nanchors++;
5214                         ap->anchor = NULL;
5215                 }
5216         }
5217         len_all = len;
5218
5219         /*  Array of aniso  */
5220         if (naniso > 0) {
5221                 len = 8 + sizeof(Int) + (sizeof(Int) + sizeof(Aniso)) * naniso;
5222                 ptr = (char *)realloc(ptr, len_all + len);
5223                 if (ptr == NULL)
5224                         goto out_of_memory;
5225                 p = ptr + len_all;
5226                 memmove(p, "ANISO\0\0\0", 8);
5227                 *((Int *)(p + 8)) = (sizeof(Int) + sizeof(Aniso)) * naniso;
5228                 p += 8 + sizeof(Int);
5229                 for (i = 0; i < mp->natoms; i++) {
5230                         ap = ATOM_AT_INDEX(mp->atoms, i);
5231                         if (ap->aniso != NULL) {
5232                                 *((Int *)p) = i;
5233                                 *((Aniso *)(p + sizeof(Int))) = *(ap->aniso);
5234                                 p += sizeof(Int) + sizeof(Aniso);
5235                         }
5236                 }
5237                 len_all += len;
5238         }
5239         
5240         /*  Array of frames  */
5241         if (nframes > 0) {
5242                 len = 8 + sizeof(Int) + sizeof(Vector) * nframes;
5243                 ptr = (char *)realloc(ptr, len_all + len);
5244                 if (ptr == NULL)
5245                         goto out_of_memory;
5246                 p = ptr + len_all;
5247                 memmove(p, "FRAME\0\0\0", 8);
5248                 *((Int *)(p + 8)) = sizeof(Vector) * nframes;
5249                 p += 8 + sizeof(Int);
5250                 for (i = 0; i < mp->natoms; i++) {
5251                         ap = ATOM_AT_INDEX(mp->atoms, i);
5252                         if (ap->frames != NULL) {
5253                                 memmove(p, ap->frames, sizeof(Vector) * ap->nframes);
5254                                 p += sizeof(Vector) * ap->nframes;
5255                         }
5256                 }
5257                 len_all += len;
5258         }
5259         
5260         /*  Array of connects  */
5261         if (nconnects > 0) {
5262                 len = 8 + sizeof(Int) + sizeof(Int) * nconnects;
5263                 ptr = (char *)realloc(ptr, len_all + len);
5264                 if (ptr == NULL)
5265                         goto out_of_memory;
5266                 p = ptr + len_all;
5267                 memmove(p, "EXTCON\0\0", 8);
5268                 *((Int *)(p + 8)) = sizeof(Int) * nconnects;
5269                 p += 8 + sizeof(Int);
5270                 for (i = 0; i < mp->natoms; i++) {
5271                         ap = ATOM_AT_INDEX(mp->atoms, i);
5272                         if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5273                                 memmove(p, ap->connect.u.ptr, sizeof(Int) * ap->connect.count);
5274                                 p += sizeof(Int) * ap->connect.count;
5275                         }
5276                 }
5277                 len_all += len;
5278         }
5279         
5280         /*  Bonds, angles, dihedrals, impropers  */
5281         if (mp->nbonds > 0) {
5282                 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nbonds;
5283                 ptr = (char *)realloc(ptr, len_all + len);
5284                 if (ptr == NULL)
5285                         goto out_of_memory;
5286                 p = ptr + len_all;
5287                 memmove(p, "BOND\0\0\0\0", 8);
5288                 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nbonds;
5289                 p += 8 + sizeof(Int);
5290                 memmove(p, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
5291                 len_all += len;
5292         }
5293         if (mp->nangles > 0) {
5294                 len = 8 + sizeof(Int) + sizeof(Int) * 3 * mp->nangles;
5295                 ptr = (char *)realloc(ptr, len_all + len);
5296                 if (ptr == NULL)
5297                         goto out_of_memory;
5298                 p = ptr + len_all;
5299                 memmove(p, "ANGLE\0\0\0", 8);
5300                 *((Int *)(p + 8)) = sizeof(Int) * 3 * mp->nangles;
5301                 p += 8 + sizeof(Int);
5302                 memmove(p, mp->angles, sizeof(Int) * 3 * mp->nangles);
5303                 len_all += len;
5304         }
5305         if (mp->ndihedrals > 0) {
5306                 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->ndihedrals;
5307                 ptr = (char *)realloc(ptr, len_all + len);
5308                 if (ptr == NULL)
5309                         goto out_of_memory;
5310                 p = ptr + len_all;
5311                 memmove(p, "DIHED\0\0\0", 8);
5312                 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->ndihedrals;
5313                 p += 8 + sizeof(Int);
5314                 memmove(p, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
5315                 len_all += len;
5316         }
5317         if (mp->nimpropers > 0) {
5318                 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->nimpropers;
5319                 ptr = (char *)realloc(ptr, len_all + len);
5320                 if (ptr == NULL)
5321                         goto out_of_memory;
5322                 p = ptr + len_all;
5323                 memmove(p, "IMPROP\0\0", 8);
5324                 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->nimpropers;
5325                 p += 8 + sizeof(Int);
5326                 memmove(p, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
5327                 len_all += len;
5328         }
5329         
5330         /*  Array of residues  */
5331         if (mp->nresidues > 0) {
5332                 len = 8 + sizeof(Int) + 4 * mp->nresidues;
5333                 ptr = (char *)realloc(ptr, len_all + len);
5334                 if (ptr == NULL)
5335                         goto out_of_memory;
5336                 p = ptr + len_all;
5337                 memmove(p, "RESIDUE\0", 8);
5338                 *((Int *)(p + 8)) = 4 * mp->nresidues;
5339                 p += 8 + sizeof(Int);
5340                 memmove(p, mp->residues, 4 * mp->nresidues);
5341                 len_all += len;
5342         }
5343
5344         /*  Unit cell  */
5345         if (mp->cell != NULL) {
5346                 len = 8 + sizeof(Int) + sizeof(XtalCell);
5347                 ptr = (char *)realloc(ptr, len_all + len);
5348                 if (ptr == NULL)
5349                         goto out_of_memory;
5350                 p = ptr + len_all;
5351                 memmove(p, "CELL\0\0\0\0", 8);
5352                 *((Int *)(p + 8)) = sizeof(XtalCell);
5353                 p += 8 + sizeof(Int);
5354                 memmove(p, mp->cell, sizeof(XtalCell));
5355                 len_all += len;
5356         }
5357         
5358         /*  Symmetry operations  */
5359         if (mp->nsyms > 0) {
5360                 len = 8 + sizeof(Int) + sizeof(Transform) * mp->nsyms;
5361                 ptr = (char *)realloc(ptr, len_all + len);
5362                 if (ptr == NULL)
5363                         goto out_of_memory;
5364                 p = ptr + len_all;
5365                 memmove(p, "SYMOP\0\0\0", 8);
5366                 *((Int *)(p + 8)) = sizeof(Transform) * mp->nsyms;
5367                 p += 8 + sizeof(Int);
5368                 memmove(p, mp->syms, sizeof(Transform) * mp->nsyms);
5369                 len_all += len;
5370         }
5371         
5372         /*  Pi-anchors  */
5373         if (nanchors > 0) {
5374                 /*  Estimate the necessary storage first  */
5375                 /*  One entry consists of { atom_index (Int), number_of_connects (Int), connects (Int's), weights (Double's) }  */
5376                 len = 8 + sizeof(Int);
5377                 for (i = 0; i < mp->natoms; i++) {
5378                         ap = ATOM_AT_INDEX(mp->atoms, i);
5379                         if (ap->anchor != NULL)
5380                                 len += sizeof(Int) * 2 + (sizeof(Int) + sizeof(Double)) * ap->anchor->connect.count;
5381                 }
5382                 ptr = (char *)realloc(ptr, len_all + len);
5383                 if (ptr == NULL)
5384                         goto out_of_memory;
5385                 p = ptr + len_all;
5386                 memmove(p, "ANCHOR\0\0", 8);
5387                 *((Int *)(p + 8)) = len - (8 + sizeof(Int));
5388                 p += 8 + sizeof(Int);
5389                 for (i = 0; i < mp->natoms; i++) {
5390                         Int count, *ip;
5391                         ap = ATOM_AT_INDEX(mp->atoms, i);
5392                         if (ap->anchor != NULL) {
5393                                 count = ap->anchor->connect.count;
5394                                 *((Int *)p) = i;
5395                                 *((Int *)(p + sizeof(Int))) = count;
5396                                 p += sizeof(Int) * 2;
5397                                 ip = AtomConnectData(&(ap->anchor->connect));
5398                                 memmove(p, ip, sizeof(Int) * count);
5399                                 p += sizeof(Int) * count;
5400                                 memmove(p, ap->anchor->coeffs, sizeof(Double) * count);
5401                                 p += sizeof(Double) * count;
5402                         }
5403                 }
5404                 len_all += len;
5405         }
5406         
5407         /*  Parameters  */
5408         if (mp->par != NULL) {
5409                 int type;
5410                 for (type = kFirstParType; type <= kLastParType; type++) {
5411                         const char *parname;
5412                         Int parsize, parcount;
5413                         void *parptr;
5414                         switch (type) {
5415                                 case kBondParType:
5416                                         parname = "BONDPAR\0";
5417                                         parsize = sizeof(BondPar);
5418                                         parcount = mp->par->nbondPars;
5419                                         parptr = mp->par->bondPars;
5420                                         break;
5421                                 case kAngleParType:
5422                                         parname = "ANGPAR\0\0";
5423                                         parsize = sizeof(AnglePar);
5424                                         parcount = mp->par->nanglePars;
5425                                         parptr = mp->par->anglePars;
5426                                         break;
5427                                 case kDihedralParType:
5428                                         parname = "DIHEPAR\0";
5429                                         parsize = sizeof(TorsionPar);
5430                                         parcount = mp->par->ndihedralPars;
5431                                         parptr = mp->par->dihedralPars;
5432                                         break;
5433                                 case kImproperParType:
5434                                         parname = "IMPRPAR\0";
5435                                         parsize = sizeof(TorsionPar);
5436                                         parcount = mp->par->nimproperPars;
5437                                         parptr = mp->par->improperPars;
5438                                         break;
5439                                 case kVdwParType:
5440                                         parname = "VDWPAR\0\0";
5441                                         parsize = sizeof(VdwPar);
5442                                         parcount = mp->par->nvdwPars;
5443                                         parptr = mp->par->vdwPars;
5444                                         break;
5445                                 case kVdwPairParType:
5446                                         parname = "VDWPPAR\0";
5447                                         parsize = sizeof(VdwPairPar);
5448                                         parcount = mp->par->nvdwpPars;
5449                                         parptr = mp->par->vdwpPars;
5450                                         break;
5451                                 case kVdwCutoffParType:
5452                                         parname = "VCUTPAR\0";
5453                                         parsize = sizeof(VdwCutoffPar);
5454                                         parcount = mp->par->nvdwCutoffPars;
5455                                         parptr = mp->par->vdwCutoffPars;
5456                                         break;
5457                                 default:
5458                                         continue;
5459                         }
5460                         if (parcount > 0) {
5461                                 len = 8 + sizeof(Int) + parsize * parcount;
5462                                 ptr = (char *)realloc(ptr, len_all + len);
5463                                 if (ptr == NULL)
5464                                         goto out_of_memory;
5465                                 p = ptr + len_all;
5466                                 memmove(p, parname, 8);
5467                                 *((Int *)(p + 8)) = parsize * parcount;
5468                                 p += 8 + sizeof(Int);
5469                                 memmove(p, parptr, parsize * parcount);
5470                                 len_all += len;
5471                         }
5472                 }
5473         }
5474         
5475         /*  Time stamp  */
5476         {
5477                 time_t tm = time(NULL);
5478                 len = 8 + sizeof(Int) + sizeof(Int);
5479                 ptr = (char *)realloc(ptr, len_all + len);
5480                 if (ptr == NULL)
5481                         goto out_of_memory;
5482                 p = ptr + len_all;
5483                 memmove(p, "TIME\0\0\0\0", 8);
5484                 *((Int *)(p + 8)) = sizeof(Int);
5485                 p += 8 + sizeof(Int);
5486                 *((Int *)p) = (Int)tm;
5487                 len_all += len;
5488                 if (timep != NULL)
5489                         *timep = (Int)tm;
5490         }
5491         
5492         if (outLength != NULL)
5493                 *outLength = len_all;
5494         return ptr;
5495
5496   out_of_memory:
5497     Panic("Low memory while serializing a molecule data");
5498         return NULL; /* Not reached */  
5499 }
5500
5501 #pragma mark ====== Search for bonds, angles, dihedrals, impropers ======
5502
5503 static IntGroup *
5504 sMoleculeSearchIncludingAtoms(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5505 {
5506         int i, j;
5507         Int *ip;
5508         IntGroup *gp = NULL;
5509         if (atomgroup == NULL)
5510                 return NULL;
5511         for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5512                 for (j = 0; j < nsize; j++) {
5513                         if (IntGroupLookup(atomgroup, ip[j], NULL) != 0) {
5514                                 if (gp == NULL)
5515                                         gp = IntGroupNew();
5516                                 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5517                                         Panic("Low memory while searching %s", msg);
5518                                 break;
5519                         }
5520                 }
5521         }
5522         return gp;
5523 }
5524
5525 IntGroup *
5526 MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5527 {
5528         if (mp == NULL)
5529                 return NULL;
5530         return sMoleculeSearchIncludingAtoms(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5531 }
5532
5533 IntGroup *
5534 MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5535 {
5536         if (mp == NULL)
5537                 return NULL;
5538         return sMoleculeSearchIncludingAtoms(mp->nangles, mp->angles, 3, atomgroup, "angles");
5539 }
5540
5541 IntGroup *
5542 MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5543 {
5544         if (mp == NULL)
5545                 return NULL;
5546         return sMoleculeSearchIncludingAtoms(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5547 }
5548
5549 IntGroup *
5550 MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5551 {
5552         if (mp == NULL)
5553                 return NULL;
5554         return sMoleculeSearchIncludingAtoms(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5555 }
5556
5557 static IntGroup *
5558 sMoleculeSearchAcrossAtomGroup(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5559 {
5560         int i, j;
5561         Int *ip;
5562         IntGroup *gp = NULL;
5563         if (atomgroup == NULL)
5564                 return NULL;
5565         for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5566                 int k = -1;
5567                 for (j = 0; j < nsize; j++) {
5568                         int kk;
5569                         kk = (IntGroupLookup(atomgroup, ip[j], NULL) != 0);
5570                         if (k < 0)
5571                                 k = kk;
5572                         else if (k != kk) {
5573                                 /*  This bond etc. crosses the atom group border  */
5574                                 if (gp == NULL)
5575                                         gp = IntGroupNew();
5576                                 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5577                                         Panic("Low memory while searching %s", msg);
5578                                 break;
5579                         }
5580                 }
5581         }
5582         return gp;
5583 }
5584
5585 IntGroup *
5586 MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5587 {
5588         if (mp == NULL)
5589                 return NULL;
5590         return sMoleculeSearchAcrossAtomGroup(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5591 }
5592
5593 IntGroup *
5594 MoleculeSearchAnglesAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5595 {
5596         if (mp == NULL)
5597                 return NULL;
5598         return sMoleculeSearchAcrossAtomGroup(mp->nangles, mp->angles, 3, atomgroup, "angles");
5599 }
5600
5601 IntGroup *
5602 MoleculeSearchDihedralsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5603 {
5604         if (mp == NULL)
5605                 return NULL;
5606         return sMoleculeSearchAcrossAtomGroup(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5607 }
5608
5609 IntGroup *
5610 MoleculeSearchImpropersAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5611 {
5612         if (mp == NULL)
5613                 return NULL;
5614         return sMoleculeSearchAcrossAtomGroup(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5615 }
5616
5617 /*  Subroutine for MoleculeGuessBonds. It can be also used independently, but make sure that *outNbonds/*outBonds 
5618     _correctly_ represents an array of two integers (as in mp->nbonds/mp->bonds).  */
5619 /*  Find atoms within the given "distance" from the given atom.  */
5620 /*  If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5621  the threshold distance is given by the sum of van der Waals radii times limit.  */
5622 /*  If triangle is non-zero, then only atoms with lower indexes than index are looked for.  */
5623 int
5624 MoleculeFindCloseAtoms(Molecule *mp, Int index, Double limit, Int *outNbonds, Int **outBonds, Int triangle)
5625 {
5626         Int n1, n2, j, nlim, newbond[2];
5627         Double a1, a2, alim;
5628         Vector dr, r1, r2;
5629         Atom *ap = ATOM_AT_INDEX(mp->atoms, index);
5630         n1 = ap->atomicNumber;
5631         if (n1 >= 0 && n1 < gCountElementParameters)
5632                 a1 = gElementParameters[n1].radius;
5633         else a1 = gElementParameters[6].radius;
5634         r1 = ap->r;
5635         nlim = (triangle ? index : mp->natoms);
5636         for (j = 0; j < nlim; j++) {
5637                 Atom *bp = ATOM_AT_INDEX(mp->atoms, j);
5638                 if (index == j)
5639                         continue;
5640                 n2 = bp->atomicNumber;
5641                 if (n2 >= 0 && n2 < gCountElementParameters)
5642                         a2 = gElementParameters[n2].radius;
5643                 else a2 = gElementParameters[6].radius;
5644                 r2 = bp->r;
5645                 VecSub(dr, r1, r2);
5646                 if (limit < 0)
5647                         alim = -limit;
5648                 else
5649                         alim = limit * (a1 + a2);
5650                 if (VecLength2(dr) < alim * alim) {
5651                         newbond[0] = index;
5652                         newbond[1] = j;
5653                         /*      MoleculeAddBonds(mp, 1, newbonds); */
5654                         AssignArray(outBonds, outNbonds, sizeof(Int) * 2, *outNbonds, newbond);
5655                 }
5656         }
5657         return 0;
5658 }
5659
5660 /*  Guess the bonds from the coordinates  */
5661 /*  If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5662     the threshold distance is given by the sum of van der Waals radii times limit.  */
5663 int
5664 MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds)
5665 {
5666         Int nbonds, *bonds, i, newbond[2];
5667 /*      int i, j, n1, n2;
5668         Atom *ap, *bp;
5669         Vector r1, r2, dr;
5670         Double a1, a2, alim;
5671         Int newbond[2];
5672         ElementPar *p = gElementParameters; */
5673         nbonds = 0;
5674         bonds = NULL;
5675         for (i = 0; i < mp->natoms; i++) {
5676                 MoleculeFindCloseAtoms(mp, i, limit, &nbonds, &bonds, 1);
5677                 /*
5678                 ap = ATOM_AT_INDEX(mp->atoms, i);
5679                 n1 = ap->atomicNumber;
5680                 if (n1 >= 0 && n1 < gCountElementParameters)
5681                         a1 = p[n1].radius;
5682                 else a1 = p[6].radius;
5683                 r1 = ap->r;
5684                 for (j = 0; j < i; j++) {
5685                         bp = ATOM_AT_INDEX(mp->atoms, j);
5686                         n2 = bp->atomicNumber;
5687                         if (n2 >= 0 && n2 < gCountElementParameters)
5688                                 a2 = p[n2].radius;
5689                         else a2 = p[6].radius;
5690                         r2 = bp->r;
5691                         VecSub(dr, r1, r2);
5692                         if (limit < 0)
5693                                 alim = -limit;
5694                         else
5695                                 alim = limit * (a1 + a2);
5696                         if (VecLength2(dr) < alim * alim) {
5697                                 newbond[0] = i;
5698                                 newbond[1] = j;
5699                                 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5700                         }
5701                 }
5702                 */
5703         }
5704         if (nbonds > 0) {
5705                 newbond[0] = kInvalidIndex;
5706                 newbond[1] = 0;
5707                 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5708                 nbonds--;
5709         }
5710         if (outNbonds != NULL)
5711                 *outNbonds = nbonds;
5712         if (outBonds != NULL)
5713                 *outBonds = bonds;
5714         return 0;
5715 }
5716
5717 /*  Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information  */
5718 int
5719 MoleculeRebuildTablesFromConnects(Molecule *mp)
5720 {
5721         int i, j, k, retval;
5722         Atom *ap;
5723         Int ibuf[6], *cp;
5724         
5725         __MoleculeLock(mp);
5726
5727         /*  Find bonds   */
5728         if (mp->nbonds == 0) {
5729                 for (i = 0; i < mp->natoms; i++) {
5730                         ap = ATOM_AT_INDEX(mp->atoms, i);
5731                         cp = AtomConnectData(&ap->connect);
5732                         for (j = 0; j < ap->connect.count; j++) {
5733                                 k = cp[j];
5734                                 if (i >= k)
5735                                         continue;
5736                                 ibuf[0] = i;
5737                                 ibuf[1] = k;
5738                                 /*  MoleculeAddBonds() should not be used, because it assumes connects[] and
5739                                     bonds are already in sync  */
5740                                 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
5741                         /*      retval = MoleculeAddBonds(mp, 1, ibuf);
5742                                 if (retval != 0)
5743                                         goto abort; */
5744                         }
5745                 }
5746         }
5747         
5748         /*  Find angles  */
5749         if (mp->nangles == 0) {
5750                 for (i = 0; i < mp->natoms; i++) {
5751                         ap = ATOM_AT_INDEX(mp->atoms, i);
5752                         cp = AtomConnectData(&ap->connect);
5753                         for (j = 0; j < ap->connect.count; j++) {
5754                                 for (k = j + 1; k < ap->connect.count; k++) {
5755                                         ibuf[0] = cp[j];
5756                                         ibuf[1] = i;
5757                                         ibuf[2] = cp[k];
5758                                         ibuf[3] = -1;
5759                                         retval = MoleculeAddAngles(mp, ibuf, NULL);
5760                                         if (retval < 0)
5761                                                 goto abort;
5762                                 }
5763                         }
5764                 }
5765         }
5766         
5767         /*  Find dihedrals  */
5768         if (mp->ndihedrals == 0) {
5769                 for (i = 0; i < mp->natoms; i++) {
5770                         ap = ATOM_AT_INDEX(mp->atoms, i);
5771                         cp = AtomConnectData(&ap->connect);
5772                         for (j = 0; j < ap->connect.count; j++) {
5773                                 int jj, kk, mm, m;
5774                                 Atom *apjj;
5775                                 Int *cpjj;
5776                                 jj = cp[j];
5777                                 if (i >= jj)
5778                                         continue;
5779                                 apjj = ATOM_AT_INDEX(mp->atoms, jj);
5780                                 cpjj = AtomConnectData(&apjj->connect);
5781                                 for (k = 0; k < ap->connect.count; k++) {
5782                                         if (k == j)
5783                                                 continue;
5784                                         kk = cp[k];
5785                                         for (m = 0; m < apjj->connect.count; m++) {
5786                                                 mm = cpjj[m];
5787                                                 if (mm == i || mm == kk)
5788                                                         continue;
5789                                                 ibuf[0] = kk;
5790                                                 ibuf[1] = i;
5791                                                 ibuf[2] = jj;
5792                                                 ibuf[3] = mm;
5793                                                 ibuf[4] = -1;
5794                                                 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
5795                                                 if (retval < 0)
5796                                                         goto abort;
5797                                         }
5798                                 }
5799                         }
5800                 }
5801         }
5802         
5803         /*  Find impropers  */
5804         if (mp->nimpropers == 0) {
5805                 for (i = 0; i < mp->natoms; i++) {
5806                         int i1, i2, i4, n1, n2, n4;
5807                         ap = ATOM_AT_INDEX(mp->atoms, i);
5808                         cp = AtomConnectData(&ap->connect);
5809                         for (i1 = 0; i1 < ap->connect.count; i1++) {
5810                                 n1 = cp[i1];
5811                                 for (i2 = i1 + 1; i2 < ap->connect.count; i2++) {
5812                                         n2 = cp[i2];
5813                                         for (i4 = i2 + 1; i4 < ap->connect.count; i4++) {
5814                                                 n4 = cp[i4];
5815                                                 ibuf[0] = n1;
5816                                                 ibuf[1] = n2;
5817                                                 ibuf[2] = i;
5818                                                 ibuf[3] = n4;
5819                                                 ibuf[4] = -1;
5820                                                 retval = MoleculeAddImpropers(mp, ibuf, NULL);
5821                                                 if (retval < 0)
5822                                                         goto abort;
5823                                         }
5824                                 }
5825                         }
5826                 }
5827         }
5828
5829         mp->needsMDRebuild = 1;
5830         __MoleculeUnlock(mp);
5831         return 0;
5832
5833   abort:
5834         __MoleculeUnlock(mp);
5835         return retval;
5836 }
5837
5838 int
5839 MoleculeAreAtomsConnected(Molecule *mol, int idx1, int idx2)
5840 {
5841         Atom *ap1 = ATOM_AT_INDEX(mol->atoms, idx1);
5842         if (AtomConnectHasEntry(&ap1->connect, idx2))
5843                 return 1;
5844         else if (ap1->anchor != NULL && AtomConnectHasEntry(&(ap1->anchor->connect), idx2))
5845                 return 2;
5846         else return 0;
5847 }
5848
5849 #pragma mark ====== Atom names ======
5850
5851 /*  Look for the n1-th atom in resno-th residue (n1 is 0-based)  */
5852 int
5853 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
5854 {
5855         int i, j, lasti;
5856         Atom *ap;
5857         if (mp == NULL || mp->natoms == 0)
5858                 return -1;
5859         lasti = -1;
5860         for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5861                 if (ap->resSeq == resno) {
5862                         lasti = i;
5863                         if (j++ == n1)
5864                                 return i;
5865                 }
5866         }
5867         if (n1 == -1)
5868                 return lasti; /* max */
5869         return -1;
5870 }
5871
5872 int
5873 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
5874 {
5875     int n;
5876     char *p;
5877         n = strtol(s, &p, 0);
5878         if (p > s) {
5879                 while (isspace(*p))
5880                         p++;
5881                 if (*p == 0) {
5882                   resName[0] = 0;
5883                   *resSeq = -1;
5884                   atomName[0] = 0;
5885                   return n;
5886                 }
5887         }
5888
5889         if ((p = strchr(s, ':')) != NULL) {
5890                 /*  Residue is specified  */
5891                 char *pp;
5892                 if ((pp = strchr(s, '.')) != NULL && pp < p) {
5893                         /*  Residue number is also specified  */
5894                         char *ppp;
5895                         n = pp - s;
5896                         *resSeq = strtol(pp + 1, &ppp, 0);
5897                         if (ppp == pp + 1)
5898                                 return -2;  /*  Bad format  */
5899                         while (isspace(*ppp))
5900                                 ppp++;
5901                         if (ppp != p)
5902                                 return -2;  /*  Bad format  */
5903                 } else {
5904                         *resSeq = -1;
5905                         /*  Check whether the "residue name" is an integer  */
5906                         n = strtol(s, &pp, 0);
5907                         if (pp > s) {
5908                                 while (isspace(*pp))
5909                                         pp++;
5910                                 if (*pp == 0 || *pp == ':') {
5911                                         *resSeq = n;
5912                                         if (*resSeq < 0)
5913                                                 return -2;  /*  Bad format  */
5914                                 }
5915                         }
5916                         if (*resSeq >= 0)
5917                                 n = 0;
5918                         else
5919                                 n = p - s;
5920                 }
5921                 if (n >= sizeof(resName))
5922                         n = sizeof(resName) - 1;
5923                 strncpy(resName, s, n);
5924                 resName[n] = 0;
5925                 p++;
5926         } else {
5927                 resName[0] = 0;
5928                 *resSeq = -1;
5929                 p = (char *)s;
5930         }
5931         strncpy(atomName, p, 4);
5932         atomName[4] = 0;
5933         return 0;
5934 }
5935
5936 /*  Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer  */
5937 int
5938 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
5939 {
5940         char resName[6];
5941         int resSeq, n;
5942         char atomName[6];
5943         /*      char *p; */
5944
5945         n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
5946         if (atomName[0] == 0) {
5947           if (n >= mp->natoms)
5948             n = -1;  /* Out of range */
5949           return n;
5950         }
5951         for (n = 0; n < mp->natoms; n++) {
5952                 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
5953                 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
5954                         && (resSeq < 0 || ap->resSeq == resSeq)
5955                         && strncmp(atomName, ap->aname, 4) == 0) {
5956                         return n;
5957                 }
5958         }
5959         return -1;  /*  Not found  */
5960 }
5961
5962 void
5963 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
5964 {
5965         Atom *ap;
5966         int n;
5967         if (mp == NULL || index < 0 || index >= mp->natoms) {
5968                 buf[0] = 0;
5969                 return;
5970         }
5971         ap = mp->atoms + index;
5972         if (ap->resSeq != 0) {
5973                 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
5974                 buf += n;
5975                 bufsize -= n;
5976         }
5977         snprintf(buf, bufsize, "%.4s", ap->aname);
5978 }
5979
5980 #pragma mark ====== Selection ======
5981
5982 static void
5983 sMoleculeNotifyChangeSelection(Molecule *mp)
5984 {
5985         /*  TODO: Finer control of notification types may be necessary  */
5986         MoleculeCallback_notifyModification(mp, 0);
5987 }
5988
5989 void
5990 MoleculeSetSelection(Molecule *mp, IntGroup *select)
5991 {
5992         if (mp == NULL)
5993                 return;
5994         if (select != NULL)
5995                 IntGroupRetain(select);
5996         if (mp->selection != NULL)
5997                 IntGroupRelease(mp->selection);
5998         mp->selection = select;
5999         sMoleculeNotifyChangeSelection(mp);
6000 }
6001
6002 IntGroup *
6003 MoleculeGetSelection(Molecule *mp)
6004 {
6005         if (mp == NULL)
6006                 return NULL;
6007         else return mp->selection;
6008 }
6009
6010 void
6011 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
6012 {
6013         if (mp->selection == NULL)
6014                 mp->selection = IntGroupNew();
6015         if (!extending)
6016                 IntGroupClear(mp->selection);
6017         IntGroupAdd(mp->selection, n1, 1);
6018         sMoleculeNotifyChangeSelection(mp);
6019 }
6020
6021 void
6022 MoleculeUnselectAtom(Molecule *mp, int n1)
6023 {
6024         if (mp->selection != NULL)
6025                 IntGroupRemove(mp->selection, n1, 1);
6026         sMoleculeNotifyChangeSelection(mp);
6027 }
6028
6029 void
6030 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
6031 {
6032         if (mp->selection == NULL)
6033                 mp->selection = IntGroupNew();
6034         IntGroupReverse(mp->selection, n1, 1);
6035         sMoleculeNotifyChangeSelection(mp);
6036 }
6037
6038 int
6039 MoleculeIsAtomSelected(Molecule *mp, int n1)
6040 {
6041         if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
6042                 return 1;
6043         else return 0;
6044 }
6045
6046 int
6047 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
6048 {
6049         if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
6050                 return 1;
6051         else return 0;
6052 }
6053
6054 IntGroup *
6055 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
6056 {
6057         int status;
6058         IntGroup *remain, *ig1, *ig2;
6059         ig1 = ig2 = NULL;
6060         remain = IntGroupNewFromIntGroup(remove);
6061         if (remain == NULL)
6062                 status = -1;
6063         else
6064                 status = IntGroupReverse(remain, 0, mp->natoms);
6065         if (status == 0) {
6066                 ig1 = IntGroupNew();
6067                 if (ig1 == NULL)
6068                         status = -1;
6069                 else
6070                         status = IntGroupDifference(selection, remove, ig1);
6071         }
6072         if (status == 0) {
6073                 ig2 = IntGroupNew();
6074                 if (ig2 == NULL)
6075                         status = -1;
6076                 else
6077                         status = IntGroupDeconvolute(ig1, remain, ig2);
6078         }
6079         if (remain != NULL)
6080                 IntGroupRelease(remain);
6081         if (ig1 != NULL)
6082                 IntGroupRelease(ig1);
6083         if (status == 0)
6084                 return ig2;
6085         else {
6086                 if (ig2 != NULL)
6087                         IntGroupRelease(ig2);
6088                 return NULL;
6089         }
6090 }
6091
6092 #pragma mark ====== Atom Equivalence ======
6093
6094 struct sEqList {
6095         int i[2];
6096         struct sEqList *next;
6097         struct sEqList *link;
6098 };
6099
6100 static struct sEqList *sListBase = NULL;
6101 static struct sEqList *sListFree = NULL;
6102
6103 static struct sEqList *
6104 sAllocEqList(void)
6105 {
6106         struct sEqList *lp;
6107         if (sListFree != NULL) {
6108                 lp = sListFree;
6109                 sListFree = lp->next;
6110                 lp->i[0] = lp->i[1] = 0;
6111                 lp->next = NULL;
6112                 return lp;
6113         }
6114         lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
6115         lp->link = sListBase;
6116         sListBase = lp;
6117         return lp;
6118 }
6119
6120 static void
6121 sFreeEqList(struct sEqList *list)
6122 {
6123         list->next = sListFree;
6124         sListFree = list;
6125 }
6126
6127 static void
6128 sDeallocateEqLists(void)
6129 {
6130         struct sEqList *lp, *lp_link;
6131         for (lp = sListBase; lp != NULL; lp = lp_link) {
6132                 lp_link = lp->link;
6133                 free(lp);
6134         }
6135         sListBase = NULL;
6136         sListFree = NULL;
6137 }
6138
6139 static int
6140 sExistInEqList(int i, int idx, struct sEqList *list)
6141 {
6142         while (list != NULL) {
6143                 if (list->i[idx] == i)
6144                         return 1;
6145                 list = list->next;
6146         }
6147         return 0;
6148 }
6149
6150 static struct sEqList *
6151 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
6152 {
6153         Atom *api, *apj;
6154         struct sEqList *list1, *list2;
6155         Int ii, jj, ni, nj, *cpi, *cpj;
6156         api = ATOM_AT_INDEX(mol->atoms, i);
6157         apj = ATOM_AT_INDEX(mol->atoms, j);
6158         if (api->atomicNumber != apj->atomicNumber)
6159                 return NULL;
6160         list1 = sAllocEqList();
6161         if (list1 == NULL)
6162                 return NULL;
6163         list1->i[0] = i;
6164         list1->i[1] = j;
6165         list1->next = list;
6166         if (i == j || (db[i] != NULL && db[i] == db[j]))
6167                 return list1;
6168         cpi = AtomConnectData(&api->connect);
6169         cpj = AtomConnectData(&apj->connect);
6170         for (ni = 0; ni < api->connect.count; ni++) {
6171                 ii = cpi[ni];
6172                 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
6173                         continue;
6174                 if (sExistInEqList(ii, 0, list1))
6175                         continue;
6176                 list2 = NULL;
6177                 for (nj = 0; nj < apj->connect.count; nj++) {
6178                         jj = cpj[nj];
6179                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6180                                 continue;
6181                         if (sExistInEqList(jj, 1, list1))
6182                                 continue;
6183                         list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
6184                         if (list2 != NULL)
6185                                 break;
6186                 }
6187                 if (list2 == NULL) {
6188                         sFreeEqList(list1);
6189                         return NULL;    /*  No equivalent to ii  */
6190                 }
6191                 list1 = list2;      /*  ii is OK, try next  */
6192         }
6193         return list1;
6194 }
6195
6196 int
6197 sDBInclude(Int *ip, int i)
6198 {
6199         int j;
6200         if (ip == NULL)
6201                 return -1;
6202         for (j = ip[0] - 1; j >= 0; j--) {
6203                 if (ip[j] == i)
6204                         return j;
6205         }
6206         return -1;
6207 }
6208
6209 Int *
6210 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
6211 {
6212         Int **db;  /*  List of equivalents for each atom  */
6213         Int *ip, *result;
6214         Atom *api, *apj, *apk;
6215         Int *cpi, *cpj, *ibuf, nibuf;
6216         int i, j, k, ii, jj, kk;
6217         if (mol == NULL || mol->natoms == 0)
6218                 return NULL;
6219         db = (Int **)calloc(sizeof(Int *), mol->natoms);
6220         ibuf = NULL;
6221         nibuf = 0;
6222
6223         /*  Find the equivalent univalent atoms  */
6224         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6225                 if (api->connect.count < 2)
6226                         continue;
6227                 cpi = AtomConnectData(&api->connect);
6228                 for (j = 0; j < api->connect.count; j++) {
6229                         Int n;
6230                         n = 0;
6231                         jj = cpi[j];
6232                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6233                                 continue;
6234                         AssignArray(&ibuf, &nibuf, sizeof(Int), n, &jj);
6235                         n++;
6236                         apj = ATOM_AT_INDEX(mol->atoms, jj);
6237                         if (apj->connect.count != 1 || db[jj] != NULL)
6238                                 continue;
6239                         cpj = AtomConnectData(&apj->connect);
6240                         for (k = j + 1; k < api->connect.count; k++) {
6241                                 kk = cpj[k];
6242                                 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
6243                                         continue;
6244                                 apk = ATOM_AT_INDEX(mol->atoms, kk);
6245                                 if (apk->connect.count != 1 || db[kk] != NULL)
6246                                         continue;
6247                                 if (apj->atomicNumber == apk->atomicNumber) {
6248                                         AssignArray(&ibuf, &nibuf, sizeof(Int), n, &kk);
6249                                         n++;
6250                                 }
6251                         }
6252                         if (n > 1) {
6253                                 ip = (Int *)calloc(sizeof(Int), n + 1);
6254                                 if (ip == NULL)
6255                                         return NULL;
6256                                 ip[0] = n;
6257                                 memmove(ip + 1, ibuf, sizeof(Int) * n);
6258                                 for (k = 0; k < n; k++)
6259                                         db[ip[k + 1]] = ip;
6260                         }
6261                 }
6262         }
6263         if (ibuf != NULL) {
6264                 free(ibuf);
6265                 ibuf = NULL;
6266         }
6267         
6268         /*  Try matching (i,j) pair  */
6269         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6270                 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
6271                         continue;
6272                 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
6273                         struct sEqList *list;
6274                         if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
6275                                 continue;
6276                         if (api->atomicNumber != apj->atomicNumber)
6277                                 continue;  /*  Different elements do not match  */
6278                         if (db[i] != NULL && db[i] == db[j])
6279                                 continue;  /*  Already equivalent  */
6280                         list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
6281                         if (list == NULL)
6282                                 continue;  /*  (i,j) do not match  */
6283                         while (list != NULL) {
6284                                 ii = list->i[0];
6285                                 jj = list->i[1];
6286                                 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
6287                                         /*  Merge db[ii] and db[jj]  */
6288                                         k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
6289                                         ip = (Int *)calloc(sizeof(Int), k + 1);
6290                                         if (ip == NULL)
6291                                                 return NULL;  /*  Out of memory  */
6292                                         if (db[ii] == NULL) {
6293                                                 ip[1] = ii;
6294                                                 k = 2;
6295                                         } else {
6296                                                 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
6297                                                 k = db[ii][0] + 1;
6298                                         }
6299                                         if (db[jj] == NULL) {
6300                                                 ip[k++] = jj;
6301                                         } else {
6302                                                 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
6303                                                 k += db[jj][0];
6304                                         }
6305                                         ip[0] = k - 1;
6306                                         /*  Free old ones  */
6307                                         if (db[ii] != NULL)
6308                                                 free(db[ii]);
6309                                         if (db[jj] != NULL)
6310                                                 free(db[jj]);
6311                                         for (k = 0; k < ip[0]; k++)
6312                                                 db[ip[k + 1]] = ip;
6313                                         if (0) {
6314                                                 /*  For debug  */
6315                                                 printf("(%d,%d) matched: ", ii, jj);
6316                                                 for (k = 0; k < ip[0]; k++) {
6317                                                         printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
6318                                                 }
6319                                                 printf("]\n");
6320                                         }
6321                                 }
6322                                 list = list->next;
6323                         }
6324                 }
6325         }
6326         
6327         /*  Record the equivalent atoms with the lowest index for each atom  */
6328         result = (Int *)calloc(sizeof(Int), mol->natoms);
6329         for (i = 0; i < mol->natoms; i++)
6330                 result[i] = -1;
6331         for (i = 0; i < mol->natoms; i++) {
6332                 if (result[i] >= 0 || (ip = db[i]) == NULL)
6333                         continue;
6334                 k = mol->natoms;
6335                 for (j = 0; j < ip[0]; j++) {
6336                         kk = ip[j + 1];
6337                         if (kk < k)
6338                                 k = kk;
6339                 }
6340                 for (j = 0; j < ip[0]; j++) {
6341                         result[ip[j + 1]] = k;
6342                         db[ip[j + 1]] = NULL;
6343                 }
6344                 free(ip);
6345         }
6346         sDeallocateEqLists();
6347         return result;
6348 }
6349
6350 #pragma mark ====== Symmetry expansion ======
6351
6352 int
6353 MoleculeGetTransformForSymop(Molecule *mp, Symop symop, Transform *tf, int is_cartesian)
6354 {
6355         Transform t;
6356         if (mp == NULL || mp->cell == NULL)
6357                 return -1;
6358         if (symop.sym >= mp->nsyms && symop.sym != 0)
6359                 return -2;
6360         memmove(*tf, SYMMETRY_AT_INDEX(mp->syms, symop.sym), sizeof(Transform));
6361         (*tf)[9] += symop.dx;
6362         (*tf)[10] += symop.dy;
6363         (*tf)[11] += symop.dz;
6364         if (is_cartesian) {
6365                 TransformMul(t, *tf, mp->cell->rtr);
6366                 TransformMul(*tf, mp->cell->tr, t);
6367         }
6368         return 0;
6369 }
6370
6371 int
6372 MoleculeGetSymopForTransform(Molecule *mp, const Transform tf, Symop *symop, int is_cartesian)
6373 {
6374         Transform t;
6375         int i, j, n[3];
6376         if (mp == NULL || mp->cell == NULL)
6377                 return -1;
6378         if (is_cartesian) {
6379                 TransformMul(t, tf, mp->cell->tr);
6380                 TransformMul(t, mp->cell->rtr, t);
6381         } else {
6382                 memmove(t, tf, sizeof(Transform));
6383         }
6384         for (i = 0; i < mp->nsyms || i == 0; i++) {
6385                 Transform *tp = &(SYMMETRY_AT_INDEX(mp->syms, i));
6386                 for (j = 0; j < 9; j++) {
6387                         if (fabs((*tp)[j] - t[j]) > 1e-4)
6388                                 break;
6389                 }
6390                 if (j == 9) {
6391                         for (j = 9; j < 12; j++) {
6392                                 double f1 = t[j] - (*tp)[j];
6393                                 double f2 = floor(f1 + 0.5);
6394                                 if (fabs(f1 - f2) > 1e-4)
6395                                         break;
6396                                 n[j - 9] = f2;
6397                         }
6398                         if (j == 12) {
6399                                 /*  Found  */
6400                                 symop->sym = i;
6401                                 symop->dx = n[0];
6402                                 symop->dy = n[1];
6403                                 symop->dz = n[2];
6404                                 symop->alive = (SYMOP_ALIVE((*symop)) != 0);
6405                                 return 0;
6406                         }
6407                 }
6408         }
6409         return -3;  /*  Not found  */
6410 }
6411
6412 int
6413 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
6414 {
6415         if (mp == NULL)
6416                 return 1;
6417         if (symop.sym >= mp->nsyms && symop.sym != 0)
6418                 return 2;
6419         if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
6420                 TransformVec(vpout, mp->cell->rtr, vpin);
6421                 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpout);
6422                 vpout->x += symop.dx;
6423                 vpout->y += symop.dy;
6424                 vpout->z += symop.dz;
6425                 TransformVec(vpout, mp->cell->tr, vpout);
6426         } else {
6427                 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpin);
6428                 vpout->x += symop.dx;
6429                 vpout->y += symop.dy;
6430                 vpout->z += symop.dz;
6431         }
6432         return 0;
6433 }
6434
6435 /*  Add expanded atoms. Returns the number of newly created atoms.
6436         If indices is non-NULL, it should be an array of Int with at least 
6437         IntGroupGetCount(group) entries, and on return it contains the
6438     indices of the expanded atoms (may be existing atoms if the expanded
6439     atoms are already present)
6440     If allowOverlap is non-zero, then the new atom is created even when the
6441     coordinates coincide with the some other atom (special position) of the
6442     same element; otherwise, such atom will not be created and the existing
6443     atom is returned in indices[].  */
6444 int
6445 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group, Int *indices, Int allowOverlap)
6446 {
6447         int i, n, n0, n1, n2, base, count, *table;
6448         Atom *ap;
6449         IntGroupIterator iter;
6450         Transform tr, t1;
6451         Symop symop1;
6452         Atom *ap2;
6453         Vector nr, dr;
6454         
6455         if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
6456                 return -1;
6457         if (symop.sym != 0 && symop.sym >= mp->nsyms)
6458                 return -2;
6459
6460         /*  Create atoms, with avoiding duplicates  */
6461         n0 = n1 = mp->natoms;
6462         table = (int *)malloc(sizeof(int) * n0);
6463         if (table == NULL)
6464                 return -3;
6465         for (i = 0; i < n0; i++)
6466                 table[i] = -1;
6467         IntGroupIteratorInit(group, &iter);
6468         MoleculeGetTransformForSymop(mp, symop, &tr, 0);
6469         __MoleculeLock(mp);
6470         for (i = 0; i < count; i++) {
6471                 n = IntGroupIteratorNext(&iter);
6472                 ap = ATOM_AT_INDEX(mp->atoms, n);
6473                 if (SYMOP_ALIVE(ap->symop)) {
6474                         /*  Calculate the cumulative symop  */
6475                         MoleculeGetTransformForSymop(mp, ap->symop, &t1, 0);
6476                         TransformMul(tr, tr, t1);
6477                         if (MoleculeGetSymopForTransform(mp, tr, &symop1, 0) != 0) {
6478                                 if (indices != NULL)
6479                                         indices[i] = -1;
6480                                 continue;  /*  Skip this atom  */
6481                         }
6482                         base = ap->symbase;
6483                 } else {
6484                         symop1 = symop;
6485                         base = n;
6486                 }
6487
6488                 /*  Calculate the expande position  */
6489                 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
6490                 
6491                 /*  Is this expansion already present?  */
6492                 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
6493                         /*  Symmetry operation and the base atom are the same  */
6494                         if (ap2->symbase == base && SYMOP_EQUAL(symop1, ap2->symop))
6495                                 break;
6496                         /*  Atomic number and the position are the same  */
6497                         if (ap2->atomicNumber == ap->atomicNumber && allowOverlap == 0) {
6498                                 VecSub(dr, ap2->r, nr);
6499                                 if (VecLength2(dr) < 1e-6)
6500                                         break;
6501                         }
6502                 }
6503                 if (n2 < n0) {
6504                         /*  If yes, then skip it  */
6505                         if (indices != NULL)
6506                                 indices[i] = n2;
6507                         continue;
6508                 } else {
6509                         /*  Create a new atom  */
6510                         Atom newAtom;
6511                         AtomDuplicate(&newAtom, ap);
6512                         MoleculeCreateAnAtom(mp, &newAtom, -1);
6513                         AtomClean(&newAtom);
6514                         ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
6515                         ap2->r = nr;
6516                         ap2->symbase = n;
6517                         ap2->symop = symop;
6518                         ap2->symop.alive = (symop.dx != 0 || symop.dy != 0 || symop.dz != 0 || symop.sym != 0);
6519                         table[n] = n1;  /*  The index of the new atom  */
6520                         MoleculeSetAnisoBySymop(mp, n1);  /*  Recalculate anisotropic parameters according to symop  */
6521                         if (indices != NULL)
6522                                 indices[i] = n1;
6523                         n1++;
6524                 }
6525         }
6526         IntGroupIteratorRelease(&iter);
6527
6528         /*  Create bonds  */
6529         for (i = n0; i < n1; i++) {
6530                 Int b[2], j;
6531                 ap = ATOM_AT_INDEX(mp->atoms, i);
6532                 if (MoleculeGetSymopForTransform(mp, tr, &ap->symop, 0) == 0) {
6533                         /*  For each connected atom, look for the transformed atom  */
6534                         Int *cp;
6535                         ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6536                         cp = AtomConnectData(&ap2->connect);
6537                         n2 = ap2->connect.count;
6538                         for (n = 0; n < n2; n++) {
6539                                 nr = ATOM_AT_INDEX(mp->atoms, cp[n])->r;
6540                                 TransformVec(&nr, tr, &nr);
6541                                 for (j = 0, ap2 = mp->atoms; j < mp->natoms; j++, ap2 = ATOM_NEXT(ap2)) {
6542                                         if (ap2->symbase == cp[n] && SYMOP_EQUAL(ap->symop, ap2->symop))
6543                                                 break;
6544                                         VecSub(dr, nr, ap2->r);
6545                                         if (ap2->atomicNumber == ap->atomicNumber && VecLength2(dr) < 1e-6)
6546                                                 break;
6547                                 }
6548                                 if (j < mp->natoms) {
6549                                         /*  Bond i-j is created  */
6550                                         b[0] = i;
6551                                         b[1] = j;
6552                                         if (MoleculeLookupBond(mp, b[0], b[1]) < 0)
6553                                                 MoleculeAddBonds(mp, 1, b, NULL, 1);
6554                                 }
6555                         }
6556                 }
6557         }
6558                                         
6559 #if 0
6560         for (i = 0; i < n0; i++) {
6561                 int b[2];
6562                 Int *cp;
6563                 b[0] = table[i];
6564                 if (b[0] < 0 || b[0] == i)
6565                         continue;
6566                 ap = ATOM_AT_INDEX(mp->atoms, i);
6567                 cp = AtomConnectData(&ap->connect);
6568                 for (n = 0; n < ap->connect.count; n++) {
6569                         b[1] = table[cp[n]];
6570                         if (b[1] < 0)
6571                                 continue;
6572                         if (b[1] > n0 && b[0] > b[1])
6573                                 continue;
6574                         if (MoleculeLookupBond(mp, b[0], b[1]) >= 0)
6575                                 continue;
6576                         MoleculeAddBonds(mp, 1, b, NULL, 1);
6577                 }
6578         }
6579 #endif
6580         mp->needsMDRebuild = 1;
6581         __MoleculeUnlock(mp);
6582         free(table);
6583         return n1 - n0;  /*  The number of added atoms  */
6584 }
6585
6586 /*  Recalculate the coordinates of symmetry expanded atoms.
6587     (Also recalculate the positions of pi-anchor atoms)
6588         Returns the number of affected atoms.
6589     If group is non-NULL, only the expanded atoms whose base atoms are in the
6590     given group are considered.
6591         If groupout and vpout are non-NULL, the indices of the affected atoms
6592         and the original positions are returned (for undo operation).
6593         The pointers returned in *groupout and *vpout must be released and 
6594         free()'ed by the caller  */
6595 int
6596 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
6597 {
6598         int i, count;
6599         Atom *ap, *bp;
6600         Vector nr, dr;
6601         IntGroup *ig = NULL;
6602         Vector *vp = NULL;
6603         
6604         if (mp == NULL || mp->natoms == 0)
6605                 return 0;
6606
6607         __MoleculeLock(mp);
6608         count = 0;
6609         if (mp->nsyms != 0) {
6610                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6611                         if (!SYMOP_ALIVE(ap->symop))
6612                                 continue;
6613                         if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
6614                                 continue;
6615                         bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6616                         MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
6617                         VecSub(dr, nr, ap->r);
6618                         if (VecLength2(dr) < 1e-20)
6619                                 continue;
6620                         if (groupout != NULL) {
6621                                 if (ig == NULL) {
6622                                         ig = IntGroupNew();
6623                                         vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
6624                                 }
6625                                 vp[count] = ap->r;
6626                                 IntGroupAdd(ig, i, 1);
6627                         }
6628                         ap->r = nr;
6629                         count++;
6630                 }
6631         }
6632         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6633                 Int *ip, j, n;
6634                 if (ap->anchor == NULL)
6635                         continue;
6636                 if (group != NULL) {
6637                         if (IntGroupLookup(group, i, NULL) == 0) {
6638                                 n = ap->anchor->connect.count;
6639                                 ip = AtomConnectData(&(ap->anchor->connect));
6640                                 for (j = 0; j < n; j++) {
6641                                         if (IntGroupLookup(group, ip[j], NULL) != 0)
6642                                                 break;
6643                                 }
6644                                 if (j == n)
6645                                         continue;  /*  This pi-anchor should not be modified  */
6646                         }
6647                 }
6648                 nr = ap->r;
6649                 MoleculeCalculatePiAnchorPosition(mp, i);
6650                 VecSub(dr, nr, ap->r);
6651                 if (VecLength2(dr) < 1e-20) {
6652                         ap->r = nr;  /*  No change  */
6653                         continue;
6654                 }
6655                 if (groupout != NULL) {
6656                         if (ig == NULL) {
6657                                 ig = IntGroupNew();
6658                                 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
6659                         }
6660                         vp[count] = nr;
6661                         IntGroupAdd(ig, i, 1);
6662                 }
6663                 count++;
6664         }
6665         mp->needsMDCopyCoordinates = 1;
6666         __MoleculeUnlock(mp);
6667
6668         if (count > 0) {
6669                 if (groupout != NULL && vpout != NULL) {
6670                         *groupout = ig;
6671                         *vpout = (Vector *)realloc(vp, sizeof(Vector) * count);
6672                 } else {
6673                         IntGroupRelease(ig);
6674                         free(vp);
6675                 }
6676         } else {
6677                 if (groupout != NULL && vpout != NULL) {
6678                         *groupout = NULL;
6679                         *vpout = NULL;
6680                 }
6681         }
6682         return count;
6683 }
6684
6685 #pragma mark ====== Show/hide atoms ======
6686
6687 static void
6688 sMoleculeNotifyChangeAppearance(Molecule *mp)
6689 {
6690         /*  TODO: Finer control of notification types may be necessary  */
6691         MoleculeCallback_notifyModification(mp, 0);
6692 }
6693
6694
6695 static void
6696 sMoleculeUnselectHiddenAtoms(Molecule *mp)
6697 {
6698         int i;
6699         if (mp == NULL || mp->selection == NULL)
6700                 return;
6701         for (i = 0; i < mp->natoms; i++) {
6702                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6703                 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
6704                         IntGroupRemove(mp->selection, i, 1);
6705         }
6706         sMoleculeNotifyChangeAppearance(mp);
6707 }
6708
6709 int
6710 MoleculeShowAllAtoms(Molecule *mp)
6711 {
6712         int i;
6713         if (mp == NULL)
6714                 return 0;
6715         for (i = 0; i < mp->natoms; i++) {
6716                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6717                 ap->exflags &= ~kAtomHiddenFlag;
6718         }
6719         sMoleculeNotifyChangeAppearance(mp);
6720         return 1;
6721 }
6722
6723 int
6724 MoleculeShowReverse(Molecule *mp)
6725 {
6726         int i;
6727         if (mp == NULL)
6728                 return 0;
6729         for (i = 0; i < mp->natoms; i++) {
6730                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6731                 ap->exflags ^= kAtomHiddenFlag;
6732         }
6733         sMoleculeUnselectHiddenAtoms(mp);
6734         sMoleculeNotifyChangeAppearance(mp);
6735         return 1;
6736 }
6737
6738 int
6739 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
6740 {
6741         int i;
6742         if (mp == NULL || ig == NULL)
6743                 return 0;
6744         for (i = 0; i < mp->natoms; i++) {
6745                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6746                 if (ap->exflags & kAtomHiddenFlag)
6747                         continue;  /*  Already hidden  */
6748                 if (IntGroupLookupPoint(ig, i) >= 0)
6749                         ap->exflags |= kAtomHiddenFlag;
6750         }
6751         sMoleculeUnselectHiddenAtoms(mp);
6752         sMoleculeNotifyChangeAppearance(mp);
6753         return 1;
6754 }
6755
6756 #pragma mark ====== Reversible Editing ======
6757
6758 /*
6759 static void
6760 sMoleculeNotifyModification(Molecule *mp)
6761 {
6762         **  TODO: Finer control of notification types may be necessary  **
6763         MoleculeCallback_notifyModification(mp, 0);
6764 }
6765 */
6766
6767 /*  Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup  */
6768 int
6769 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
6770 {
6771         int n1, n2, n3, i;
6772         if (where == NULL) {
6773                 /*  Append the new objects at the end  */
6774                 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
6775                 return 0;
6776         }
6777         n1 = IntGroupGetCount(where);  /*  Position to get new object  */
6778         n2 = nobjs;                    /*  Position to get old object  */
6779         n3 = n1 + n2;                  /*  Position to place new/old object  */
6780         for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
6781                 int start = IntGroupGetStartPoint(where, i);
6782                 int end = IntGroupGetEndPoint(where, i);
6783                 if (end < n3) {
6784                         /*  old[end-(n3-n2)..n2-1] is moved to old[end..n3-1]  */
6785                         memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
6786                         n2 = end - (n3 - n2);
6787                         n3 = end;
6788                 }
6789                 /*  new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1]  */
6790                 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
6791                 n3 -= end - start;
6792                 n1 -= end - start;
6793         }
6794         return 0;
6795 }
6796
6797 /*  Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
6798 int
6799 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6800 {
6801         int n1, n2, n3, start, end, i;
6802         if (objs == NULL || where == NULL)
6803                 return 1;  /*  Bad argument  */
6804         n1 = 0;  /*  Position to move remaining elements to */
6805         n2 = 0;  /*  Position to move remaining elements from  */
6806         n3 = 0;  /*  Position to move removed elements to  */
6807         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6808                 end = IntGroupGetEndPoint(where, i);
6809                 if (n2 < start) {
6810                         /*  Move (start - n2) elements from objs[n2] to objs[n1]  */
6811                         if (n1 < n2)
6812                                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
6813                         n1 += start - n2;
6814                         n2 = start;
6815                 }
6816                 /*  Move (end - start) elements from objs[n2] to clip[n3]  */
6817                 if (clip != NULL)
6818                         memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
6819                 n3 += (end - start);
6820                 n2 += (end - start);
6821         }
6822         /*  Move (nobjs - n2) elements from objs[n2] to objs[n1]  */
6823         if (nobjs > n2)
6824                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
6825         return 0;
6826 }
6827
6828 /*  Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
6829 int
6830 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6831 {
6832         int n1, start, end, i;
6833         if (objs == NULL || where == NULL)
6834                 return 1;  /*  Bad argument  */
6835         n1 = 0;  /*  Position to move removed elements to  */
6836         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6837                 end = IntGroupGetEndPoint(where, i);
6838                 /*  Copy (end - start) elements from objs[start] to clip[n1]  */
6839                 if (clip != NULL)
6840                         memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
6841                 n1 += (end - start);
6842         }
6843         return 0;
6844 }
6845
6846 /*  Create a new atom with no bonding information. ap must _not_ be inside the given molecule
6847    (Use AtomDuplicate() first) */
6848 int
6849 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
6850 {
6851     Atom *ap1, *api;
6852         int i;
6853         if (mp == NULL || ap == NULL || mp->noModifyTopology)
6854                 return -1;
6855         __MoleculeLock(mp);
6856         if (pos < 0 || pos >= mp->natoms)
6857                 pos = mp->natoms;
6858         ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
6859         if (ap1 == NULL)
6860                 goto error;  /*  Out of memory  */
6861         ap1 = ATOM_AT_INDEX(mp->atoms, pos);
6862         if (pos < mp->natoms - 1) {
6863                 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6864         }
6865         if (AtomDuplicate(ap1, ap) == NULL) {
6866                 /*  Cannot duplicate: restore the original state  */
6867                 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6868                 mp->natoms--;
6869                 goto error;
6870         }
6871         ap1->connect.count = 0;
6872         if (ap1->resSeq >= mp->nresidues)
6873                 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
6874         if (ap1->resName[0] == 0)
6875           strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
6876         if (ap1->segName[0] == 0)
6877           strncpy(ap1->segName, "MAIN", 4);
6878         if (pos < mp->natoms - 1) {
6879                 /*  Renumber the connect table, bonds, angles, etc. */
6880                 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
6881                         int j;
6882                         Int *cp;
6883                         cp = AtomConnectData(&api->connect);
6884                         for (j = 0; j < api->connect.count; j++) {
6885                                 if (cp[j] >= pos)
6886                                         cp[j]++;
6887                         }
6888                         if (api->anchor != NULL) {
6889                                 cp = AtomConnectData(&api->anchor->connect);
6890                                 for (j = 0; j < api->anchor->connect.count; j++) {
6891                                         if (cp[j] >= pos)
6892                                                 cp[j]++;
6893                                 }
6894                         }
6895                 }
6896                 for (i = 0; i < mp->nbonds * 2; i++) {
6897                         if (mp->bonds[i] >= pos)
6898                                 mp->bonds[i]++;
6899                 }
6900                 for (i = 0; i < mp->nangles * 3; i++) {
6901                         if (mp->angles[i] >= pos)
6902                                 mp->angles[i]++;
6903                 }
6904                 for (i = 0; i < mp->ndihedrals * 4; i++) {
6905                         if (mp->dihedrals[i] >= pos)
6906                                 mp->dihedrals[i]++;
6907                 }
6908                 for (i = 0; i < mp->nimpropers * 4; i++) {
6909                         if (mp->impropers[i] >= pos)
6910                                 mp->impropers[i]++;
6911                 }
6912         }
6913         mp->nframes = -1;  /*  Should be recalculated later  */
6914         MoleculeIncrementModifyCount(mp);
6915         mp->needsMDRebuild = 1;
6916         __MoleculeUnlock(mp);
6917         return pos;
6918 error:
6919         __MoleculeUnlock(mp);
6920         return -1;
6921 }
6922
6923 #if defined(DEBUG)
6924
6925 static int s_error_count;
6926
6927 static int
6928 s_fprintf(FILE *fp, const char *fmt, ...)
6929 {
6930         va_list va;
6931         va_start(va, fmt);
6932         s_error_count++;
6933         return vfprintf(fp, fmt, va);
6934 }
6935
6936 int
6937 MoleculeCheckSanity(Molecule *mol)
6938 {
6939         const char *fail = "Sanity check failure";
6940         Int i, j, *ip, c[4];
6941         Atom *ap;
6942         s_error_count = 0;
6943         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
6944                 if (ap->resSeq >= mol->nresidues)
6945                         s_fprintf(stderr, "%s: atom %d residue %d but nresidues %d\n", fail, i, ap->resSeq, mol->nresidues);
6946                 if (ap->type != 0 && ap->type < kAtomTypeMinimum)
6947                         s_fprintf(stderr, "%s: atom %d atom type %d less than minimum\n", fail, i, ap->type);
6948                 if (ap->atomicNumber < 0 || ap->atomicNumber > 113)
6949                         s_fprintf(stderr, "%s: atom %d atomic number %d\n", fail, i, ap->atomicNumber);
6950                 ip = AtomConnectData(&ap->connect);
6951                 for (j = 0; j < ap->connect.count; j++) {
6952                         if (ip[j] < 0 || ip[j] >= mol->natoms)
6953                                 s_fprintf(stderr, "%s: atom %d connect[%d] = %d out of range\n", fail, i, j, ip[j]);
6954                         if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[j])->connect), i) == 0)
6955                                 s_fprintf(stderr, "%s: atom %d has connect %d but atom %d has no connect %d\n", fail, i, ip[j], ip[j], i);
6956                 }
6957         }
6958         for (i = 0, ip = mol->bonds; i < mol->nbonds; i++, ip += 2) {
6959                 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms)
6960                         s_fprintf(stderr, "%s: bond %d %d-%d out of range\n", fail, i, ip[0], ip[1]);
6961                 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[0])->connect), ip[1]) == 0)
6962                         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]);
6963         }
6964         for (i = 0, ip = mol->angles; i < mol->nangles; i++, ip += 3) {
6965                 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms)
6966                         s_fprintf(stderr, "%s: angle %d %d-%d-%d out of range\n", fail, i, ip[0], ip[1], ip[2]);
6967                 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
6968                 if (c[0] == 0)
6969                         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]);
6970                 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
6971                 if (c[1] == 0)
6972                         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]);
6973                 if (c[0] == 2 && c[1] == 2)
6974                         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]);
6975         }
6976         for (i = 0, ip = mol->dihedrals; i < mol->ndihedrals; i++, ip += 4) {
6977                 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)
6978                         s_fprintf(stderr, "%s: dihedral %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
6979                 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
6980                 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
6981                 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
6982                 if (c[0] == 0)
6983                         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]);
6984                 if (c[1] == 0)
6985                         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]);
6986                 if (c[2] == 0)
6987                         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]);
6988         }
6989         for (i = 0, ip = mol->impropers; i < mol->nimpropers; i++, ip += 4) {
6990                 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)
6991                         s_fprintf(stderr, "%s: improper %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
6992                 c[0] = MoleculeAreAtomsConnected(mol, ip[2], ip[0]);
6993                 c[1] = MoleculeAreAtomsConnected(mol, ip[2], ip[1]);
6994                 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
6995                 if (c[0] == 0)
6996                         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]);
6997                 if (c[1] == 0)
6998                         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]);
6999                 if (c[2] == 0)
7000                         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]);
7001         }
7002         return s_error_count;
7003 }
7004 #endif
7005
7006 /*  Merge two molecules. We use this procedure for all add-atom operations.  */
7007 /*  resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
7008 /*  If nactions and actions are non-NULL, then the corresponding undo actions are created and returned. */
7009 /*  If forUndo is non-zero, then only the atoms are inserted; other information should be inserted
7010     separately by other undo actions.  */
7011 int
7012 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, Int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7013 {
7014         Int nsrc, ndst;
7015         Int i, j, n1, n2, n3, n4, *cp;
7016         Int *new2old, *old2new;
7017         IntGroup *ig;
7018         Atom *ap;
7019         MolAction *act;
7020         
7021         if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
7022                 return 0;  /*  Do nothing  */
7023
7024         if (dst->noModifyTopology)
7025                 return 1;  /*  Prohibited operation  */
7026
7027         if (where != NULL && IntGroupGetCount(where) != src->natoms)
7028                 return 1;  /*  Bad parameter  */
7029
7030         if (nactions != NULL)
7031                 *nactions = 0;
7032         if (actions != NULL)
7033                 *actions = NULL;
7034         act = NULL;
7035
7036         __MoleculeLock(dst);
7037
7038         nsrc = src->natoms;
7039         ndst = dst->natoms;
7040         if (resSeqOffset < 0)
7041                 resSeqOffset = 0;
7042
7043         /*  Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
7044             and ndst..ndst+nsrc-1 are for atoms in src.  */ 
7045         new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
7046         if (new2old == NULL)
7047                 goto panic;
7048         old2new = new2old + ndst + nsrc;
7049         n1 = 0;  /*  dst index  */
7050         n2 = 0;  /*  src index  */
7051         n3 = 0;  /*  "merged" index  */
7052         i = 0;
7053         while (n1 < ndst || n2 < nsrc) {
7054                 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
7055                         n4 = ndst - n1;
7056                 else n4 -= n3;
7057                 /*  n4 elements from dst[n1] will go to merged[n3]  */
7058                 for (j = 0; j < n4; j++) {
7059                         old2new[n1 + j] = n3 + j;
7060                         new2old[n3 + j] = n1 + j;
7061                 }
7062                 n3 += n4;
7063                 n1 += n4;
7064                 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
7065                         n4 = nsrc - n2;
7066                 /*  n4 elements from src[n2] will go to merged[n3]  */
7067                 for (j = 0; j < n4; j++) {
7068                         old2new[ndst + n2 + j] = n3 + j;
7069                         new2old[n3 + j] = ndst + n2 + j;
7070                 }
7071                 n3 += n4;
7072                 n2 += n4;
7073                 i++;
7074         }
7075
7076         /*  Expand the destination array  */
7077         if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
7078                 goto panic;
7079
7080         /*  Move the atoms  */
7081         if (where == NULL) {
7082                 /*  Duplicate atoms to the end of the destination array  */
7083                 for (i = 0; i < nsrc; i++) {
7084                         ap = ATOM_AT_INDEX(dst->atoms, ndst + i);
7085                         if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7086                                 goto panic;
7087                         if (forUndo)  /*  For undo action, all bonds come from another undo action, so connection info are cleared */
7088                                 AtomConnectResize(&ap->connect, 0);
7089                 }
7090         } else {
7091                 /*  Duplicate to a temporary storage and then insert  */
7092                 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
7093                 if (tempatoms == NULL)
7094                         goto panic;
7095                 for (i = 0; i < nsrc; i++) {
7096                         ap = ATOM_AT_INDEX(tempatoms, i);
7097                         if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7098                                 goto panic;
7099                         if (forUndo)  /*  See above  */
7100                                 AtomConnectResize(&ap->connect, 0);                             
7101                 }
7102                 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
7103                         goto panic;
7104                 free(tempatoms);
7105         }
7106         dst->natoms = ndst + nsrc;
7107
7108         /*  Renumber the atom indices in connect[] and symbase, and modify the residue numbers  */
7109         for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7110                 if (new2old[i] < ndst) {
7111                         /*  This atom is from dst  */
7112                         n1 = 0;
7113                 } else {
7114                         /*  This atom is from src  */
7115                         n1 = ndst;  /*  Offset to the internal number  */
7116                         if (ap->resSeq != 0)
7117                                 ap->resSeq += resSeqOffset;  /*  Modify residue number  */
7118                 }
7119                 cp = AtomConnectData(&ap->connect);
7120                 for (j = 0; j < ap->connect.count; j++)
7121                         cp[j] = old2new[cp[j] + n1];
7122                 if (SYMOP_ALIVE(ap->symop))
7123                         ap->symbase = old2new[ap->symbase + n1];
7124                 if (ap->anchor != NULL) {
7125                         cp = AtomConnectData(&ap->anchor->connect);
7126                         for (j = 0; j < ap->anchor->connect.count; j++)
7127                                 cp[j] = old2new[cp[j] + n1];
7128                 }
7129         }
7130         
7131         /*  Move the bonds, angles, dihedrals, impropers  */
7132         for (i = 0; i < 4; i++) {
7133                 Int *nitems, *nitems_src;
7134                 Int **items, **items_src;
7135                 Int nsize;  /*  Number of Ints in one element  */
7136                 switch (i) {
7137                         case 0:
7138                                 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
7139                         case 1:
7140                                 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
7141                         case 2:
7142                                 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
7143                         case 3:
7144                                 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
7145                 }
7146                 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
7147                 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
7148                 if (forUndo) {
7149                         /*  During undo, no bonds etc. are copied from src; they will be taken care later
7150                             by undo actions  */
7151                         n1 = *nitems;
7152                         n2 = 0;
7153                 } else {
7154                         /*  Keep the old number of entries in dst, because it is updated by AssignArray()  */
7155                         n1 = *nitems;
7156                         /*  Also keep the old number of entries in src, in case src and dst point the same molecule  */
7157                         n2 = *nitems_src;
7158                         /*  Expand the array  */
7159                         if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
7160                                 goto panic;
7161                         /*  Copy the items  */
7162                         memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
7163                 }
7164                 /*  Renumber  */
7165                 for (j = 0; j < n1 * nsize; j++)
7166                         (*items)[j] = old2new[(*items)[j]];
7167                 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
7168                         (*items)[j] = old2new[(*items)[j] + ndst];
7169                 if (forUndo == 0 && actions != NULL) {
7170                         ig = IntGroupNewWithPoints(n1, n2, -1);
7171                         switch (i) {
7172                                 case 0: act = MolActionNew(gMolActionDeleteBonds, ig); break;
7173                                 case 1: act = MolActionNew(gMolActionDeleteAngles, ig); break;
7174                                 case 2: act = MolActionNew(gMolActionDeleteDihedrals, ig); break;
7175                                 case 3: act = MolActionNew(gMolActionDeleteImpropers, ig); break;
7176                         }
7177                         IntGroupRelease(ig);
7178                         AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7179                         act = NULL;
7180                 }
7181         }
7182         
7183         /*  Renumber existing parameters  */
7184         if (dst->par != NULL) {
7185                 int type;
7186                 for (type = kFirstParType; type <= kLastParType; type++) {
7187                         UnionPar *up1;
7188                         n1 = ParameterGetCountForType(dst->par, type);
7189                         for (i = 0; i < n1; i++) {
7190                                 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7191                                 ParameterRenumberAtoms(type, up1, ndst, old2new);
7192                         }
7193                 }
7194         }
7195
7196         /*  Merge parameters from src  */
7197         if (src->par != NULL && forUndo == 0) {
7198                 UnionPar *up1, *up2;
7199                 int type;
7200                 if (dst->par == NULL)
7201                         dst->par = ParameterNew();
7202                 else {
7203                         /*  Renumber existing parameters  */
7204                         for (type = kFirstParType; type <= kLastParType; type++) {
7205                                 n1 = ParameterGetCountForType(dst->par, type);
7206                                 for (i = 0; i < n1; i++) {
7207                                         up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7208                                         ParameterRenumberAtoms(type, up1, ndst, old2new);
7209                                 }
7210                         }
7211                 }
7212                 ig = IntGroupNew();
7213                 for (type = kFirstParType; type <= kLastParType; type++) {
7214                         n1 = ParameterGetCountForType(src->par, type);
7215                         n2 = ParameterGetCountForType(dst->par, type);
7216                         if (n1 == 0)
7217                                 continue;
7218                         /*  Determine which parameter should be copied from src to dst  */
7219                         for (i = 0; i < n1; i++) {
7220                                 UInt types[4];
7221                                 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
7222                                 n3 = ParameterGetAtomTypes(type, up1, types);
7223                                 for (j = 0; j < n3; j++) {
7224                                         /*  If it includes explicit atom index, then it should be copied  */
7225                                         if (types[j] < kAtomTypeMinimum) {
7226                                                 IntGroupAdd(ig, i, 1);
7227                                                 break;
7228                                         }
7229                                 }
7230                                 if (j == n3) {
7231                                         for (j = 0; j < n2; j++) {
7232                                                 up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
7233                                                 if (ParameterCompare(up1, up2, type))
7234                                                         break;
7235                                         }
7236                                         if (j >= n2)
7237                                                 /*  This is an unknown parameter; should be copied  */
7238                                                 IntGroupAdd(ig, i, 1);
7239                                 }
7240                         }
7241                         n1 = IntGroupGetCount(ig);
7242                         if (n1 == 0)
7243                                 continue;
7244                         up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
7245                         if (up1 == NULL)
7246                                 goto panic;
7247                         /*  Copy parameters and renumber indices if necessary  */
7248                         for (i = j = 0; i < n1; i++) {
7249                                 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
7250                                 if (up2 == NULL)
7251                                         continue;
7252                                 up1[j] = *up2;
7253                                 ParameterRenumberAtoms(type, up1 + j, nsrc, old2new + ndst);
7254                                 j++;
7255                         }
7256                         /*  Merge parameters  */
7257                         IntGroupClear(ig);
7258                         IntGroupAdd(ig, n2, j);
7259                         if (ParameterInsert(dst->par, type, up1, ig) < j)
7260                                 goto panic;
7261                         if (actions != NULL) {
7262                                 act = MolActionNew(gMolActionDeleteParameters, type, ig);
7263                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7264                                 act = NULL;
7265                         }
7266                         IntGroupClear(ig);
7267                         free(up1);
7268                 }
7269                 IntGroupRelease(ig);
7270         }
7271         
7272         /*  Copy the residues if necessary  */
7273         /*  src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
7274             However, 1+resSeqOffset should not overwrite the existing residue in dst;
7275                 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1].  */
7276         if (forUndo == 0) {
7277                 n1 = dst->nresidues;
7278                 if (1 + resSeqOffset < n1) {
7279                         n2 = n1;
7280                 } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
7281                 if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
7282                         if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
7283                                 goto panic;
7284                         memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
7285                         if (nactions != NULL) {
7286                                 act = MolActionNew(gMolActionChangeNumberOfResidues, n1);
7287                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7288                                 act = NULL;
7289                         }
7290                 }
7291         }
7292
7293         MoleculeCleanUpResidueTable(dst);
7294         
7295         free(new2old);
7296         dst->nframes = -1;  /*  Should be recalculated later  */
7297
7298         MoleculeIncrementModifyCount(dst);
7299         dst->needsMDRebuild = 1;
7300         __MoleculeUnlock(dst);
7301         return 0;
7302
7303   panic:
7304         __MoleculeUnlock(dst);
7305     Panic("Low memory while adding atoms");
7306         return 1;  /*  Not reached  */
7307 }
7308
7309 /*  Unmerge the molecule. If necessary, the undo actions are stored in nactions/actions array.
7310     (The nactions/actions array must be initialized by the caller)  */
7311 static int
7312 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag, Int *nactions, MolAction ***actions, Int forUndo)
7313 {
7314         Int nsrc, ndst, nsrcnew;
7315         Int i, j, n1, n2, n3, n4, *cp;
7316         Int *new2old, *old2new;
7317         IntGroup *move_g, *del_g, *remain_g, *dst_par_g, *remove_par_g;
7318         Molecule *dst;
7319         Atom *ap, *dst_ap;
7320         UnionPar *up;
7321         MolAction *act;
7322
7323         if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
7324                 /*  Do nothing  */
7325                 if (dstp != NULL)
7326                         *dstp = NULL;
7327                 return 0;
7328         }
7329         
7330         if (src->noModifyTopology && moveFlag)
7331                 return 1;  /*  Prohibit editing  */
7332
7333         if ((ndst = IntGroupGetCount(where)) > src->natoms)
7334                 return 1;  /*  Bad parameter  */
7335
7336         __MoleculeLock(src);
7337         
7338         act = NULL;
7339         
7340         nsrc = src->natoms;
7341         nsrcnew = nsrc - ndst;
7342         if (resSeqOffset < 0)
7343                 resSeqOffset = 0;
7344
7345         /*  Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
7346             and nsrcnew..nsrc-1 are for atoms moved into dst.  */ 
7347         new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
7348         if (new2old == NULL)
7349                 goto panic;
7350         old2new = new2old + nsrc;
7351         n1 = 0;  /*  src index  */
7352         n2 = 0;  /*  dst index  */
7353         n3 = 0;  /*  src index after "unmerge"  */
7354         i = 0;
7355         while (n1 < nsrc || n2 < ndst) {
7356                 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
7357                         n4 = nsrc - n1;
7358                 else n4 -= n1;
7359                 /*  n4 elements from src[n1] will go to unmerged[n3]  */
7360                 for (j = 0; j < n4; j++) {
7361                         old2new[n1 + j] = n3 + j;
7362                         new2old[n3 + j] = n1 + j;
7363                 }
7364                 n3 += n4;
7365                 n1 += n4;
7366                 if ((n4 = IntGroupGetInterval(where, i)) < 0)
7367                         n4 = nsrc - n1;
7368                 /*  n4 elements from src[n1] will go to dst[n2]  */
7369                 for (j = 0; j < n4; j++) {
7370                         old2new[n1 + j] = nsrcnew + n2 + j;
7371                         new2old[nsrcnew + n2 + j] = n1 + j;
7372                 }
7373                 n1 += n4;
7374                 n2 += n4;
7375                 i++;
7376         }
7377
7378         /*  Atoms to remain in the source group  */
7379         if (moveFlag) {
7380                 remain_g = IntGroupNewWithPoints(0, nsrc, -1);
7381                 IntGroupRemoveIntGroup(remain_g, where);
7382         } else remain_g = NULL;
7383         
7384         /*  Find parameters to be moved to the dst (dst_par_g), and to be removed from the src (remove_par_g) */
7385         if (src->par != NULL) {
7386                 dst_par_g = IntGroupNew();
7387                 if (moveFlag)
7388                         remove_par_g = IntGroupNew();
7389                 else remove_par_g = NULL;
7390                 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7391                         n2 = ParameterGetCountForType(src->par, n1);
7392                         if (n2 == 0)
7393                                 continue;
7394                         for (i = 0; i < n2; i++) {
7395                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7396                                 if (ParameterIsRelevantToAtomGroup(n1, up, src->atoms, where)) {
7397                                         /*  This parameter is to be copied to dst  */
7398                                         IntGroupAdd(dst_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7399                                 }
7400                                 if (moveFlag && !ParameterIsRelevantToAtomGroup(n1, up, src->atoms, remain_g)) {
7401                                         /*  This parameter is to be removed  */
7402                                         IntGroupAdd(remove_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7403                                 }
7404                         }
7405                 }
7406         } else dst_par_g = remove_par_g = NULL;
7407         
7408         /*  Pi anchors should be modified if the anchor and its component atoms become separated between
7409             src anc dst  */
7410         if (moveFlag) {
7411                 Int ibufsize, *ibuf, flag_i, flag_j;
7412                 ibufsize = 8;
7413                 ibuf = (Int *)malloc(sizeof(Int) * ibufsize);
7414                 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7415                         if (ap->anchor == NULL)
7416                                 continue;
7417                         flag_i = (old2new[i] < nsrcnew);
7418                         cp = AtomConnectData(&ap->anchor->connect);
7419                         for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7420                                 flag_j = (old2new[cp[j]] < nsrcnew);
7421                                 if (flag_i == flag_j) {
7422                                         if (n1 >= ibufsize) {
7423                                                 ibufsize += 8;
7424                                                 ibuf = (Int *)realloc(ibuf, sizeof(Int) * ibufsize);
7425                                         }
7426                                         ibuf[n1++] = cp[j];
7427                                 }
7428                         }
7429                         if (n1 < j) {
7430                                 /*  Need to modify the pi anchor list  */
7431                                 if (n1 <= 1)
7432                                         n1 = 0;
7433                                 MolActionCreateAndPerform(src, SCRIPT_ACTION("isI"), "set_atom_attr", i, "anchor_list", n1, ibuf);
7434                         }
7435                 }
7436         }
7437         
7438         /*  Make a new molecule  */
7439         if (dstp != NULL) {
7440                 dst = MoleculeNew();
7441                 if (dst == NULL)
7442                         goto panic;
7443                 /*  Expand the destination array  */
7444                 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
7445                         goto panic;
7446                 dst_ap = dst->atoms;
7447         } else {
7448                 dst = NULL;
7449                 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
7450                 if (dst_ap == NULL)
7451                         goto panic;
7452         }
7453         
7454         /*  Move the atoms  */
7455         if (moveFlag) {
7456                 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
7457                         goto panic;
7458                 src->natoms = nsrcnew;
7459                 if (dst == NULL) {
7460                         /*  The atom record must be deallocated correctly  */
7461                         for (i = 0; i < ndst; i++)
7462                                 AtomClean(ATOM_AT_INDEX(dst_ap, i));
7463                 }
7464         } else {
7465                 if (dst != NULL) {
7466                         for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
7467                                 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
7468                 }
7469         }
7470         
7471         if (dst == NULL) {
7472                 /*  The dummy destination array is no longer needed  */
7473                 free(dst_ap);
7474                 dst_ap = NULL;
7475         }
7476         
7477         /*  Renumber the atom indices in connect[] (src) */
7478         if (moveFlag) {
7479                 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7480                         cp = AtomConnectData(&ap->connect);
7481                         for (j = n1 = 0; j < ap->connect.count; j++) {
7482                                 n2 = old2new[cp[j]];
7483                                 if (n2 < nsrcnew)
7484                                         cp[n1++] = n2;
7485                         }
7486                         AtomConnectResize(&ap->connect, n1);
7487                         if (ap->anchor != NULL) {
7488                                 cp = AtomConnectData(&ap->anchor->connect);
7489                                 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7490                                         n2 = old2new[cp[j]];
7491                                         if (n2 < nsrcnew)
7492                                                 cp[n1++] = n2;
7493                                 }
7494                                 if (n1 != ap->anchor->connect.count) {
7495                                         /*  This should not happen!!  */
7496                                         AtomConnectResize(&ap->anchor->connect, n1);
7497                                         fprintf(stderr, "Internal error in sMoleculeUnmergeSub (line %d)\n", __LINE__);
7498                                         if (n1 == 0) {
7499                                                 free(ap->anchor->coeffs);
7500                                                 free(ap->anchor);
7501                                                 ap->anchor = NULL;
7502                                         }
7503                                 }
7504                         }
7505                 }
7506         }
7507         
7508         /*  Renumber the atom indices in connect[] (dst)  */
7509         if (dst != NULL) {
7510                 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7511                         if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
7512                                 ap->resSeq -= resSeqOffset;
7513                         else ap->resSeq = 0;
7514                         cp = AtomConnectData(&ap->connect);
7515                         for (j = n1 = 0; j < ap->connect.count; j++) {
7516                                 n2 = old2new[cp[j]] - nsrcnew;
7517                                 if (n2 >= 0)
7518                                         cp[n1++] = n2;
7519                         }
7520                         AtomConnectResize(&ap->connect, n1);
7521                         if (ap->anchor != NULL) {
7522                                 cp = AtomConnectData(&ap->anchor->connect);
7523                                 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7524                                         n2 = old2new[cp[j]] - nsrcnew;
7525                                         if (n2 >= 0)
7526                                                 cp[n1++] = n2;
7527                                 }
7528                                 if (n1 != ap->anchor->connect.count) {
7529                                         /*  This can happen, and the anchor info is silently modified  */
7530                                         if (n1 <= 1) {
7531                                                 AtomConnectResize(&ap->anchor->connect, 0);
7532                                                 free(ap->anchor->coeffs);
7533                                                 free(ap->anchor);
7534                                                 ap->anchor = NULL;
7535                                         } else {
7536                                                 Double d;
7537                                                 AtomConnectResize(&ap->anchor->connect, n1);
7538                                                 d = 0.0;
7539                                                 for (j = 0; j < n1; j++)
7540                                                         d += ap->anchor->coeffs[j];
7541                                                 for (j = 0; j < n1; j++)
7542                                                         ap->anchor->coeffs[j] /= d;
7543                                                 MoleculeCalculatePiAnchorPosition(dst, i);
7544                                         }
7545                                 }
7546                         }
7547                 }
7548         }
7549
7550         /*  Separate the bonds, angles, dihedrals, impropers  */
7551         /*  TODO: Improper torsions should also be copied!  */
7552         move_g = IntGroupNew();
7553         if (move_g == NULL)
7554                 goto panic;
7555         for (i = 3; i >= 0; i--) {
7556                 Int *nitems, *nitems_dst;
7557                 Int **items, **items_dst;
7558                 Int nsize;  /*  Number of Ints in one element  */
7559                 unsigned char *counts;
7560                 del_g = IntGroupNew();
7561                 switch (i) {
7562                         case 0:
7563                                 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
7564                         case 1:
7565                                 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
7566                         case 2:
7567                                 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
7568                         case 3:
7569                                 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
7570                         default:
7571                                 nitems = NULL; items = NULL; nsize = 0; break;  /*  Not reached  */
7572                 }
7573                 if (dst != NULL) {
7574                         nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
7575                         items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
7576                 } else {
7577                         nitems_dst = NULL;
7578                         items_dst = NULL;
7579                 }
7580                 counts = (unsigned char *)calloc(1, *nitems);
7581                 /*  Find the entries that should be moved to dst  */
7582                 n2 = 0;
7583                 for (j = 0; j < *nitems * nsize; j++) {
7584                         n1 = old2new[(*items)[j]];
7585                         if (n1 >= nsrcnew)
7586                                 counts[j / nsize]++; /* Count the atom belonging to dst */ 
7587                 }
7588                 for (j = n2 = n3 = 0; j < *nitems; j++) {
7589                         if (counts[j] > 0) {
7590                                 /*  Remove from src  */
7591                                 n2++;
7592                                 if (IntGroupAdd(del_g, j, 1) != 0)
7593                                         goto panic;
7594                                 if (counts[j] == nsize) {
7595                                         /*  Move to dst  */
7596                                         n3++;
7597                                         if (IntGroupAdd(move_g, j, 1) != 0)
7598                                                 goto panic;
7599                                 }
7600                         }
7601                 }
7602                 if (n2 > 0) {
7603                         /*  Expand the destination array  */
7604                         if (items_dst != NULL && n3 > 0) {
7605                                 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
7606                                         goto panic;
7607                                 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
7608                                         goto panic;
7609                         }
7610                         /*  Remove from src  */
7611                         if (moveFlag && forUndo == 0) {
7612                                 if (nactions != NULL) {
7613                                         Int k, *ip;
7614                                         ip = (Int *)malloc(sizeof(Int) * nsize * n2);
7615                                         for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
7616                                                 memmove(ip + j * nsize, *items + k * nsize, sizeof(Int) * nsize);
7617                                         switch (i) {
7618                                                 case 0:
7619                                                         act = MolActionNew(gMolActionAddBondsForUndo, n2 * nsize, ip, del_g); break;
7620                                                 case 1:
7621                                                         act = MolActionNew(gMolActionAddAngles, n2 * nsize, ip, del_g); break;
7622                                                 case 2:
7623                                                         act = MolActionNew(gMolActionAddDihedrals, n2 * nsize, ip, del_g); break;
7624                                                 case 3:
7625                                                         act = MolActionNew(gMolActionAddImpropers, n2 * nsize, ip, del_g); break;
7626                                         }
7627                                         if (act != NULL) {
7628                                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7629                                                 act = NULL;
7630                                         }
7631                                         free(ip);
7632                                 }
7633                                 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
7634                                         goto panic;
7635                                 (*nitems) -= n2;
7636                         }
7637                 }
7638                 /*  Renumber the entries  */
7639                 if (moveFlag) {
7640                         for (j = 0; j < *nitems * nsize; j++) {
7641                                 (*items)[j] = old2new[(*items)[j]];
7642                         }
7643                 }
7644                 if (items_dst != NULL) {
7645                         for (j = 0; j < *nitems_dst * nsize; j++) {
7646                                 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
7647                         }
7648                 }
7649                 free(counts);
7650                 IntGroupClear(move_g);
7651                 IntGroupRelease(del_g);
7652         }
7653         IntGroupRelease(move_g);
7654         
7655         /*  Copy the residues  */
7656         if (dst != NULL) {
7657                 /*  src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset)  */
7658                 n1 = src->nresidues - resSeqOffset;  /*  This will be dst->nresidues (if >0)  */
7659                 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
7660                         goto panic;
7661                 if (n1 > 1) {
7662                         memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
7663                 }
7664         }
7665
7666         /*  Copy the parameters to dst */
7667         if (dst != NULL && dst_par_g != NULL && (n2 = IntGroupGetCount(dst_par_g)) > 0) {
7668                 IntGroup *dst_new_g = IntGroupNew();
7669                 Int dst_par_count[kLastParType - kFirstParType + 1];
7670                 if (dst_new_g == NULL)
7671                         goto panic;
7672                 for (i = 0; i <= kLastParType - kFirstParType; i++)
7673                         dst_par_count[i] = 0;
7674                 up = (UnionPar *)calloc(sizeof(UnionPar), n2);
7675                 if (up == NULL)
7676                         goto panic;
7677                 if (ParameterCopy(src->par, kFirstParType, up, dst_par_g) < n2)
7678                         goto panic;
7679                 /*  Renumber the explicit atom indices  */
7680                 for (i = 0; i < nsrc; i++)
7681                         old2new[i] -= nsrcnew;  /*  new indices for atoms in dst; otherwise negative numbers  */
7682                 for (i = 0; i < n2; i++) {
7683                         /*  Renumber the indices, and count the number of parameters for each type  */
7684                         n1 = kFirstParType + IntGroupGetNthPoint(dst_par_g, i) / kParameterIndexOffset;
7685                         dst_par_count[n1 - kFirstParType]++;
7686                         ParameterRenumberAtoms(n1, up + i, nsrc, old2new);
7687                 }
7688                 for (i = 0; i < nsrc; i++)
7689                         old2new[i] += nsrcnew;
7690                 if (dst->par == NULL)
7691                         dst->par = ParameterNew();
7692                 for (i = 0; i <= kLastParType - kFirstParType; i++) {
7693                         if (dst_par_count[i] > 0)
7694                                 IntGroupAdd(dst_new_g, i * kParameterIndexOffset, dst_par_count[i]);
7695                 }
7696                 if (ParameterInsert(dst->par, kFirstParType, up, dst_new_g) < n2)
7697                         goto panic;
7698                 free(up);
7699                 IntGroupRelease(dst_new_g);
7700         }
7701         IntGroupRelease(dst_par_g);
7702
7703         /*  Remove the unused parameter. Note: the parameters that are in remove_par_g and not in 
7704             dst_par_g will disappear. To support undo, these parameters should be taken care separately.  */
7705         if (forUndo == 0 && remove_par_g != NULL && (n2 = IntGroupGetCount(remove_par_g)) > 0) {
7706                 UnionPar *up = (UnionPar *)malloc(sizeof(UnionPar) * n2);
7707                 ParameterDelete(src->par, kFirstParType, up, remove_par_g);
7708                 if (nactions != NULL) {
7709                         act = MolActionNew(gMolActionAddParameters, kFirstParType, remove_par_g, n2, up);
7710                         AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7711                         act = NULL;
7712                 }
7713                 free(up);
7714         }
7715         IntGroupRelease(remove_par_g);
7716         
7717         /*  Renumber the parameter records remaining in the src  */
7718         if (moveFlag) {
7719                 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7720                         n2 = ParameterGetCountForType(src->par, n1);
7721                         for (i = 0; i < n2; i++) {
7722                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7723                                 ParameterRenumberAtoms(n1, up, nsrc, old2new);
7724                         }
7725                 }
7726         }
7727
7728         /*  Clean up  */
7729         IntGroupRelease(remain_g);
7730         MoleculeCleanUpResidueTable(src);
7731         if (dst != NULL)
7732                 MoleculeCleanUpResidueTable(dst);
7733         free(new2old);
7734
7735         src->nframes = -1;  /*  Should be recalculated later  */
7736         if (dst != NULL)
7737                 dst->nframes = -1;  /*  Should be recalculated later  */
7738
7739         
7740         if (dstp != NULL)
7741                 *dstp = dst;
7742
7743         MoleculeIncrementModifyCount(src);
7744         src->needsMDRebuild = 1;
7745         __MoleculeUnlock(src);
7746         
7747         return 0;
7748
7749   panic:
7750         __MoleculeUnlock(src);
7751 /*    Panic("Low memory while removing atoms"); */
7752         return -1;
7753 }
7754
7755 /*  Separate molecule into two parts. The atoms specified by 'where' are moved
7756     from src to a new molecule, which is returned as *dstp. Dstp can be NULL, 
7757         in which case the moved atoms are discarded.  */
7758 int
7759 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7760 {
7761         return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1, nactions, actions, forUndo);
7762 }
7763
7764 /*  Extract atoms from a given molecule into two parts. The atoms specified by 
7765         'where' are copied from src to a new molecule, which is returned as *dstp.
7766     If dummyFlag is non-zero, then the atoms that are not included in the group 
7767         but are connected to any atoms in the group are converted to "dummy" atoms 
7768         (i.e. with element "Du" and names beginning with an underscore) and included 
7769         in the new molecule object.  */
7770 int
7771 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
7772 {
7773         int retval;
7774
7775         /*  Extract the fragment  */
7776         retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0, NULL, NULL, 0);
7777         if (retval != 0)
7778                 return retval;
7779
7780         if (dummyFlag) {
7781
7782                 /*  Search bonds crossing the molecule border  */
7783                 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
7784                 if (ig != NULL) {
7785                         IntGroupIterator iter;
7786                         Int i, idx;
7787                         idx = 1;
7788                         IntGroupIteratorInit(ig, &iter);
7789                         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7790                                 /*  The atoms at the border  */
7791                                 Int n1, n2, nn[3];
7792                                 Atom a, *ap;
7793                                 n1 = src->bonds[i*2];
7794                                 n2 = src->bonds[i*2+1];
7795                                 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
7796                                         int w = n1;
7797                                         n1 = n2;
7798                                         n2 = w;
7799                                         if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
7800                                                 continue;  /*  Actually this is an internal error  */
7801                                 }
7802                                 /*  n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule  */
7803                                 /*  Create a new dummy atom with the same segment/residue info with n1
7804                                     and the same position as n2  */
7805                                 ap = ATOM_AT_INDEX(src->atoms, n1);
7806                                 memset(&a, 0, gSizeOfAtomRecord);
7807                                 a.segSeq = ap->segSeq;
7808                                 memmove(a.segName, ap->segName, 4);
7809                                 a.resSeq = ap->resSeq;
7810                                 memmove(a.resName, ap->resName, 4);
7811                                 ElementToString(0, a.element);  /*  "Du"  */
7812                                 snprintf(a.aname, 4, "_%d", idx++);
7813                                 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
7814                                 /*  Add the dummy atom to the new molecule; nn[1] is the index
7815                                     of the new dummy atom in the new molecule  */
7816                                 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
7817                                 /*  Connect nn1 and nn2  */
7818                                 nn[2] = kInvalidIndex;
7819                                 MoleculeAddBonds(*dstp, 1, nn, NULL, 1);
7820                         }
7821                         IntGroupIteratorRelease(&iter);
7822                         IntGroupRelease(ig);
7823                 }
7824         }
7825         
7826         return 0;
7827 }
7828
7829 int
7830 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds, IntGroup *where, Int autoGenerate)
7831 {
7832         Int nangles, ndihedrals;
7833         Int *angles, *dihedrals;
7834         Int i, j, k, kk, n1, n2;
7835         Int *cp1, *cp2;
7836         Int temp[4];
7837         Atom *ap1, *ap2, *ap3;
7838         
7839         if (mp == NULL || bonds == NULL || nbonds <= 0)
7840                 return 0;
7841         if (mp->noModifyTopology)
7842                 return -4;  /*  Prohibited operation  */
7843
7844         /*  Note: Duplicates and validity are not checked (the caller must do that)  */
7845
7846         __MoleculeLock(mp);
7847
7848         n1 = mp->nbonds;
7849         if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, n1 + nbonds - 1, NULL) == NULL
7850                 || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nbonds, sizeof(Int) * 2, where) != 0) {
7851                 __MoleculeUnlock(mp);
7852                 return -4;  /*  Out of memory  */
7853         }
7854         
7855         angles = dihedrals = NULL;
7856         nangles = ndihedrals = 0;
7857         
7858         /*  Add connects[], and angles/dihedrals (if autoGenerate is true)  */
7859         for (i = 0; i < nbonds; i++) {
7860                 
7861                 /*  One entry at time  */
7862                 /*  (Otherwise, duplicate entries of angles and dihedrals result)  */
7863                 n1 = bonds[i * 2];
7864                 n2 = bonds[i * 2 + 1];
7865                 
7866                 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
7867                 AtomConnectInsertEntry(&ap1->connect, -1, n2);
7868                 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
7869                 AtomConnectInsertEntry(&ap2->connect, -1, n1);
7870         
7871                 /*  Add angles and dihedrals  */
7872                 if (autoGenerate) {
7873                         AtomConnect *ac1, *ac2;
7874                         if (ap1->anchor == NULL || ap2->anchor == NULL) {
7875                                 /*  N1-N2-{XY} or N2-N1-{XY} angles (X: connected atom, Y: constitute atom of pi-anchor)  */
7876                                 for (j = 0; j < 4; j++) {
7877                                         switch (j) {
7878                                                 case 0: temp[0] = n1; temp[1] = n2; ac1 = &ap2->connect; break;  /* N1-N2-X */
7879                                                 case 1: if (ap2->anchor == NULL) continue; else ac1 = &ap2->anchor->connect; break; /* N1-N2-Y */
7880                                                 case 2: temp[0] = n2; temp[1] = n1; ac1 = &ap1->connect; break;  /* N2-N1-X */
7881                                                 case 3: if (ap1->anchor == NULL) continue; else ac1 = &ap1->anchor->connect; break; /* N2-N1-Y */
7882                                         }
7883                                         cp1 = AtomConnectData(ac1);
7884                                         for (k = 0; k < ac1->count; k++) {
7885                                                 temp[2] = cp1[k];
7886                                                 if (temp[2] == temp[0])
7887                                                         continue;
7888                                                 ap3 = ATOM_AT_INDEX(mp->atoms, temp[2]);
7889                                                 if (ap3->anchor != NULL) {
7890                                                         /*  Avoid X-anchor-anchor angle (anchor-X-anchor is allowed)  */
7891                                                         if ((j < 2 && ap2->anchor != NULL) || (j >= 2 && ap1->anchor != NULL))
7892                                                                 continue;
7893                                                 }
7894                                                 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7895                                                         goto panic;
7896                                                 /*  Dihedrals N1-N2-X-X or N2-N1-X-X  */
7897                                                 if (j == 1 || j == 3)
7898                                                         continue;
7899                                                 cp2 = AtomConnectData(&ap3->connect);
7900                                                 for (kk = 0; kk < ap3->connect.count; kk++) {
7901                                                         temp[3] = cp2[kk];
7902                                                         if (temp[3] == temp[0] || temp[3] == temp[1])
7903                                                                 continue;
7904                                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7905                                                                 goto panic;
7906                                                 }
7907                                         }
7908                                 }
7909                         }
7910                         /*  X-N1-N2-X angles  */
7911                         if (ap1->anchor == NULL)
7912                                 ac1 = &ap1->connect;
7913                         else ac1 = &ap1->anchor->connect;
7914                         if (ap2->anchor == NULL)
7915                                 ac2 = &ap2->connect;
7916                         else ac2 = &ap2->anchor->connect;
7917                         temp[1] = n1;
7918                         temp[2] = n2;
7919                         cp1 = AtomConnectData(ac1);
7920                         cp2 = AtomConnectData(ac2);
7921                         for (j = 0; j < ac1->count; j++) {
7922                                 temp[0] = cp1[j];
7923                                 if (temp[0] == temp[2])
7924                                         continue;
7925                                 if (ATOM_AT_INDEX(mp->atoms, temp[0])->anchor != NULL)
7926                                         continue;
7927                                 for (k = 0; k < ac2->count; k++) {
7928                                         temp[3] = cp2[k];
7929                                         if (temp[3] == temp[0] || temp[3] == temp[1])
7930                                                 continue;
7931                                         if (ATOM_AT_INDEX(mp->atoms, temp[3])->anchor != NULL)
7932                                                 continue;
7933                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7934                                                 goto panic;
7935                                 }
7936                         }
7937                 }
7938         }
7939         
7940         if (angles != NULL) {
7941                 temp[0] = kInvalidIndex;
7942                 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7943                         goto panic;
7944                 MoleculeAddAngles(mp, angles, NULL);
7945                 free(angles);
7946         }
7947         if (dihedrals != NULL) {
7948                 temp[0] = kInvalidIndex;
7949                 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7950                         goto panic;
7951                 MoleculeAddDihedrals(mp, dihedrals, NULL);
7952                 free(dihedrals);
7953         }
7954
7955         MoleculeIncrementModifyCount(mp);
7956         mp->needsMDRebuild = 1;
7957         __MoleculeUnlock(mp);
7958
7959         return nbonds;
7960
7961   panic:
7962         __MoleculeUnlock(mp);
7963         Panic("Low memory while adding bonds");
7964         return -1;  /*  Not reached  */
7965 }
7966
7967 /*  Delete bonds  */
7968 /*  The deleted angles and dihedrals are stored in outRemoval.  */
7969 /*  (*outRemoval) is an array of integers, containing:
7970       [0..na*3-1]: the angle indices
7971       [na*3..na*3+nd*4-1]: the dihedral indices
7972           [na*3+nd*4..na*3+nd*4+ni*4-1]: the improper indices
7973     *outRemovedPos is an intgroup denoting the positions of the removed angles/dihedrals/impropers.
7974           the angle indices are included as they are,
7975       the dihedral indices are offset by ATOMS_MAX_NUMBER,
7976       the improper indices are offset by ATOMS_MAX_NUMBER*2.
7977     Note: the removed bond indices are not returned, because the caller should already know them.  */
7978 int
7979 MoleculeDeleteBonds(Molecule *mp, Int *bonds, IntGroup *where, Int **outRemoved, IntGroup **outRemovedPos)
7980 {
7981         Int i, j, n1, n2, nw;
7982         Int *ip, *jp, na, nd, ni;
7983         IntGroup *ag, *dg, *ig;
7984         Atom *ap;
7985         IntGroupIterator iter;
7986
7987         if (mp == NULL)
7988                 return 0;
7989         if (mp->noModifyTopology)
7990                 return -4;  /*  Prohibited operation  */
7991
7992         __MoleculeLock(mp);
7993
7994         /*  Update connects[]  */
7995         IntGroupIteratorInit(where, &iter);
7996         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7997                 n1 = mp->bonds[i * 2];
7998                 n2 = mp->bonds[i * 2 + 1];
7999                 ap = ATOM_AT_INDEX(mp->atoms, n1);
8000                 ip = AtomConnectData(&ap->connect);
8001                 for (j = 0; j < ap->connect.count; j++) {
8002                         if (ip[j] == n2) {
8003                                 AtomConnectDeleteEntry(&ap->connect, j);
8004                                 break;
8005                         }
8006                 }
8007                 ap = ATOM_AT_INDEX(mp->atoms, n2);
8008                 ip = AtomConnectData(&ap->connect);
8009                 for (j = 0; j < ap->connect.count; j++) {
8010                         if (ip[j] == n1) {
8011                                 AtomConnectDeleteEntry(&ap->connect, j);
8012                                 break;
8013                         }
8014                 }
8015         }
8016         
8017         /*  Remove bonds, angles, dihedrals, impropers  */
8018         ag = dg = ig = NULL;
8019         na = nd = ni = 0;
8020         
8021         nw = IntGroupGetCount(where);
8022         jp = (Int *)malloc(sizeof(Int) * nw * 2);
8023         j = 0;
8024         IntGroupIteratorReset(&iter);
8025         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8026                 jp[j++] = mp->bonds[i * 2];
8027                 jp[j++] = mp->bonds[i * 2 + 1];
8028         }
8029         IntGroupIteratorRelease(&iter);
8030
8031         for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8032                 for (j = 0; j < nw; j++) {
8033                         n1 = jp[j * 2];
8034                         n2 = jp[j * 2 + 1];
8035                         if ((ip[0] == n1 && ip[1] == n2)
8036                                 || (ip[1] == n1 && ip[0] == n2)
8037                                 || (ip[1] == n1 && ip[2] == n2)
8038                                 || (ip[2] == n1 && ip[1] == n2)) {
8039                                 if (ag == NULL)
8040                                         ag = IntGroupNew();
8041                                 if (IntGroupAdd(ag, i, 1) != 0)
8042                                         goto panic;
8043                                 na++;
8044                                 break;
8045                         }
8046                 }
8047         }
8048         for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8049                 for (j = 0; j < nw; j++) {
8050                         n1 = jp[j * 2];
8051                         n2 = jp[j * 2 + 1];
8052                         if ((ip[0] == n1 && ip[1] == n2)
8053                          || (ip[1] == n1 && ip[0] == n2)
8054                          || (ip[1] == n1 && ip[2] == n2)
8055                          || (ip[2] == n1 && ip[1] == n2)
8056                          || (ip[2] == n1 && ip[3] == n2)
8057                          || (ip[3] == n1 && ip[2] == n2)) {
8058                                 if (dg == NULL)
8059                                         dg = IntGroupNew();
8060                                 if (IntGroupAdd(dg, i, 1) != 0)
8061                                         goto panic;
8062                                 nd++;
8063                                 break;
8064                         }
8065                 }
8066         }
8067         for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8068                 for (j = 0; j < nw; j++) {
8069                         n1 = jp[j * 2];
8070                         n2 = jp[j * 2 + 1];
8071                         if ((ip[0] == n1 && ip[2] == n2)
8072                          || (ip[1] == n1 && ip[2] == n2)
8073                          || (ip[3] == n1 && ip[2] == n2)
8074                          || (ip[0] == n2 && ip[2] == n1)
8075                          || (ip[1] == n2 && ip[2] == n1)
8076                          || (ip[3] == n2 && ip[2] == n1)) {
8077                                 if (ig == NULL)
8078                                         ig = IntGroupNew();
8079                                 if (IntGroupAdd(ig, i, 1) != 0)
8080                                         goto panic;
8081                                 ni++;
8082                                 break;
8083                         }
8084                 }
8085         }
8086         free(jp);
8087         
8088         if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, bonds, sizeof(Int) * 2, where) != 0)
8089                 goto panic;
8090         mp->nbonds -= IntGroupGetCount(where);
8091         if (mp->nbonds == 0) {
8092                 free(mp->bonds);
8093                 mp->bonds = NULL;
8094         }
8095         if (na == 0 && nd == 0 && ni == 0)
8096                 ip = NULL;
8097         else
8098                 ip = (Int *)malloc(sizeof(Int) * (na * 3 + nd * 4 + ni * 4));
8099         if (na > 0)
8100                 MoleculeDeleteAngles(mp, ip, ag);
8101         if (nd > 0)
8102                 MoleculeDeleteDihedrals(mp, ip + na * 3, dg);
8103         if (ni > 0)
8104                 MoleculeDeleteImpropers(mp, ip + na * 3 + nd * 4, ig);
8105         if (ip != NULL) {
8106                 IntGroupOffset(dg, ATOMS_MAX_NUMBER);
8107                 IntGroupOffset(ig, ATOMS_MAX_NUMBER * 2);
8108                 IntGroupAddIntGroup(ag, dg);
8109                 IntGroupAddIntGroup(ag, ig);
8110                 IntGroupRelease(dg);
8111                 IntGroupRelease(ig);
8112         }
8113
8114         *outRemoved = ip;
8115         *outRemovedPos = ag;
8116
8117         MoleculeIncrementModifyCount(mp);
8118         mp->needsMDRebuild = 1;
8119         __MoleculeUnlock(mp);
8120
8121         return na * 3 + nd * 4 + ni * 4;
8122
8123   panic:
8124         __MoleculeUnlock(mp);
8125         Panic("Low memory while removing bonds");
8126         return -1;  /*  Not reached  */
8127 }
8128
8129 int
8130 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
8131 {
8132         int n1, nc;
8133         if (mp == NULL || angles == NULL)
8134                 return 0;
8135         if (mp->noModifyTopology)
8136                 return -4;  /*  Prohibited operation  */
8137
8138         __MoleculeLock(mp);
8139         if (where != NULL)
8140                 nc = IntGroupGetCount(where);
8141         else {
8142                 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
8143                         ;
8144                 nc = n1;
8145         }
8146         if (nc > 0) {
8147                 n1 = mp->nangles;
8148                 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
8149                         || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
8150                         __MoleculeUnlock(mp);
8151                         Panic("Low memory while adding angles");
8152                 }
8153         }
8154         mp->needsMDRebuild = 1;
8155         __MoleculeUnlock(mp);
8156         return nc;
8157 }
8158
8159 int
8160 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
8161 {
8162         int nc;
8163         if (mp == NULL || where == NULL)
8164                 return 0;
8165         if (mp->noModifyTopology)
8166                 return -4;  /*  Prohibited operation  */
8167         __MoleculeLock(mp);
8168         if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
8169                 __MoleculeUnlock(mp);
8170                 Panic("Low memory while adding angles");
8171         }
8172         mp->nangles -= (nc = IntGroupGetCount(where));
8173         if (mp->nangles == 0) {
8174                 free(mp->angles);
8175                 mp->angles = NULL;
8176         }
8177         mp->needsMDRebuild = 1;
8178         __MoleculeUnlock(mp);
8179         return nc;
8180 }
8181
8182 int
8183 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
8184 {
8185         int n1, nc;
8186         if (mp == NULL || dihedrals == NULL)
8187                 return 0;
8188         if (mp->noModifyTopology)
8189                 return -4;  /*  Prohibited operation  */
8190         if (where != NULL)
8191                 nc = IntGroupGetCount(where);
8192         else {
8193                 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
8194                         ;
8195                 nc = n1;
8196         }
8197         if (nc <= 0)
8198                 return 0;
8199         n1 = mp->ndihedrals;
8200         __MoleculeLock(mp);
8201         if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8202         || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
8203                 __MoleculeUnlock(mp);
8204                 Panic("Low memory while adding dihedrals");
8205         }
8206         mp->needsMDRebuild = 1;
8207         __MoleculeUnlock(mp);
8208         return nc;
8209 }
8210
8211 int
8212 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
8213 {       
8214         int nc;
8215         if (mp == NULL || where == NULL)
8216                 return 0;
8217         if (mp->noModifyTopology)
8218                 return -4;  /*  Prohibited operation  */
8219         __MoleculeLock(mp);
8220         if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
8221                 __MoleculeUnlock(mp);
8222                 Panic("Low memory while adding dihedrals");
8223         }
8224         mp->ndihedrals -= (nc = IntGroupGetCount(where));
8225         if (mp->ndihedrals == 0) {
8226                 free(mp->dihedrals);
8227                 mp->dihedrals = NULL;
8228         }
8229         mp->needsMDRebuild = 1;
8230         __MoleculeUnlock(mp);
8231         return nc;
8232 }
8233
8234 int
8235 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
8236 {
8237         int n1, nc;
8238         if (mp == NULL || impropers == NULL)
8239                 return 0;
8240         if (mp->noModifyTopology)
8241                 return -4;  /*  Prohibited operation  */
8242         if (where != NULL)
8243                 nc = IntGroupGetCount(where);
8244         else {
8245                 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
8246                         ;
8247                 nc = n1;
8248         }
8249         if (nc <= 0)
8250                 return 0;
8251         n1 = mp->nimpropers;
8252         __MoleculeLock(mp);
8253         if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8254         || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
8255                 __MoleculeUnlock(mp);
8256                 Panic("Low memory while adding impropers");
8257         }
8258         mp->needsMDRebuild = 1;
8259         __MoleculeUnlock(mp);
8260         return nc;
8261 }
8262
8263 int
8264 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
8265 {
8266         int nc;
8267         if (mp == NULL || where == NULL)
8268                 return 0;
8269         if (mp->noModifyTopology)
8270                 return -4;  /*  Prohibited operation  */
8271         __MoleculeLock(mp);
8272         if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
8273                 __MoleculeUnlock(mp);
8274                 Panic("Low memory while adding impropers");
8275         }
8276         mp->nimpropers -= (nc = IntGroupGetCount(where));
8277         if (mp->impropers == NULL) {
8278                 free(mp->impropers);
8279                 mp->impropers = NULL;
8280         }
8281         __MoleculeUnlock(mp);
8282         return nc;
8283 }
8284
8285 int
8286 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
8287 {
8288         Int i, *ip;
8289         if (mp == NULL || mp->bonds == NULL)
8290                 return -1;
8291         for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
8292                 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
8293                         return i;
8294         }
8295         return -1;
8296 }
8297
8298 int
8299 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
8300 {
8301         Int i, *ip;
8302         if (mp == NULL || mp->angles == NULL)
8303                 return -1;
8304         for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8305                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
8306                         (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
8307                         return i;
8308         }
8309         return -1;
8310 }
8311
8312 int
8313 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8314 {
8315         Int i, *ip;
8316         if (mp == NULL || mp->dihedrals == NULL)
8317                 return -1;
8318         for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8319                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
8320                         (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
8321                         return i;
8322         }
8323         return -1;
8324 }
8325
8326 int
8327 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8328 {
8329         Int i, *ip;
8330         if (mp == NULL || mp->impropers == NULL)
8331                 return -1;
8332         for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8333                 if (n3 != ip[2])
8334                         continue;
8335                 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
8336                         (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
8337                         (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
8338                         return i;
8339         }
8340         return -1;
8341 }
8342
8343 /*  Remove the bond at bondIndex and create two dummy atoms instead.
8344     The dummy atoms are placed at the end of atoms[], and the residue
8345         numbers are the same as the root atoms (i.e. the atoms to which
8346         the dummy atoms are connected). The indices are returned in
8347         dummyIndices[0,1].  */
8348 int
8349 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
8350 {
8351         Int roots[3], newBonds[5];
8352         Vector dr;
8353         Atom *rootp[2];
8354         Atom na[2], *nap;
8355         int i, natoms;
8356         IntGroup *ig;
8357         if (mp == NULL || mp->noModifyTopology)
8358                 return 0;
8359         if (bondIndex < 0 || bondIndex >= mp->nbonds)
8360                 return -1;
8361         roots[0] = mp->bonds[bondIndex * 2];
8362         roots[1] = mp->bonds[bondIndex * 2 + 1];
8363         roots[2] = kInvalidIndex;
8364         rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
8365         rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
8366         VecSub(dr, rootp[0]->r, rootp[1]->r);
8367         for (i = 0; i < 2; i++) {
8368                 float w;
8369                 nap = &na[i];
8370                 memmove(nap, rootp[i], sizeof(na));
8371                 nap->aname[0] = '*';
8372                 strcpy(nap->element, "Du");
8373                 nap->type = 0;
8374                 nap->charge = nap->weight = 0.0;
8375                 nap->atomicNumber = 0;
8376                 nap->connect.count = 0;
8377                 w = (i == 0 ? 0.4 : -0.4);
8378                 VecScaleInc(nap->r, dr, w);
8379                 VecZero(nap->v);
8380                 VecZero(nap->f);
8381                 nap->intCharge = 0;
8382                 nap->exflags = 0;
8383         }
8384
8385         /*  Expand atoms array and append the dummy atoms at the end  */
8386         __MoleculeLock(mp);
8387         natoms = mp->natoms;
8388         if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
8389                 goto panic;
8390         memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
8391         dummyIndices[0] = natoms;
8392         dummyIndices[1] = natoms + 1;
8393
8394         /*  Remove the old bond and create new bonds  */
8395         ig = IntGroupNewWithPoints(bondIndex, 1, -1);
8396         if (ig == NULL)
8397                 goto panic;
8398         MoleculeDeleteBonds(mp, NULL, ig, NULL, NULL);
8399         IntGroupRelease(ig);
8400         newBonds[0] = roots[0];
8401         newBonds[1] = dummyIndices[0];
8402         newBonds[2] = roots[1];
8403         newBonds[3] = dummyIndices[1];
8404         newBonds[4] = kInvalidIndex;
8405         
8406         i = (MoleculeAddBonds(mp, 2, newBonds, NULL, 1) < 0 ? -1 : 0);
8407         mp->needsMDRebuild = 1;
8408         __MoleculeUnlock(mp);
8409         return i;
8410
8411 panic:
8412         __MoleculeUnlock(mp);
8413         Panic("Low memory during creating dummy atoms");
8414         return 1;
8415 }
8416
8417 /*  Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
8418     a bond between the two root atoms. The value bondIndex is used as a
8419         hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
8420         the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
8421         is ignored and the new bond is stored at the end of bonds[].  */
8422 int
8423 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
8424 {
8425         return 0;
8426 }
8427
8428 /*
8429 Int
8430 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
8431 {
8432         Int n1, *np1;
8433         if (mol == NULL || mol->noModifyTopology)
8434                 return -1;
8435         n1 = mol->nangles;
8436         np1 = mol->angles;
8437         mol->nangles = 0;
8438         mol->angles = NULL;
8439         if (nangles > 0) {
8440                 __MoleculeLock(mol);
8441                 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
8442                 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
8443                 mol->needsMDRebuild = 1;
8444                 __MoleculeUnlock(mol);
8445         }
8446         *outAngles = np1;
8447         return n1;
8448 }
8449                                                 
8450 Int
8451 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
8452 {
8453         Int n1, *np1;
8454         if (mol == NULL || mol->noModifyTopology)
8455                 return -1;
8456         n1 = mol->ndihedrals;
8457         np1 = mol->dihedrals;
8458         mol->ndihedrals = 0;
8459         mol->dihedrals = NULL;
8460         if (ndihedrals > 0) {
8461                 __MoleculeLock(mol);
8462                 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
8463                 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
8464                 mol->needsMDRebuild = 1;
8465                 __MoleculeUnlock(mol);
8466         }
8467         *outDihedrals = np1;
8468         return n1;
8469 }
8470
8471 Int
8472 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
8473 {
8474         Int n1, *np1;
8475         if (mol == NULL || mol->noModifyTopology)
8476                 return -1;
8477         n1 = mol->nimpropers;
8478         np1 = mol->impropers;
8479         mol->nimpropers = 0;
8480         mol->impropers = NULL;
8481         if (nimpropers > 0) {
8482                 __MoleculeLock(mol);
8483                 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
8484                 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
8485                 mol->needsMDRebuild = 1;
8486                 __MoleculeUnlock(mol);
8487         }
8488         *outImpropers = np1;
8489         return n1;
8490 }
8491 */
8492
8493 Int
8494 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
8495 {
8496         Int i, j, k, *ip;
8497         Atom *ap;
8498         Int nangles;
8499         Int *angles;
8500         
8501         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8502                 return 0;  /*  molecule is empty  */
8503         if (mol->noModifyTopology)
8504                 return -1;
8505         nangles = 0;
8506         angles = NULL;
8507         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
8508                 Int *cp = AtomConnectData(&ap->connect);
8509                 if (ap->anchor != NULL)
8510                         continue;
8511                 for (j = 0; j < ap->connect.count; j++) {
8512                         Int j0 = cp[j];
8513                         if (ATOM_AT_INDEX(mol->atoms, j0)->anchor != NULL)
8514                                 continue;
8515                         for (k = j + 1; k < ap->connect.count; k++) {
8516                                 Int k0 = cp[k];
8517                                 if (ATOM_AT_INDEX(mol->atoms, k0)->anchor != NULL)
8518                                         continue;
8519                                 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
8520                                         ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
8521                                         ip[0] = j0;
8522                                         ip[1] = i;
8523                                         ip[2] = k0;
8524                                 }
8525                         }
8526                 }
8527         }
8528         if (nangles > 0) {
8529                 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
8530                 ip[0] = -1;
8531                 nangles--;
8532         }
8533         if (outAngles != NULL)
8534                 *outAngles = angles;
8535         return nangles;
8536 }
8537
8538 Int
8539 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
8540 {
8541         Int n1, n2, n3, n4, *ip, *cp2, *cp3;
8542         Atom *ap2, *ap3;
8543         Int ndihedrals;
8544         Int *dihedrals;
8545         
8546         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8547                 return 0;  /*  molecule is empty  */
8548         ndihedrals = 0;
8549         dihedrals = NULL;
8550         for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
8551                 Int i1, i3, i4, *ip;
8552                 if (ap2->anchor != NULL)
8553                         continue;
8554                 cp2 = AtomConnectData(&ap2->connect);
8555                 for (i3 = 0; i3 < ap2->connect.count; i3++) {
8556                         n3 = cp2[i3];
8557                         if (n2 > n3)
8558                                 continue;
8559                         ap3 = ATOM_AT_INDEX(mol->atoms, n3);
8560                         if (ap3->anchor != NULL)
8561                                 continue;
8562                         cp3 = AtomConnectData(&ap3->connect);
8563                         for (i1 = 0; i1 < ap2->connect.count; i1++) {
8564                                 n1 = cp2[i1];
8565                                 if (n1 == n3)
8566                                         continue;
8567                                 if (ATOM_AT_INDEX(mol->atoms, n1)->anchor != NULL)
8568                                         continue;
8569                                 for (i4 = 0; i4 < ap3->connect.count; i4++) {
8570                                         n4 = cp3[i4];
8571                                         if (n2 == n4 || n1 == n4)
8572                                                 continue;
8573                                         if (ATOM_AT_INDEX(mol->atoms, n4)->anchor != NULL)
8574                                                 continue;
8575                                         if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
8576                                                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
8577                                                 ip[0] = n1;
8578                                                 ip[1] = n2;
8579                                                 ip[2] = n3;
8580                                                 ip[3] = n4;
8581                                         }
8582                                 }
8583                         }
8584                 }
8585         }
8586         if (ndihedrals > 0) {
8587                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
8588                 ip[0] = -1;
8589                 ndihedrals--;
8590         }
8591         if (outDihedrals != NULL)
8592                 *outDihedrals = dihedrals;
8593         return ndihedrals;
8594 }
8595
8596 Int
8597 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
8598 {
8599         Int n1, n2, n3, n4, t1, t2, t3, t4, *ip, *cp;
8600         Parameter *par = mol->par;
8601         Atom *ap, *ap3;
8602         Int nimpropers;
8603         Int *impropers;
8604         
8605         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8606                 return 0;  /*  molecule is empty  */
8607         if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
8608                 return 0;  /*  No improper parameters are defined  */
8609         nimpropers = 0;
8610         impropers = NULL;
8611         ap = mol->atoms;
8612         for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
8613                 Int i1, i2, i4, found, *ip;
8614                 t3 = ap3->type;
8615                 cp = AtomConnectData(&ap3->connect);
8616                 for (i1 = 0; i1 < ap3->connect.count; i1++) {
8617                         n1 = cp[i1];
8618                         t1 = ATOM_AT_INDEX(ap, n1)->type;
8619                         for (i2 = i1 + 1; i2 < ap3->connect.count; i2++) {
8620                                 n2 = cp[i2];
8621                                 t2 = ATOM_AT_INDEX(ap, n2)->type;
8622                                 for (i4 = i2 + 1; i4 < ap3->connect.count; i4++) {
8623                                         n4 = cp[i4];
8624                                         t4 = ATOM_AT_INDEX(ap, n4)->type;
8625                                         found = 0;
8626                                         if (ParameterLookupImproperPar(par, t1, t2, t3, t4, n1, n2, n3, n4, 0) != NULL)
8627                                                 found = 1;
8628                                         else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, -1, -1, -1, -1, 0) != NULL)
8629                                                 found = 1;
8630                                         if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
8631                                                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8632                                                 ip[0] = n1;
8633                                                 ip[1] = n2;
8634                                                 ip[2] = n3;
8635                                                 ip[3] = n4;
8636                                         }
8637                                 }
8638                         }
8639                 }
8640         }
8641         if (nimpropers > 0) {
8642                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8643                 ip[0] = -1;
8644                 nimpropers--;
8645         }
8646         if (outImpropers != NULL)
8647                 *outImpropers = impropers;
8648         return nimpropers;
8649 }
8650
8651 #pragma mark ====== Residues ======
8652
8653 void
8654 MoleculeCleanUpResidueTable(Molecule *mp)
8655 {
8656         int i, maxres;
8657         Atom *ap;
8658         if (mp == NULL || mp->natoms == 0)
8659                 return;
8660         maxres = 0;
8661         __MoleculeLock(mp);
8662         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8663                 if (ap->resSeq >= maxres)
8664                         maxres = ap->resSeq + 1;
8665                 if (ap->resSeq < mp->nresidues) {
8666                         if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
8667                                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8668                 } else {
8669                         AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
8670                 }
8671         }
8672         if (maxres < mp->nresidues)
8673                 mp->nresidues = maxres;
8674         __MoleculeUnlock(mp);
8675 }
8676
8677 /*  Change the number of residues. If nresidues is greater than the current value,
8678     then the array mp->residues is expanded with null names. If nresidues is smaller
8679         than the current value, mp->nresidues is set to the smallest possible value
8680         that is no smaller than nresidues and larger than any of the resSeq values.  */
8681 int
8682 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
8683 {
8684         int n;
8685         if (mp == NULL)
8686                 return 0;
8687         if (mp->nresidues == nresidues)
8688                 return nresidues;
8689         else if (mp->nresidues < nresidues) {
8690                 __MoleculeLock(mp);
8691                 n = mp->nresidues;
8692                 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
8693                 while (n < nresidues)
8694                         mp->residues[n++][0] = 0;
8695                 __MoleculeUnlock(mp);
8696                 return nresidues;
8697         } else {
8698                 int i;
8699                 Atom *ap;
8700                 n = nresidues;
8701                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8702                         if (ap->resSeq >= n)
8703                                 n = ap->resSeq + 1;
8704                 }
8705                 mp->nresidues = n;
8706                 return n;
8707         }
8708 }
8709
8710 int
8711 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
8712 {
8713         IntGroupIterator iter;
8714         int withArray, resSeq, maxSeq;
8715         int i, j;
8716         Atom *ap;
8717         
8718         /*  If LSB of resSeqs is 1, then a constant value is used for all specified atoms  */
8719         if (((int)resSeqs & 1) == 0) {
8720                 withArray = 1;
8721                 resSeq = 0;
8722         } else {
8723                 withArray = 0;
8724                 resSeq = ((int)resSeqs - 1) / 2;
8725         }
8726         
8727         IntGroupIteratorInit(group, &iter);
8728
8729         /*  Change resSeqs  */
8730         maxSeq = 0;
8731         j = 0;
8732         __MoleculeLock(mp);
8733         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8734                 ap = ATOM_AT_INDEX(mp->atoms, i);
8735                 if (withArray)
8736                         resSeq = resSeqs[j++];
8737                 if (resSeq > maxSeq)
8738                         maxSeq = resSeq;
8739                 ap->resSeq = resSeq;
8740         }
8741         __MoleculeUnlock(mp);
8742
8743         /*  Expand array if necessary  */
8744         if (maxSeq >= mp->nresidues)
8745                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8746
8747         /*  Synchronize resName and residues[]  */
8748         __MoleculeLock(mp);
8749         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8750                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8751                         continue;
8752                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8753         }
8754         IntGroupIteratorRelease(&iter);
8755         __MoleculeUnlock(mp);
8756         
8757         MoleculeIncrementModifyCount(mp);
8758         
8759         return 0;
8760 }
8761
8762 int
8763 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
8764 {
8765         return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(resSeq * 2 + 1));
8766 }
8767
8768 /*  Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
8769     specifies the mp->nresidues after modifying the residue numbers.
8770         If all atoms are modified, then the table of residue names is also shifted. Otherwise,
8771         the table of residue names is not touched. */
8772 int
8773 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
8774 {
8775         int i, maxSeq, nmodatoms;
8776         Atom *ap;
8777         IntGroupIterator iter;
8778         IntGroupIteratorInit(group, &iter);
8779         maxSeq = 0;
8780         if (nresidues < 0)
8781                 nresidues = mp->nresidues;
8782         nmodatoms = 0;
8783         __MoleculeLock(mp);
8784         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8785                 ap = ATOM_AT_INDEX(mp->atoms, i);
8786                 ap->resSeq += offset;
8787                 if (ap->resSeq < 0) {
8788                         /*  Bad argument; undo change and returns this index + 1  */
8789                         int bad_index = i;
8790                         ap->resSeq -= offset;
8791                         while ((i = IntGroupIteratorLast(&iter)) >= 0) {
8792                                 ap = ATOM_AT_INDEX(mp->atoms, i);
8793                                 ap->resSeq -= offset;
8794                         }
8795                         IntGroupIteratorRelease(&iter);
8796                         return bad_index + 1;
8797                 }
8798                 if (ap->resSeq > maxSeq)
8799                         maxSeq = ap->resSeq;
8800                 nmodatoms++;
8801         }
8802         if (maxSeq >= nresidues)
8803                 nresidues = maxSeq + 1;
8804         if (offset < 0 && nmodatoms == mp->natoms) {
8805                 /*  Shift the residue names downward  */
8806                 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
8807         }
8808         __MoleculeUnlock(mp);
8809         MoleculeChangeNumberOfResidues(mp, nresidues);
8810         if (offset > 0 && nmodatoms == mp->natoms) {
8811                 /*  Shift the residue names upward  */
8812                 __MoleculeLock(mp);
8813                 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
8814                 __MoleculeUnlock(mp);
8815         }
8816         IntGroupIteratorRelease(&iter);
8817
8818         MoleculeIncrementModifyCount(mp);
8819         
8820         return 0;
8821 }
8822
8823 /*  Change residue names for the specified residue numbers. Names is an array of
8824     chars containing argc*4 characters, and every 4 characters represent a
8825         residue name; characters '\x01'-'\x1f' are converted to '\0', which allow 
8826         names to be handled as a C string.  */
8827 int
8828 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
8829 {
8830         int i, maxSeq;
8831         Atom *ap;
8832         maxSeq = 0;
8833         for (i = 0; i < argc; i++) {
8834                 if (maxSeq < resSeqs[i])
8835                         maxSeq = resSeqs[i];
8836         }
8837         if (maxSeq >= mp->nresidues)
8838                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8839         __MoleculeLock(mp);
8840         for (i = 0; i < argc; i++) {
8841                 char *p = mp->residues[resSeqs[i]];
8842                 int j;
8843                 strncpy(p, names + i * 4, 4);
8844                 for (j = 0; j < 4; j++) {
8845                         if (p[j] >= 0 && p[j] < 0x20)
8846                                 p[j] = 0;
8847                 }
8848         }
8849         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8850                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8851         }
8852         __MoleculeUnlock(mp);
8853
8854         MoleculeIncrementModifyCount(mp);
8855         
8856         return 0;
8857 }
8858
8859 /*  Returns the maximum residue number actually used  */
8860 int
8861 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
8862 {
8863         int i, maxSeq;
8864         Atom *ap;
8865         maxSeq = -1;
8866         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8867                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8868                         continue;
8869                 if (ap->resSeq > maxSeq)
8870                         maxSeq = ap->resSeq;
8871         }
8872         return maxSeq;
8873 }
8874
8875 /*  Returns the minimum residue number actually used  */
8876 int
8877 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
8878 {
8879         int i, minSeq;
8880         Atom *ap;
8881         minSeq = ATOMS_MAX_NUMBER;
8882         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8883                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8884                         continue;
8885                 if (ap->resSeq < minSeq)
8886                         minSeq = ap->resSeq;
8887         }
8888         return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
8889 }
8890
8891 #pragma mark ====== Sort by Residues ======
8892
8893 static int
8894 sAtomSortComparator(const void *a, const void *b)
8895 {
8896         const Atom *ap, *bp;
8897         ap = *((const Atom **)a);
8898         bp = *((const Atom **)b);
8899         if (ap->resSeq == bp->resSeq) {
8900                 /*  Retain the original order (i.e. atom with larger pointer address is larger)  */
8901                 if (ap < bp)
8902                         return -1;
8903                 else if (ap > bp)
8904                         return 1;
8905                 else return 0;
8906         } else {
8907                 /*  Compare the residue sequence. However, residue sequence 0 is always larger.  */
8908                 if (ap->resSeq == 0)
8909                         return 1;
8910                 else if (bp->resSeq == 0)
8911                         return -1;
8912                 else if (ap->resSeq < bp->resSeq)
8913                         return -1;
8914                 else if (ap->resSeq > bp->resSeq)
8915                         return 1;
8916                 else return 0;
8917         }
8918 }
8919
8920 static void
8921 sMoleculeReorder(Molecule *mp)
8922 {
8923         int i, res, prevRes;
8924         Atom **apArray;
8925         Int *old2new;
8926         Atom *newAtoms;
8927         if (mp == NULL || mp->natoms <= 1)
8928                 return;
8929
8930         /*  Sort the atoms, bonds, etc. */
8931         apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
8932         old2new = (Int *)calloc(sizeof(Int), mp->natoms);
8933         newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
8934         if (apArray == NULL || old2new == NULL || newAtoms == NULL)
8935                 Panic("Low memory during reordering atoms");
8936         for (i = 0; i < mp->natoms; i++)
8937                 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
8938
8939         /*  Sort the atoms. Note: apArray is an array of "Pointer to Atom"  */
8940         qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
8941         
8942         /*  Make a table of 'which atom becomes which'  */
8943         for (i = 0; i < mp->natoms; i++) {
8944                 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
8945                 old2new[j] = i;
8946         }
8947         
8948         /*  Renumber the bonds, etc.  */
8949         for (i = 0; i < mp->nbonds * 2; i++) {
8950                 mp->bonds[i] = old2new[mp->bonds[i]];
8951         }
8952         for (i = 0; i < mp->nangles * 3; i++) {
8953                 mp->angles[i] = old2new[mp->angles[i]];
8954         }
8955         for (i = 0; i < mp->ndihedrals * 4; i++) {
8956                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
8957         }
8958         for (i = 0; i < mp->nimpropers * 4; i++) {
8959                 mp->impropers[i] = old2new[mp->impropers[i]];
8960         }
8961         for (i = 0; i < mp->natoms; i++) {
8962                 Int *ip, j;
8963                 ip = AtomConnectData(&(apArray[i]->connect));
8964                 for (j = 0; j < apArray[i]->connect.count; j++, ip++)
8965                         *ip = old2new[*ip];
8966         }
8967         
8968         /*  Renumber the residues so that the residue numbers are contiguous  */
8969         res = prevRes = 0;
8970         for (i = 0; i < mp->natoms; i++) {
8971                 if (apArray[i]->resSeq == 0)
8972                         break;
8973                 if (apArray[i]->resSeq != prevRes) {
8974                         res++;
8975                         prevRes = apArray[i]->resSeq;
8976                         if (prevRes != res) {
8977                                 strncpy(mp->residues[res], mp->residues[prevRes], 4);
8978                         }
8979                 }
8980                 apArray[i]->resSeq = res;
8981         }
8982         mp->nresidues = res + 1;
8983
8984         /*  Sort the atoms and copy back to atoms[] */
8985         for (i = 0; i < mp->natoms; i++) {
8986                 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
8987         }
8988         memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
8989         
8990         /*  Free the locally allocated storage  */
8991         free(newAtoms);
8992         free(old2new);
8993         free(apArray);
8994 }
8995
8996 /*  Renumber atoms  */
8997 int
8998 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
8999 {
9000         Int *old2new, i, j, retval;
9001         Atom *saveAtoms;
9002         if (mp == NULL)
9003                 return 0;
9004         if (mp->noModifyTopology)
9005                 return -1;
9006         if (old2new_out != NULL)
9007                 old2new = old2new_out;
9008         else
9009                 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9010         saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9011         if (old2new == NULL || saveAtoms == NULL)
9012                 Panic("Low memory during reordering atoms");
9013         memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
9014         __MoleculeLock(mp);
9015         for (i = 0; i < mp->natoms; i++)
9016                 old2new[i] = -1;
9017         for (i = 0; i < isize && i < mp->natoms; i++) {
9018                 j = new2old[i];
9019                 if (j < 0 || j >= mp->natoms) {
9020                         retval = 1; /* Out of range */
9021                         goto end;
9022                 }
9023                 if (old2new[j] != -1) {
9024                         retval = 2;  /*  Duplicate entry  */
9025                         goto end;
9026                 }
9027                 old2new[j] = i;
9028         }
9029         if (i < mp->natoms) {
9030                 for (j = 0; j < mp->natoms; j++) {
9031                         if (old2new[j] != -1)
9032                                 continue;
9033                         old2new[j] = i++;
9034                 }
9035         }
9036         if (i != mp->natoms) {
9037                 retval = 3;  /*  Internal inconsistency  */
9038                 goto end;
9039         }
9040
9041         /*  Renumber the bonds, etc.  */
9042         for (i = 0; i < mp->nbonds * 2; i++) {
9043                 mp->bonds[i] = old2new[mp->bonds[i]];
9044         }
9045         for (i = 0; i < mp->nangles * 3; i++) {
9046                 mp->angles[i] = old2new[mp->angles[i]];
9047         }
9048         for (i = 0; i < mp->ndihedrals * 4; i++) {
9049                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9050         }
9051         for (i = 0; i < mp->nimpropers * 4; i++) {
9052                 mp->impropers[i] = old2new[mp->impropers[i]];
9053         }
9054         /*  Renumber the connection table and pi anchor table  */
9055         for (i = 0; i < mp->natoms; i++) {
9056                 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
9057                 Int *ip = AtomConnectData(&ap->connect);
9058                 for (j = 0; j < ap->connect.count; j++, ip++)
9059                         *ip = old2new[*ip];
9060                 if (ap->anchor != NULL) {
9061                         ip = AtomConnectData(&ap->anchor->connect);
9062                         for (j = 0; j < ap->anchor->connect.count; j++, ip++)
9063                                 *ip = old2new[*ip];
9064                 }
9065         }
9066         
9067         if (mp->par != NULL) {
9068                 /*  Renumber the parameters  */
9069                 int n;
9070                 for (j = kFirstParType; j <= kLastParType; j++) {
9071                         n = ParameterGetCountForType(mp->par, j);
9072                         for (i = 0; i < n; i++) {
9073                                 UnionPar *up = ParameterGetUnionParFromTypeAndIndex(mp->par, j, i);
9074                                 if (up != NULL)
9075                                         ParameterRenumberAtoms(j, up, mp->natoms, old2new);
9076                         }
9077                 }
9078         }
9079         
9080         /*  Renumber the atoms  */
9081         for (i = 0; i < mp->natoms; i++)
9082                 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
9083         retval = 0;
9084         
9085         MoleculeIncrementModifyCount(mp);
9086         mp->needsMDRebuild = 1;
9087
9088   end:
9089         __MoleculeUnlock(mp);
9090         free(saveAtoms);
9091         if (old2new_out == NULL)
9092                 free(old2new);
9093         return retval;
9094 }
9095
9096 #pragma mark ====== Coordinate Transform ======
9097
9098 void
9099 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
9100 {
9101         int i;
9102         Atom *ap;
9103         Symop new_symop;
9104         Transform rtr, symtr;
9105         if (mp == NULL || tr == NULL)
9106                 return;
9107         TransformInvert(rtr, tr);
9108         __MoleculeLock(mp);
9109         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9110                 if (group == NULL || IntGroupLookup(group, i, NULL) != 0) {
9111                         TransformVec(&ap->r, tr, &ap->r);
9112                         if (!SYMOP_ALIVE(ap->symop))
9113                                 continue;
9114                         /*  Transform symop  */
9115                         if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9116                                 continue;
9117                         TransformMul(symtr, tr, symtr);
9118                         if (group == NULL || IntGroupLookup(group, ap->symbase, NULL) != 0)
9119                                 TransformMul(symtr, symtr, rtr);
9120                 } else {
9121                         if (!SYMOP_ALIVE(ap->symop))
9122                                 continue;
9123                         /*  Transform symop if the base atom is transformed  */
9124                         if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
9125                                 continue;
9126                         if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9127                                 continue;
9128                         TransformMul(symtr, symtr, rtr);
9129                 }
9130                 if (MoleculeGetSymopForTransform(mp, symtr, &new_symop, 1) != 0)
9131                         continue;
9132                 ap->symop = new_symop;
9133         }
9134         mp->needsMDCopyCoordinates = 1;
9135         __MoleculeUnlock(mp);
9136         sMoleculeNotifyChangeAppearance(mp);
9137 }
9138
9139 /*
9140 void
9141 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
9142 {
9143         int i;
9144         Atom *ap;
9145         if (mp == NULL || tr == NULL)
9146                 return;
9147         __MoleculeLock(mp);
9148         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9149                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9150                         continue;
9151                 TransformVec(&ap->r, tr, &ap->r);
9152         }
9153         mp->needsMDCopyCoordinates = 1;
9154         __MoleculeUnlock(mp);
9155         sMoleculeNotifyChangeAppearance(mp);
9156 }
9157 */
9158
9159 void
9160 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
9161 {
9162         Transform tr;
9163         if (mp == NULL || vp == NULL)
9164                 return;
9165         memset(tr, 0, sizeof(tr));
9166         tr[0] = tr[4] = tr[8] = 1.0;
9167         tr[9] = vp->x;
9168         tr[10] = vp->y;
9169         tr[11] = vp->z;
9170         MoleculeTransform(mp, tr, group);
9171 }
9172
9173 void
9174 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
9175 {
9176         Transform tr;
9177         TransformForRotation(tr, axis, angle, center);
9178         MoleculeTransform(mp, tr, group);
9179 }
9180
9181 int
9182 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
9183 {
9184         int i;
9185         Atom *ap;
9186         Double w;
9187         if (mp == NULL || center == NULL)
9188                 return 1;
9189         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9190                 return 2;   /*  Empty molecule  */
9191         w = 0.0;
9192         center->x = center->y = center->z = 0.0;
9193         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9194                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9195                         continue;
9196                 VecScaleInc(*center, ap->r, ap->weight);
9197                 w += ap->weight;
9198         }
9199         if (w < 1e-7)
9200                 return 3;  /*  Atomic weights are not defined?  */
9201         w = 1.0 / w;
9202         VecScaleSelf(*center, w);
9203         return 0;
9204 }
9205
9206 int
9207 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
9208 {
9209         Vector vmin, vmax;
9210         int i;
9211         Atom *ap;
9212         if (mp == NULL)
9213                 return 1;
9214         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9215                 return 2;   /*  Empty molecule  */
9216         vmin.x = vmin.y = vmin.z = 1e50;
9217         vmax.x = vmax.y = vmax.z = -1e50;
9218         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9219                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9220                         continue;
9221                 if (vmin.x > ap->r.x)
9222                         vmin.x = ap->r.x;
9223                 if (vmin.y > ap->r.y)
9224                         vmin.y = ap->r.y;
9225                 if (vmin.z > ap->r.z)
9226                         vmin.z = ap->r.z;
9227                 if (vmax.x < ap->r.x)
9228                         vmax.x = ap->r.x;
9229                 if (vmax.y < ap->r.y)
9230                         vmax.y = ap->r.y;
9231                 if (vmax.z < ap->r.z)
9232                         vmax.z = ap->r.z;
9233         }
9234         if (min != NULL)
9235                 *min = vmin;
9236         if (max != NULL)
9237                 *max = vmax;
9238         return 0;       
9239 }
9240
9241 #pragma mark ====== Measurements ======
9242
9243 Double
9244 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
9245 {
9246         Vector r1, r2;
9247 /*      if (mp->is_xtal_coord) {
9248                 TransformVec(&r1, mp->cell->tr, vp1);
9249                 TransformVec(&r2, mp->cell->tr, vp2);
9250         } else */ {
9251                 r1 = *vp1;
9252                 r2 = *vp2;
9253         }
9254         VecDec(r1, r2);
9255         return VecLength(r1);
9256 }
9257
9258 Double
9259 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
9260 {
9261         Vector r1, r2, r3;
9262         double w;
9263 /*      if (mp->is_xtal_coord) {
9264                 TransformVec(&r1, mp->cell->tr, vp1);
9265                 TransformVec(&r2, mp->cell->tr, vp2);
9266                 TransformVec(&r3, mp->cell->tr, vp3);
9267         } else */ {
9268                 r1 = *vp1;
9269                 r2 = *vp2;
9270                 r3 = *vp3;
9271         }
9272         VecDec(r1, r2);
9273         VecDec(r3, r2);
9274         w = VecLength(r1) * VecLength(r3);
9275         if (w < 1e-20)
9276                 return NAN;
9277         return acos(VecDot(r1, r3) / w) * kRad2Deg;
9278 }
9279
9280 Double
9281 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
9282 {
9283         Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
9284         double w1, w2, w3;
9285 /*      if (mp->is_xtal_coord) {
9286                 TransformVec(&r1, mp->cell->tr, vp1);
9287                 TransformVec(&r2, mp->cell->tr, vp2);
9288                 TransformVec(&r3, mp->cell->tr, vp3);
9289                 TransformVec(&r4, mp->cell->tr, vp4);
9290         } else */ {
9291                 r1 = *vp1;
9292                 r2 = *vp2;
9293                 r3 = *vp3;
9294                 r4 = *vp4;
9295         }
9296         VecSub(r21, r1, r2);
9297         VecSub(r32, r2, r3);
9298         VecSub(r43, r3, r4);
9299         VecCross(v1, r21, r32);
9300         VecCross(v2, r32, r43);
9301         VecCross(v3, r32, v1);
9302         w1 = VecLength(v1);
9303         w2 = VecLength(v2);
9304         w3 = VecLength(v3);
9305         if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
9306                 return NAN;
9307         } else {
9308                 w1 = 1.0 / w1;
9309                 w2 = 1.0 / w2;
9310                 w3 = 1.0 / w3;
9311                 VecScaleSelf(v1, w1);
9312                 VecScaleSelf(v2, w2);
9313                 VecScaleSelf(v3, w3);
9314                 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
9315         }
9316 }
9317
9318 #pragma mark ====== XtalCell Parameters ======
9319
9320 void
9321 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
9322 {
9323         if (mp->cell != NULL) {
9324                 TransformVec(dst, mp->cell->tr, src);
9325         } else *dst = *src;
9326 }
9327
9328 void
9329 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
9330 {
9331         if (mp->cell != NULL) {
9332                 TransformVec(dst, mp->cell->rtr, src);
9333         } else *dst = *src;
9334 }
9335
9336 int
9337 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
9338 {
9339         static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
9340         int n1, n2, n3;
9341         Vector *vp1, *vp2, *vp3;
9342         Vector v1, v2;
9343
9344         if (cp == NULL)
9345                 return 0;
9346         for (n1 = 0; n1 < 3; n1++) {
9347                 if (cp->flags[n1] != 0)
9348                         break;
9349         }
9350         if (n1 == 3) {
9351                 /*  All directions are non-periodic  */
9352                 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
9353                 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
9354         } else {
9355                 n2 = (n1 + 1) % 3;
9356                 n3 = (n1 + 2) % 3;
9357                 vp1 = &(cp->axes[n1]);
9358                 vp2 = &(cp->axes[n2]);
9359                 vp3 = &(cp->axes[n3]);
9360                 cp->tr[n1*3] = vp1->x;
9361                 cp->tr[n1*3+1] = vp1->y;
9362                 cp->tr[n1*3+2] = vp1->z;
9363                 cp->tr[9] = cp->origin.x;
9364                 cp->tr[10] = cp->origin.y;
9365                 cp->tr[11] = cp->origin.z;
9366                 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
9367                         /*  1-dimensional or 2-dimensional system  */
9368                         /*  Create "dummy" axes, so that transforms between internal and cartesian coordinates are
9369                          possible with a single matrix  */
9370                         if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
9371                                 /*  1-dimensional  */
9372                                 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
9373                                 VecCross(v1, *vp1, xvec);
9374                                 VecCross(v2, *vp1, yvec);
9375                                 if (VecLength2(v1) < VecLength2(v2))
9376                                         v1 = v2;
9377                                 VecCross(v2, *vp1, v1);
9378                                 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
9379                                         return -1;   /*  Non-regular transform  */
9380                         } else if (cp->flags[n2] == 0) {
9381                                 v2 = *vp3;
9382                                 VecCross(v1, v2, *vp1);
9383                                 if (NormalizeVec(&v1, &v1))
9384                                         return -1;  /*  Non-regular transform  */
9385                         } else {
9386                                 v1 = *vp2;
9387                                 VecCross(v2, *vp1, v1);
9388                                 if (NormalizeVec(&v2, &v2))
9389                                         return -1;  /*  Non-regular transform  */
9390                         }
9391                         cp->tr[n2*3] = v1.x;
9392                         cp->tr[n2*3+1] = v1.y;
9393                         cp->tr[n2*3+2] = v1.z;
9394                         cp->tr[n3*3] = v2.x;
9395                         cp->tr[n3*3+1] = v2.y;
9396                         cp->tr[n3*3+2] = v2.z;
9397                 } else {
9398                         VecCross(v1, *vp1, *vp2);
9399                         if (fabs(VecDot(v1, *vp3)) < 1e-7)
9400                                 return -1;  /*  Non-regular transform  */
9401                         cp->tr[n2*3] = vp2->x;
9402                         cp->tr[n2*3+1] = vp2->y;
9403                         cp->tr[n2*3+2] = vp2->z;
9404                         cp->tr[n3*3] = vp3->x;
9405                         cp->tr[n3*3+1] = vp3->y;
9406                         cp->tr[n3*3+2] = vp3->z;
9407                 }
9408         }
9409         if (TransformInvert(cp->rtr, cp->tr))
9410                 return -1;  /*  Non-regular transform  */
9411
9412         /*  Calculate the reciprocal cell parameters  */
9413         cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[3] * cp->rtr[3] + cp->rtr[6] * cp->rtr[6]);
9414         cp->rcell[1] = sqrt(cp->rtr[1] * cp->rtr[1] + cp->rtr[4] * cp->rtr[4] + cp->rtr[7] * cp->rtr[7]);
9415         cp->rcell[2] = sqrt(cp->rtr[2] * cp->rtr[2] + cp->rtr[5] * cp->rtr[5] + cp->rtr[8] * cp->rtr[8]);
9416         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;
9417         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;
9418         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;
9419         
9420         if (calc_abc) {
9421                 /*  Calculate a, b, c, alpha, beta, gamma  */
9422                 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[1] * cp->tr[1] + cp->tr[2] * cp->tr[2]);
9423                 cp->cell[1] = sqrt(cp->tr[3] * cp->tr[3] + cp->tr[4] * cp->tr[4] + cp->tr[5] * cp->tr[5]);
9424                 cp->cell[2] = sqrt(cp->tr[6] * cp->tr[6] + cp->tr[7] * cp->tr[7] + cp->tr[8] * cp->tr[8]);
9425                 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;
9426                 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;
9427                 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;
9428         }
9429         return 0;
9430 }
9431
9432 void
9433 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
9434 {
9435         XtalCell *cp;
9436         int i;
9437         Atom *ap;
9438         Transform cmat;
9439         if (mp == NULL)
9440                 return;
9441         __MoleculeLock(mp);
9442         memset(&cmat, 0, sizeof(Transform));
9443         if (mp->cell != NULL)
9444                 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
9445         else
9446                 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
9447         if (a == 0.0) {
9448                 if (mp->cell != NULL) {
9449                         free(mp->cell);
9450                         mp->needsMDRebuild = 1;
9451                 }
9452                 mp->cell = NULL;
9453         } else {
9454                 cp = mp->cell;
9455                 if (cp == NULL) {
9456                         cp = (XtalCell *)calloc(sizeof(XtalCell), 1);
9457                         if (cp == NULL)
9458                                 Panic("Low memory during setting cell parameters");
9459                         mp->cell = cp;
9460                         mp->needsMDRebuild = 1;
9461                 }
9462                 /*  alpha, beta, gamma are in degree  */
9463                 cp->cell[0] = a;
9464                 cp->cell[1] = b;
9465                 cp->cell[2] = c;
9466                 cp->cell[3] = alpha;
9467                 cp->cell[4] = beta;
9468                 cp->cell[5] = gamma;
9469                 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
9470                         /*  c unique (hexagonal etc.)  */
9471                         Double cosa, cosb, sinb, cosg;
9472                         cosa = cos(alpha * kDeg2Rad);
9473                         cosb = cos(beta * kDeg2Rad);
9474                         sinb = sin(beta * kDeg2Rad);
9475                         cosg = cos(gamma * kDeg2Rad);
9476                         cp->axes[0].x = a * sinb;
9477                         cp->axes[0].y = 0;
9478                         cp->axes[0].z = a * cosb;
9479                         cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
9480                         cp->axes[1].z = b * cosa;
9481                         cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
9482                         cp->axes[2].x = 0;
9483                         cp->axes[2].y = 0;
9484                         cp->axes[2].z = c;
9485                 } else {
9486                         /*  b unique  */
9487                         Double cosg, sing, cosa, cosb;
9488                         cosa = cos(alpha * kDeg2Rad);
9489                         cosb = cos(beta * kDeg2Rad);
9490                         cosg = cos(gamma * kDeg2Rad);
9491                         sing = sin(gamma * kDeg2Rad);
9492                         cp->axes[0].x = a * sing;
9493                         cp->axes[0].y = a * cosg;
9494                         cp->axes[0].z = 0;
9495                         cp->axes[1].x = 0;
9496                         cp->axes[1].y = b;
9497                         cp->axes[1].z = 0;
9498                         cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
9499                         cp->axes[2].y = c * cosa;
9500                         cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
9501                 }
9502                 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
9503                 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
9504                 MoleculeCalculateCellFromAxes(cp, 0);
9505                 TransformMul(cmat, cp->tr, cmat);
9506         }
9507         
9508         /*  Update the coordinates (if requested)  */
9509         if (convertCoordinates) {
9510                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9511                         TransformVec(&(ap->r), cmat, &(ap->r));
9512                 }
9513         }
9514         
9515         /*  Update the anisotropic parameters  */
9516         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9517                 Aniso *anp = ap->aniso;
9518                 if (anp != NULL) {
9519                         MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
9520                 }
9521         }
9522         __MoleculeUnlock(mp);
9523         sMoleculeNotifyChangeAppearance(mp);
9524 }
9525
9526 void
9527 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x13, Double x23, const Double *sigmaptr)
9528 {
9529         Double d, dx;
9530         int u = 0;
9531         const Double log2 = 0.693147180559945;
9532         const Double pi22 = 19.7392088021787;  /* 2*pi**2 */
9533         Transform m1, m2;
9534         Aniso *anp;
9535         XtalCell *cp;
9536         Vector axis[3];
9537         Double val[3];
9538         if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
9539                 return;
9540         anp = mp->atoms[n1].aniso;
9541         __MoleculeLock(mp);
9542         if (anp == NULL) {
9543                 anp = (Aniso *)calloc(sizeof(Aniso), 1);
9544                 if (anp == NULL) {
9545                         __MoleculeUnlock(mp);
9546                         Panic("Low memory during setting anisotropic atom parameters");
9547                 }
9548                 mp->atoms[n1].aniso = anp;
9549         }
9550         switch (type) {
9551                 case 1: d = 1; dx = 0.5; break;
9552                 case 2: d = log2; dx = log2; break;
9553                 case 3: d = log2; dx = log2 * 0.5; break;
9554                 case 4: u = 1; d = 0.25; dx = 0.25; break;
9555                 case 5: u = 1; d = 0.25; dx = 0.125; break;
9556                 case 8: u = 1; d = pi22; dx = pi22; break;
9557                 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
9558                 case 10: d = pi22; dx = pi22; break;
9559                 default: d = dx = 1; break;
9560         }
9561         anp->bij[0] = x11 * d;
9562         anp->bij[1] = x22 * d;
9563         anp->bij[2] = x33 * d;
9564         anp->bij[3] = x12 * dx;
9565         anp->bij[4] = x13 * dx;
9566         anp->bij[5] = x23 * dx;
9567         if (sigmaptr != NULL) {
9568                 anp->has_bsig = 1;
9569                 anp->bsig[0] = sigmaptr[0] * d;
9570                 anp->bsig[1] = sigmaptr[1] * d;
9571                 anp->bsig[2] = sigmaptr[2] * d;
9572                 anp->bsig[3] = sigmaptr[3] * dx;
9573                 anp->bsig[4] = sigmaptr[4] * dx;
9574                 anp->bsig[5] = sigmaptr[5] * dx;
9575         } else {
9576                 anp->has_bsig = 0;
9577                 anp->bsig[0] = anp->bsig[1] = anp->bsig[2] = anp->bsig[3] = anp->bsig[4] = anp->bsig[5] = 0.0;
9578         }
9579         cp = mp->cell;
9580         if (cp != NULL && u == 1) {
9581                 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
9582                 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
9583                 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
9584                 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
9585                 anp->bij[4] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[3] * kDeg2Rad); */
9586                 anp->bij[5] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[4] * kDeg2Rad); */
9587                 if (sigmaptr != NULL) {
9588                         anp->bsig[0] *= cp->rcell[0] * cp->rcell[0];
9589                         anp->bsig[1] *= cp->rcell[1] * cp->rcell[1];
9590                         anp->bsig[2] *= cp->rcell[2] * cp->rcell[2];
9591                         anp->bsig[3] *= cp->rcell[0] * cp->rcell[1];
9592                         anp->bsig[4] *= cp->rcell[2] * cp->rcell[0];
9593                         anp->bsig[5] *= cp->rcell[1] * cp->rcell[2];
9594                 }
9595         }
9596         
9597         /*  Calculate the principal axes (in Cartesian coordinates)  */
9598         /*  The principal axes are the eigenvectors of matrix At(B^-1)A, where
9599                 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
9600                 in which x and z are the crystal-space and cartesian coordinates. */
9601         m1[0] = anp->bij[0] / pi22;
9602         m1[4] = anp->bij[1] / pi22;
9603         m1[8] = anp->bij[2] / pi22;
9604         m1[1] = m1[3] = anp->bij[3] / pi22;
9605         m1[2] = m1[6] = anp->bij[4] / pi22;
9606         m1[5] = m1[7] = anp->bij[5] / pi22;
9607         MatrixInvert(m1, m1);
9608         if (cp != NULL) {
9609                 memmove(m2, cp->rtr, sizeof(Mat33));
9610                 MatrixMul(m1, m1, m2);
9611                 MatrixTranspose(m2, m2);
9612                 MatrixMul(m1, m2, m1);
9613         }
9614         MatrixSymDiagonalize(m1, val, axis);
9615         for (u = 0; u < 3; u++) {
9616                 if (val[u] < 0) {
9617                         fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
9618                         val[u] = 0.001;
9619                 } else {
9620                         val[u] = 1 / sqrt(val[u]);
9621                 }
9622                 anp->pmat[u*3] = axis[u].x * val[u];
9623                 anp->pmat[u*3+1] = axis[u].y * val[u];
9624                 anp->pmat[u*3+2] = axis[u].z * val[u];
9625         }
9626         __MoleculeUnlock(mp);
9627 }
9628
9629 /*  Set the anisotropic parameter for atom idx according to the symop. If symop is not alive, nothing is done. */
9630 void
9631 MoleculeSetAnisoBySymop(Molecule *mp, int idx)
9632 {
9633         Atom *ap, *ap2;
9634         Transform t1, t2;
9635         if (mp == NULL || idx < 0 || idx >= mp->natoms)
9636                 return;
9637         ap = ATOM_AT_INDEX(mp->atoms, idx);
9638         if (!SYMOP_ALIVE(ap->symop))
9639                 return;
9640         ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
9641         if (ap2->aniso == NULL) {
9642                 if (ap->aniso != NULL) {
9643                         free(ap->aniso);
9644                         ap->aniso = NULL;
9645                 }
9646                 return;
9647         }
9648         if (ap->aniso == NULL)
9649                 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
9650         if (ap->symop.sym == 0 || ap->symop.sym >= mp->nsyms) {
9651                 /*  Just copy the aniso parameters  */
9652                 memmove(ap->aniso, ap2->aniso, sizeof(Aniso));
9653                 return;
9654         }
9655         memmove(t1, SYMMETRY_AT_INDEX(mp->syms, ap->symop.sym), sizeof(Transform));
9656         t1[9] = t1[10] = t1[11] = 0.0;
9657         memset(t2, 0, sizeof(Transform));
9658         t2[0] = ap2->aniso->bij[0];
9659         t2[4] = ap2->aniso->bij[1];
9660         t2[8] = ap2->aniso->bij[2];
9661         t2[1] = t2[3] = ap2->aniso->bij[3];
9662         t2[2] = t2[6] = ap2->aniso->bij[4];
9663         t2[5] = t2[7] = ap2->aniso->bij[5];
9664         TransformMul(t2, t1, t2);
9665         TransformInvert(t1, t1);
9666         TransformMul(t2, t2, t1);
9667         MoleculeSetAniso(mp, idx, 0, t2[0], t2[4], t2[8], t2[1], t2[2], t2[5], (ap2->aniso->has_bsig ? ap2->aniso->bsig : NULL));
9668 }
9669
9670 int
9671 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic, int convertCoordinates)
9672 {
9673         static Vector zeroVec = {0, 0, 0};
9674         XtalCell b;
9675         Transform cmat;
9676         int i, n;
9677         Atom *ap;
9678         if (mp == NULL)
9679                 return 0;
9680         if (mp->cell != NULL)
9681                 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
9682         else
9683                 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
9684         if (ax == NULL) {
9685                 if (mp->cell != NULL) {
9686                         free(mp->cell);
9687                         mp->needsMDRebuild = 1;
9688                 }
9689                 mp->cell = NULL;
9690                 return 0;
9691         }       
9692         memset(&b, 0, sizeof(b));
9693         b.axes[0] = (ax != NULL ? *ax : zeroVec);
9694         b.axes[1] = (ay != NULL ? *ay : zeroVec);
9695         b.axes[2] = (az != NULL ? *az : zeroVec);
9696         b.origin = *ao;
9697         memmove(b.flags, periodic, 3);
9698         if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
9699                 return -1;
9700         __MoleculeLock(mp);
9701         if (mp->cell == NULL) {
9702                 mp->needsMDRebuild = 1;
9703         } else {
9704                 if (mp->cell->has_sigma) {
9705                         /*  Keep the sigma  */
9706                         b.has_sigma = 1;
9707                         memmove(b.cellsigma, mp->cell->cellsigma, sizeof(mp->cell->cellsigma));
9708                 }
9709                 if ((b.flags[0] != mp->cell->flags[0]) || (b.flags[1] != mp->cell->flags[1]) || (b.flags[2] != mp->cell->flags[2])) {
9710                         mp->needsMDRebuild = 1;
9711                 }
9712                 free(mp->cell);
9713         }
9714         mp->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
9715         if (mp->cell != NULL) {
9716                 memmove(mp->cell, &b, sizeof(XtalCell));
9717                 TransformMul(cmat, b.tr, cmat);
9718                 /*  Update the coordinates (if requested)  */
9719                 if (convertCoordinates) {
9720                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9721                                 TransformVec(&(ap->r), cmat, &(ap->r));
9722                         }
9723                 }
9724                 
9725                 /*  Update the anisotropic parameters  */
9726                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9727                         Aniso *anp = ap->aniso;
9728                         if (anp != NULL) {
9729                                 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
9730                         }
9731                 }
9732                 n = 0;
9733         } else n = -2;  /*  Out of memory  */
9734         __MoleculeUnlock(mp);
9735         sMoleculeNotifyChangeAppearance(mp);
9736         return n;
9737 }
9738
9739 #pragma mark ====== Fragment manipulation ======
9740
9741 static void
9742 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
9743 {
9744         Atom *ap;
9745         Int i, *cp, idx2;
9746         if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
9747                 return;
9748         IntGroupAdd(result, idx, 1);
9749         ap = ATOM_AT_INDEX(mp->atoms, idx);
9750         cp = AtomConnectData(&ap->connect);
9751         for (i = 0; i < ap->connect.count; i++) {
9752                 idx2 = cp[i];
9753                 if (IntGroupLookup(result, idx2, NULL))
9754                         continue;
9755                 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, idx2)->anchor != NULL)
9756                         continue;  /*  bond between two pi_anchors is ignored  */
9757                 sMoleculeFragmentSub(mp, idx2, result, exatoms);
9758         }
9759         if (ap->anchor != NULL) {
9760                 cp = AtomConnectData(&ap->anchor->connect);
9761                 for (i = 0; i < ap->anchor->connect.count; i++) {
9762                         idx2 = cp[i];
9763                         if (IntGroupLookup(result, idx2, NULL))
9764                                 continue;
9765                         sMoleculeFragmentSub(mp, idx2, result, exatoms);
9766                 }
9767         }
9768 }
9769
9770 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
9771     not containing the atoms in exatoms  */
9772 IntGroup *
9773 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
9774 {
9775         IntGroup *result;
9776         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9777                 return NULL;
9778         result = IntGroupNew();
9779         sMoleculeFragmentSub(mp, n1, result, exatoms);
9780         return result;
9781 }
9782
9783 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
9784     not containing the atoms n2, n3, ... (terminated by -1)  */
9785 IntGroup *
9786 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
9787 {
9788         int i;
9789         IntGroup *exatoms, *result;
9790         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9791                 return NULL;
9792         exatoms = IntGroupNew();
9793         for (i = 0; i < argc; i++)
9794                 IntGroupAdd(exatoms, argv[i], 1);
9795         result = IntGroupNew();
9796         sMoleculeFragmentSub(mp, n1, result, exatoms);
9797         IntGroupRelease(exatoms);
9798         return result;
9799 }
9800
9801 /*  The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
9802     not containing the atoms in exatoms  */
9803 IntGroup *
9804 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
9805 {
9806         IntGroupIterator iter;
9807         IntGroup *result;
9808         int i;
9809         if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
9810                 return NULL;
9811         IntGroupIteratorInit(inatoms, &iter);
9812         result = IntGroupNew();
9813         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9814                 sMoleculeFragmentSub(mp, i, result, exatoms);
9815         }
9816         IntGroupIteratorRelease(&iter);
9817         return result;
9818 }
9819
9820 /*  Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
9821     group is bound to the rest of the molecule via only one bond.
9822         If the result is true, then the atoms belonging to the (only) bond are returned
9823         in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
9824         and n2 can be NULL, if those informations are not needed.  */
9825 int
9826 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
9827 {
9828         Int i, i1, i2, j, k, bond_count, nval1, nval2, *cp;
9829         Atom *ap;
9830         if (mp == NULL || mp->natoms == 0 || group == NULL)
9831                 return 0;  /*  Invalid arguments  */
9832         bond_count = 0;
9833         for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
9834                 i2 = IntGroupGetEndPoint(group, i);
9835                 for (j = i1; j < i2; j++) {
9836                         if (j < 0 || j >= mp->natoms)
9837                                 return 0;  /*  Invalid atom group  */
9838                         ap = ATOM_AT_INDEX(mp->atoms, j);
9839                         cp = AtomConnectData(&ap->connect);
9840                         for (k = 0; k < ap->connect.count; k++) {
9841                                 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, cp[k])->anchor != NULL)
9842                                         continue;  /*  Ignore bond between two pi_anchors  */
9843                                 if (IntGroupLookup(group, cp[k], NULL) == 0) {
9844                                         bond_count++;
9845                                         nval1 = j;
9846                                         nval2 = cp[k];
9847                                         if (bond_count > 1)
9848                                                 return 0;  /*  Too many bonds  */
9849                                 }
9850                         }
9851                         if (ap->anchor != NULL) {
9852                                 cp = AtomConnectData(&ap->anchor->connect);
9853                                 for (k = 0; k < ap->anchor->connect.count; k++) {
9854                                         if (IntGroupLookup(group, cp[k], NULL) == 0) {
9855                                                 bond_count++;
9856                                                 nval1 = j;
9857                                                 nval2 = cp[k];
9858                                                 if (bond_count > 1)
9859                                                         return 0;  /*  Too many bonds  */
9860                                         }
9861                                 }                                       
9862                         }
9863                 }
9864         }
9865         if (bond_count == 1) {
9866                 if (n1 != NULL)
9867                         *n1 = nval1;
9868                 if (n2 != NULL)
9869                         *n2 = nval2;
9870                 return 1;
9871         } else {
9872                 return 0;
9873         }       
9874 }
9875
9876 /*  Returns non-zero if the given group is 'rotatable' in the molecule. The group
9877     is said to be 'rotatable' when either of the following conditions are met; (1)
9878         the group is detachable, or (2) the group consists of two bonded atoms that define
9879         a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
9880         (either a new IntGroup or 'group' with incremented reference count; thus the caller
9881         is responsible for releasing the returned value).  */
9882 int
9883 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
9884 {
9885         int i1, i2;
9886         if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
9887                 if (rotGroup != NULL) {
9888                         IntGroupRetain(group);
9889                         *rotGroup = group;
9890                 }
9891                 return 1;
9892         }
9893         if (group != NULL && IntGroupGetCount(group) == 2) {
9894                 i1 = IntGroupGetNthPoint(group, 0);
9895                 i2 = IntGroupGetNthPoint(group, 1);
9896                 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
9897                         IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
9898                         if (frag == NULL)
9899                                 return 0;
9900                         i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
9901                         if (i1 == 0) {
9902                                 IntGroupRelease(frag);
9903                                 if (rotGroup != NULL)
9904                                         *rotGroup = NULL;
9905                                 return 0;
9906                         }
9907                         if (rotGroup != NULL)
9908                                 *rotGroup = frag;
9909                         else if (frag != NULL)
9910                                 IntGroupRelease(frag);
9911                         return i1;
9912                 }
9913         }
9914         return 0;
9915 }
9916
9917 #pragma mark ====== Multiple frame ======
9918
9919 int
9920 MoleculeGetNumberOfFrames(Molecule *mp)
9921 {
9922         if (mp == NULL)
9923                 return 0;
9924         if (mp->nframes <= 0) {
9925                 /*  Recalculate  */
9926                 int i, n;
9927                 Atom *ap;
9928                 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9929                         if (ap->nframes > n)
9930                                 n = ap->nframes;
9931                 }
9932                 if (n == 0)
9933                         n = 1;
9934                 mp->nframes = n;
9935         }
9936         return mp->nframes;
9937 }
9938
9939 int
9940 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
9941 {
9942         int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
9943         Vector *tempv, *vp;
9944         Atom *ap;
9945         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
9946                 return -1;
9947
9948         n_old = MoleculeGetNumberOfFrames(mp);
9949         n_new = n_old + count;
9950         last_inserted = IntGroupGetNthPoint(group, count - 1);
9951         if (n_new <= last_inserted) {
9952                 exframes = last_inserted - n_new + 1;  /*  number of extra frames that will be silently inserted  */
9953                 n_new += exframes;
9954         } else exframes = 0;
9955
9956         tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4);  /*  "*4" for handling cells  */
9957         if (tempv == NULL)
9958                 return -1;
9959
9960         __MoleculeLock(mp);
9961
9962         /*  Copy back the current coordinates  */
9963         /*  No change in the current coordinates, but the frame buffer is updated  */
9964         MoleculeSelectFrame(mp, mp->cframe, 1); 
9965         
9966         /*  Expand ap->frames for all atoms  */
9967         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9968                 if (ap->frames == NULL)
9969                         vp = (Vector *)calloc(sizeof(Vector), n_new);
9970                 else
9971                         vp = (Vector *)realloc(ap->frames, sizeof(Vector) * n_new);
9972                 if (vp == NULL) {
9973                         __MoleculeUnlock(mp);
9974                         return -1;
9975                 }
9976                 for (j = ap->nframes; j < n_new; j++)
9977                         vp[j] = ap->r;
9978                 ap->frames = vp;
9979         }
9980         if (mp->cell != NULL) {
9981                 j = mp->nframe_cells;
9982                 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, n_new - 1, NULL);
9983                 for (i = j; i < n_new; i++) {
9984                         /*  Set the current cell parameters to the expanded frames  */
9985                         mp->frame_cells[i * 4] = mp->cell->axes[0];
9986                         mp->frame_cells[i * 4 + 1] = mp->cell->axes[1];
9987                         mp->frame_cells[i * 4 + 2] = mp->cell->axes[2];
9988                         mp->frame_cells[i * 4 + 3] = mp->cell->origin;
9989                 }
9990         }
9991         
9992         /*  group = [n0..n1-1, n2..n3-1, ...]  */
9993         /*  s = t = 0,  */
9994         /*  tempv[0..n0-1] <- ap[0..n0-1], s += n0,
9995             tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
9996                 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
9997                 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
9998                 ...
9999                 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
10000                 At last, s will become n_old and t will become count.  */
10001         for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10002                 int s, t, ns, ne, mult;
10003                 Vector cr;
10004                 ne = s = t = 0;
10005                 if (i == mp->natoms) {
10006                         if (mp->cell == NULL || mp->frame_cells == NULL)
10007                                 break;
10008                         vp = mp->frame_cells;
10009                         mult = 4;
10010                 } else {
10011                         cr = ap->r;
10012                         vp = ap->frames;
10013                         mult = 1;
10014                 }
10015                 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10016                         if (ns > ne) {
10017                                 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
10018                                 s += ns - ne;
10019                         }
10020                         ne = IntGroupGetEndPoint(group, j);
10021                         while (ns < ne) {
10022                                 if (i == mp->natoms) {
10023                                         if (inFrameCell != NULL) {
10024                                                 tempv[ns * 4] = inFrameCell[t * 4];
10025                                                 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
10026                                                 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
10027                                                 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
10028                                         } else {
10029                                                 tempv[ns * 4] = mp->cell->axes[0];
10030                                                 tempv[ns * 4 + 1] = mp->cell->axes[1];
10031                                                 tempv[ns * 4 + 2] = mp->cell->axes[2];
10032                                                 tempv[ns * 4 + 3] = mp->cell->origin;
10033                                         }
10034                                 } else {
10035                                         if (inFrame != NULL)
10036                                                 tempv[ns] = inFrame[natoms * t + i];
10037                                         else
10038                                                 tempv[ns] = cr;
10039                                 }
10040                                 t++;
10041                                 ns++;
10042                         }
10043                 }
10044                 if (n_new > ne) {
10045                         memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
10046                         s += n_new - ne;
10047                 }
10048                 if (i < mp->natoms)
10049                         ap->nframes = n_new;
10050                 memmove(vp, tempv, sizeof(Vector) * mult * n_new);
10051         }
10052         free(tempv);
10053         mp->nframes = n_new;
10054         MoleculeSelectFrame(mp, last_inserted, 0);
10055         MoleculeIncrementModifyCount(mp);
10056         __MoleculeUnlock(mp);
10057         return count;
10058 }
10059
10060 int
10061 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
10062 {
10063         int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
10064         Vector *tempv, *vp;
10065         Atom *ap;
10066         IntGroup *group, *group2;
10067
10068         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
10069                 return -1;
10070
10071         /*  outFrame[] should have enough size for Vector * natoms * group.count  */
10072         memset(outFrame, 0, sizeof(Vector) * natoms * count);
10073         if (mp->cell != NULL && mp->frame_cells != NULL)
10074                 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
10075
10076         n_old = MoleculeGetNumberOfFrames(mp);
10077         if (n_old == 1)
10078                 return -2;  /*  Cannot delete last frame  */
10079
10080         group = IntGroupNew();
10081         group2 = IntGroupNewWithPoints(0, n_old, -1);
10082         IntGroupIntersect(inGroup, group2, group);
10083         IntGroupRelease(group2);
10084         count = IntGroupGetCount(group);
10085         n_new = n_old - count;
10086         if (n_new < 1) {
10087                 IntGroupRelease(group);
10088                 return -2;  /*  Trying to delete too many frames  */
10089         }
10090         tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4);  /*  "*4" for handling cells  */
10091         if (tempv == NULL) {
10092                 IntGroupRelease(group);
10093                 return -1;
10094         }
10095
10096         __MoleculeLock(mp);
10097
10098         /*  Copy back the current coordinates  */
10099         /*  No change in the current coordinates, but the frame buffer is updated  */
10100         MoleculeSelectFrame(mp, mp->cframe, 1); 
10101
10102         /*  Determine which frame should be selected after removal is completed  */
10103         {
10104                 int n1;
10105                 if (IntGroupLookup(group, mp->cframe, &i)) {
10106                         /*  cframe will be removed  */
10107                         n1 = IntGroupGetStartPoint(group, i) - 1;
10108                         if (n1 < 0)
10109                                 n1 = IntGroupGetEndPoint(group, i);
10110                 } else n1 = mp->cframe;
10111                 /*  Change to that frame  */
10112                 MoleculeSelectFrame(mp, n1, 0);
10113                 group2 = IntGroupNewFromIntGroup(group);
10114                 IntGroupReverse(group2, 0, n_old);
10115                 new_cframe = IntGroupLookupPoint(group2, n1);
10116                 if (new_cframe < 0)
10117                         return -3;  /*  This cannot happen  */
10118                 IntGroupRelease(group2);
10119         }
10120
10121         /*  group = [n0..n1-1, n2..n3-1, ...]  */
10122         /*  s = t = 0, */
10123         /*  tempv[0..n0-1] -> ap[0..n0-1], s += n0,
10124             tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
10125                 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
10126                 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
10127                 ...
10128                 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
10129                 At last, s will become n_new and t will become count.  */
10130         nframes = 0;
10131         for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10132                 int s, t, j, ns, ne;
10133                 int mult;
10134                 /*  if i == mp->natoms, mp->frame_cells is handled  */
10135                 if (i == mp->natoms) {
10136                         if (mp->cell == NULL || mp->frame_cells == NULL)
10137                                 break;
10138                         mult = 4;
10139                         vp = mp->frame_cells;
10140                         old_count = n_old;
10141                 } else {
10142                         mult = 1;
10143                         vp = ap->frames;
10144                         if (vp == NULL) {
10145                                 ap->frames = vp = (Vector *)calloc(sizeof(Vector), n_old);
10146                                 if (vp == NULL) {
10147                                         __MoleculeUnlock(mp);
10148                                         return -1;
10149                                 }
10150                         }
10151                         old_count = ap->nframes;
10152                 }
10153
10154                 /*  Copy vp to tempv  */
10155                 memset(tempv, 0, sizeof(Vector) * mult * n_old);
10156                 memmove(tempv, vp, sizeof(Vector) * mult * (old_count > n_old ? n_old : old_count));
10157                 ne = ns = s = t = 0;
10158                 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10159                         if (ns > n_old)
10160                                 ns = n_old;
10161                         if (ns > ne) {
10162                                 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (ns - ne));
10163                                 s += ns - ne;
10164                         }
10165                         ne = IntGroupGetEndPoint(group, j);
10166                         if (ne > n_old)
10167                                 ne = n_old;
10168                         while (ns < ne) {
10169                                 if (i < mp->natoms)
10170                                         outFrame[natoms * t + i] = tempv[ns];
10171                                 else if (outFrameCell != NULL) {
10172                                         outFrameCell[t * 4] = tempv[ns * 4];
10173                                         outFrameCell[t * 4 + 1] = tempv[ns * 4 + 1];
10174                                         outFrameCell[t * 4 + 2] = tempv[ns * 4 + 2];
10175                                         outFrameCell[t * 4 + 3] = tempv[ns * 4 + 3];
10176                                 }
10177                                 t++;
10178                                 ns++;
10179                         }
10180                 }
10181                 if (n_old > ne) {
10182                         memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (n_old - ne));
10183                         s += n_old - ne;
10184                 }
10185                 if (i < mp->natoms)
10186                         ap->nframes = s;
10187                 if (nframes < s)
10188                         nframes = s;
10189                 if (s <= 1) {
10190                         if (i < mp->natoms) {
10191                                 free(ap->frames);
10192                                 ap->frames = NULL;
10193                                 ap->nframes = 0;
10194                         } else {
10195                                 free(mp->frame_cells);
10196                                 mp->frame_cells = NULL;
10197                                 mp->nframe_cells = 0;
10198                         }
10199                 } else {
10200                         if (i < mp->natoms)
10201                                 ap->frames = (Vector *)realloc(ap->frames, sizeof(Vector) * s);
10202                         else {
10203                                 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, s - 1, NULL);
10204                                 mp->nframe_cells = s;
10205                         }
10206                 }
10207         }
10208         free(tempv);
10209         mp->nframes = nframes;
10210         
10211         /*  Select the "last" frame; do not "copy back" the coordinates to the frame table  */
10212 /*      i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
10213         MoleculeSelectFrame(mp, new_cframe, 0);
10214
10215         IntGroupRelease(group);
10216
10217         MoleculeIncrementModifyCount(mp);
10218         __MoleculeUnlock(mp);
10219         return count;
10220 }
10221
10222 int
10223 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
10224 {
10225         int i, cframe, nframes, modified;
10226         Atom *ap;
10227         cframe = mp->cframe;
10228         nframes = MoleculeGetNumberOfFrames(mp);
10229         if (frame == -1)
10230                 frame = mp->cframe;
10231         if (mp == NULL || mp->natoms == 0 || frame < 0 || frame >= nframes)
10232                 return -1;
10233         modified = 0;
10234         __MoleculeLock(mp);
10235         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10236                 if (copyback && cframe >= 0 && cframe < ap->nframes) {
10237                         /*  Write the current coordinate back to the frame array  */
10238                         ap->frames[cframe] = ap->r;
10239                 }
10240                 if (frame != cframe && frame >= 0 && frame < ap->nframes) {
10241                         /*  Read the coordinate from the frame array  */
10242                         ap->r = ap->frames[frame];
10243                         modified = 1;
10244                 }
10245         }
10246
10247         if (mp->cell != NULL && mp->frame_cells != NULL) {
10248                 /*  Write the current cell back to the frame_cells array  */
10249                 if (copyback && cframe >= 0) {
10250                         Vector *vp = (Vector *)AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, cframe, NULL);
10251                         vp[0] = mp->cell->axes[0];
10252                         vp[1] = mp->cell->axes[1];
10253                         vp[2] = mp->cell->axes[2];
10254                         vp[3] = mp->cell->origin;
10255                 }
10256                 /*  Set the cell from the frame array  */
10257                 if (frame != cframe && frame >= 0 && frame < mp->nframe_cells) {
10258                         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);
10259                         modified = 1;
10260                         MoleculeAmendBySymmetry(mp, NULL, NULL, NULL);
10261                 }
10262         }
10263         mp->cframe = frame;
10264         if (modified)
10265                 mp->needsMDCopyCoordinates = 1;
10266         __MoleculeUnlock(mp);
10267         sMoleculeNotifyChangeAppearance(mp);
10268         return frame;
10269 }
10270
10271 /*  If molecule is multi-frame, then flush the current information to the frame buffer.
10272     Returns the number of frames.  */
10273 int
10274 MoleculeFlushFrames(Molecule *mp)
10275 {
10276         int nframes = MoleculeGetNumberOfFrames(mp);
10277         if (nframes > 1)
10278                 MoleculeSelectFrame(mp, mp->cframe, 1);
10279         return nframes;
10280 }
10281
10282 #pragma mark ====== Pi Atoms ======
10283
10284 void
10285 MoleculeCalculatePiAnchorPosition(Molecule *mol, int idx)
10286 {
10287         Atom *ap, *ap2;
10288         Int i, n, *ip;
10289         if (mol == NULL || idx < 0 || idx >= mol->natoms)
10290                 return;
10291         ap = ATOM_AT_INDEX(mol->atoms, idx);
10292         if (ap->anchor == NULL)
10293                 return;
10294         ip = AtomConnectData(&ap->anchor->connect);
10295         n = ap->anchor->connect.count;
10296         VecZero(ap->r);
10297         for (i = 0; i < ap->anchor->connect.count; i++) {
10298                 ap2 = ATOM_AT_INDEX(mol->atoms, ip[i]);
10299                 VecScaleInc(ap->r, ap2->r, ap->anchor->coeffs[i]);
10300         }
10301 }
10302
10303 int
10304 MoleculeSetPiAnchorList(Molecule *mol, Int idx, Int nentries, Int *entries, Double *weights, Int *nUndoActions, struct MolAction ***undoActions)
10305 {
10306         Atom *ap;
10307         Int *ip, i, j, n, *np;
10308         Double d;
10309         if (mol == NULL || idx < 0 || idx >= mol->natoms || nentries <= 1)
10310                 return -1;  /*  Invalid argument  */
10311         if (weights != NULL) {
10312                 d = 0.0;
10313                 for (i = 0; i < nentries; i++) {
10314                         if (weights[i] <= 0.0) {
10315                                 return 10;  /*  Weights must be positive  */
10316                         }
10317                         d += weights[i];
10318                 }
10319                 d = 1.0 / d;
10320         } else d = 1.0 / nentries;
10321         ap = ATOM_AT_INDEX(mol->atoms, idx);
10322         if (ap->anchor != NULL) {
10323                 /*  Already an anchor: check if bonds/angles/dihedrals have entries related to this anchor  */
10324                 IntGroup *bg, *ag, *dg, *ig;
10325                 Int *ibuf, ibufsize;
10326                 MolAction *act;
10327                 bg = ag = dg = ig = NULL;
10328                 ip = AtomConnectData(&ap->anchor->connect);
10329                 for (i = 0; i < ap->anchor->connect.count; i++) {
10330                         n = ip[i];
10331                         for (j = 0; j < nentries; j++) {
10332                                 if (n == entries[j])
10333                                         break;
10334                         }
10335                         if (j == nentries) {
10336                                 /*  This entry will disappear: if any bond/angle/dihedral has idx-n pair, that should be removed.  */
10337                                 for (j = 0, np = mol->bonds; j < mol->nbonds; j++, np += 2) {
10338                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[0])) {
10339                                                 if (bg == NULL)
10340                                                         bg = IntGroupNew();
10341                                                 IntGroupAdd(bg, j, 1);
10342                                         }
10343                                 }
10344                                 for (j = 0, np = mol->angles; j < mol->nangles; j++, np += 3) {
10345                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) ||
10346                                                 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1])) {
10347                                                 if (ag == NULL)
10348                                                         ag = IntGroupNew();
10349                                                 IntGroupAdd(ag, j, 1);
10350                                         }
10351                                 }
10352                                 for (j = 0, np = mol->dihedrals; j < mol->ndihedrals; j++, np += 4) {
10353                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) || (idx == np[2] && n == np[3]) ||
10354                                                 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[3] && n == np[2])) {
10355                                                 if (dg == NULL)
10356                                                         dg = IntGroupNew();
10357                                                 IntGroupAdd(dg, j, 1);
10358                                         }
10359                                 }
10360                                 for (j = 0, np = mol->impropers; j < mol->nimpropers; j++, np += 4) {
10361                                         if ((idx == np[0] && n == np[2]) || (idx == np[1] && n == np[2]) || (idx == np[3] && n == np[2]) ||
10362                                                 (idx == np[2] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[2] && n == np[3])) {
10363                                                 if (ig == NULL)
10364                                                         ig = IntGroupNew();
10365                                                 IntGroupAdd(ig, j, 1);
10366                                         }
10367                                 }
10368                         }
10369                 }
10370                 ibuf = NULL;
10371                 ibufsize = 0;
10372                 if (ig != NULL) {
10373                         /*  Delete impropers (with undo info) */
10374                         i = IntGroupGetCount(ig);
10375                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
10376                         MoleculeDeleteImpropers(mol, ibuf, ig);
10377                         if (nUndoActions != NULL && undoActions != NULL) {
10378                                 act = MolActionNew(gMolActionAddImpropers, i * 4, ibuf, ig);
10379                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10380                         }
10381                         IntGroupRelease(ig);
10382                 }
10383                 if (dg != NULL) {
10384                         /*  Delete dihedrals (with undo info)  */
10385                         i = IntGroupGetCount(dg);
10386                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
10387                         MoleculeDeleteDihedrals(mol, ibuf, dg);
10388                         if (nUndoActions != NULL && undoActions != NULL) {
10389                                 act = MolActionNew(gMolActionAddDihedrals, i * 4, ibuf, dg);
10390                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10391                         }
10392                         IntGroupRelease(dg);
10393                 }
10394                 if (ag != NULL) {
10395                         /*  Delete angles (with undo info) */
10396                         i = IntGroupGetCount(ag);
10397                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 3 - 1, NULL);
10398                         MoleculeDeleteAngles(mol, ibuf, ag);
10399                         if (nUndoActions != NULL && undoActions != NULL) {
10400                                 act = MolActionNew(gMolActionAddAngles, i * 3, ibuf, ag);
10401                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10402                         }
10403                         IntGroupRelease(ag);
10404                 }
10405                 if (bg != NULL) {
10406                         /*  Delete bonds (with undo info) */
10407                         i = IntGroupGetCount(bg);
10408                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 2 - 1, NULL);
10409                         MoleculeDeleteBonds(mol, ibuf, bg, NULL, NULL);
10410                         if (nUndoActions != NULL && undoActions != NULL) {
10411                                 act = MolActionNew(gMolActionAddBondsForUndo, i * 2, ibuf, bg);
10412                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10413                         }
10414                         IntGroupRelease(bg);
10415                 }
10416         } else {
10417                 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
10418         }
10419         AtomConnectResize(&ap->anchor->connect, nentries);
10420         memmove(AtomConnectData(&ap->anchor->connect), entries, sizeof(Int) * nentries);
10421         AssignArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), nentries - 1, NULL);
10422         if (weights != NULL) {
10423                 memmove(ap->anchor->coeffs, weights, sizeof(Double) * nentries);
10424                 for (i = 0; i < nentries; i++)
10425                         ap->anchor->coeffs[i] *= d;   /*  Normalize weight  */
10426         } else {
10427                 for (i = 0; i < nentries; i++)
10428                         ap->anchor->coeffs[i] = d;
10429         }
10430         MoleculeCalculatePiAnchorPosition(mol, idx);
10431         return 0;
10432 }
10433
10434 #pragma mark ====== MO calculation ======
10435
10436 /*  Calculate an MO value for a single point.  */
10437 /*  Index is the MO number (1-based)  */
10438 /*  tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom.  */
10439 static Double
10440 sCalcMOPoint(const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
10441 {
10442         ShellInfo *sp;
10443         PrimInfo *pp;
10444         Double val, tval, *cnp, *tmpp, *mobasep, *mop;
10445         Int i, j;
10446         /*  Cache dr and |dr|^2  */
10447         for (i = 0; i < bset->natoms; i++) {
10448                 Vector r = bset->pos[i];
10449                 tmp[i * 4] = r.x = vp->x - r.x;
10450                 tmp[i * 4 + 1] = r.y = vp->y - r.y;
10451                 tmp[i * 4 + 2] = r.z = vp->z - r.z;
10452                 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
10453         }
10454         /*  Iterate over all shells  */
10455         val = 0.0;
10456         mobasep = bset->mo + (index - 1) * bset->ncomps;
10457         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
10458                 pp = bset->priminfos + sp->p_idx;
10459                 cnp = bset->cns + sp->cn_idx;
10460                 tmpp = tmp + sp->a_idx * 4;
10461                 mop = mobasep + sp->m_idx;
10462                 switch (sp->sym) {
10463                         case kGTOType_S: {
10464                                 tval = 0;
10465                                 for (j = 0; j < sp->nprim; j++) {
10466                                         tval += *cnp++ * exp(-pp->A * tmpp[3]);
10467                                         pp++;
10468                                 }
10469                                 val += mop[0] * tval;
10470                                 break;
10471                         }
10472                         case kGTOType_P: {
10473                                 Double x, y, z;
10474                                 x = y = z = 0;
10475                                 for (j = 0; j < sp->nprim; j++) {
10476                                         tval = exp(-pp->A * tmpp[3]);
10477                                         x += *cnp++ * tval;
10478                                         y += *cnp++ * tval;
10479                                         z += *cnp++ * tval;
10480                                         pp++;
10481                                 }
10482                                 x *= mop[0] * tmpp[0];
10483                                 y *= mop[1] * tmpp[1];
10484                                 z *= mop[2] * tmpp[2];
10485                                 val += x + y + z;
10486                                 break;
10487                         }
10488                         case kGTOType_SP: {
10489                                 Double t, x, y, z;
10490                                 t = x = y = z = 0;
10491                                 for (j = 0; j < sp->nprim; j++) {
10492                                         tval = exp(-pp->A * tmpp[3]);
10493                                         t += *cnp++ * tval;
10494                                         x += *cnp++ * tval;
10495                                         y += *cnp++ * tval;
10496                                         z += *cnp++ * tval;
10497                                         pp++;
10498                                 }
10499                                 t *= mop[0];
10500                                 x *= mop[1] * tmpp[0];
10501                                 y *= mop[2] * tmpp[1];
10502                                 z *= mop[3] * tmpp[2];
10503                                 val += t + x + y + z;
10504                                 break;
10505                         }
10506                         case kGTOType_D: {
10507                                 Double xx, yy, zz, xy, xz, yz;
10508                                 xx = yy = zz = xy = xz = yz = 0;
10509                                 for (j = 0; j < sp->nprim; j++) {
10510                                         tval = exp(-pp->A * tmpp[3]);
10511                                         xx += *cnp++ * tval;
10512                                         yy += *cnp++ * tval;
10513                                         zz += *cnp++ * tval;
10514                                         xy += *cnp++ * tval;
10515                                         xz += *cnp++ * tval;
10516                                         yz += *cnp++ * tval;
10517                                         pp++;
10518                                 }
10519                                 xx *= mop[0] * tmpp[0] * tmpp[0];
10520                                 yy *= mop[1] * tmpp[1] * tmpp[1];
10521                                 zz *= mop[2] * tmpp[2] * tmpp[2];
10522                                 xy *= mop[3] * tmpp[0] * tmpp[1];
10523                                 xz *= mop[4] * tmpp[0] * tmpp[2];
10524                                 yz *= mop[5] * tmpp[1] * tmpp[2];
10525                                 val += xx + yy + zz + xy + xz + yz;
10526                                 break;
10527                         }
10528                         case kGTOType_D5: {
10529                                 Double d0, d1p, d1n, d2p, d2n;
10530                                 d0 = d1p = d1n = d2p = d2n = 0;
10531                                 for (j = 0; j < sp->nprim; j++) {
10532                                         tval = exp(-pp->A * tmpp[3]);
10533                                         d0 += *cnp++ * tval;
10534                                         d1p += *cnp++ * tval;
10535                                         d1n += *cnp++ * tval;
10536                                         d2p += *cnp++ * tval;
10537                                         d2n += *cnp++ * tval;
10538                                         pp++;
10539                                 }
10540                                 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
10541                                 d1p *= mop[1] * tmpp[0] * tmpp[2];
10542                                 d1n *= mop[2] * tmpp[1] * tmpp[2];
10543                                 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
10544                                 d2n *= mop[4] * tmpp[0] * tmpp[1];
10545                                 val += d0 + d1p + d1n + d2p + d2n;
10546                                 break;
10547                         }
10548                         /*  TODO: Support F/F7 and G/G9 type orbitals  */
10549                 }
10550         }
10551         return val;
10552 }
10553
10554 /*  Calculate one MO. The input vectors should be in bohr unit (angstrom * 1.889725989 = kAngstrom2Bohr).  */
10555 /*  mono is the MO number (1-based)  */
10556 int
10557 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)
10558 {
10559         int ix, iy, iz, n, nn;
10560         Cube *cp;
10561         Double *tmp;
10562         if (mp == NULL || mp->bset == NULL)
10563                 return -1;
10564         if (mp->bset->cns == NULL) {
10565                 if (sSetupGaussianCoefficients(mp->bset) != 0)
10566                         return -1;
10567         }
10568         cp = (Cube *)calloc(sizeof(Cube), 1);
10569         if (cp == NULL) {
10570                 return -1;
10571         }
10572         cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
10573         if (cp->dp == NULL) {
10574                 free(cp);
10575                 return -1;
10576         }
10577         cp->idn = mono;
10578         cp->origin = *op;
10579         cp->dx = *dxp;
10580         cp->dy = *dyp;
10581         cp->dz = *dzp;
10582         cp->nx = nx;
10583         cp->ny = ny;
10584         cp->nz = nz;
10585         
10586         /*  TODO: use multithread  */
10587         tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms * 4);
10588         if (tmp == NULL) {
10589                 free(cp->dp);
10590                 free(cp);
10591                 return -1;
10592         }
10593         n = nn = 0;
10594         for (ix = 0; ix < nx; ix++) {
10595                 Vector p;
10596                 for (iy = 0; iy < ny; iy++) {
10597                         for (iz = 0; iz < nz; iz++) {
10598                                 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
10599                                 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
10600                                 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
10601                                 cp->dp[n++] = sCalcMOPoint(mp->bset, mono, &p, tmp);
10602                         }
10603                         if (callback != NULL && n - nn > 100) {
10604                                 nn = n;
10605                                 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
10606                                         free(cp->dp);
10607                                         free(cp);
10608                                         free(tmp);
10609                                         return -2;  /*  User interrupt  */
10610                                 }
10611                         }
10612                 }
10613         }
10614         free(tmp);
10615
10616         AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
10617         return mp->bset->ncubes - 1;
10618 }
10619
10620 int
10621 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
10622 {
10623         int i;
10624         Vector rmin, rmax, *vp;
10625         Double dr, dx, dy, dz;
10626         if (mp == NULL || mp->bset == NULL || mp->bset->natoms == 0)
10627                 return -1;
10628         if (npoints <= 0)
10629                 npoints = 1000000;
10630         rmin.x = rmin.y = rmin.z = 1e10;
10631         rmax.x = rmax.y = rmax.z = -1e10;
10632         for (i = 0, vp = mp->bset->pos; i < mp->bset->natoms; i++, vp++) {
10633                 dr = RadiusForAtomicNumber(ATOM_AT_INDEX(mp->atoms, i)->atomicNumber);
10634                 if (dr == 0.0)
10635                         dr = 1.0;
10636                 dr = dr * kAngstrom2Bohr * 3.0 + 2.0;
10637                 if (rmin.x > vp->x - dr)
10638                         rmin.x = vp->x - dr;
10639                 if (rmin.y > vp->y - dr)
10640                         rmin.y = vp->y - dr;
10641                 if (rmin.z > vp->z - dr)
10642                         rmin.z = vp->z - dr;
10643                 if (rmax.x < vp->x + dr)
10644                         rmax.x = vp->x + dr;
10645                 if (rmax.y < vp->y + dr)
10646                         rmax.y = vp->y + dr;
10647                 if (rmax.z < vp->z + dr)
10648                         rmax.z = vp->z + dr;
10649         }
10650         dx = rmax.x - rmin.x;
10651         dy = rmax.y - rmin.y;
10652         dz = rmax.z - rmin.z;
10653         dr = pow(dx * dy * dz / npoints, 1.0/3.0);
10654         *nx = floor(dx / dr + 0.5);
10655         *ny = floor(dy / dr + 0.5);
10656         *nz = floor(dz / dr + 0.5);
10657         if (*nx == 0)
10658                 *nx = 1;
10659         if (*ny == 0)
10660                 *ny = 1;
10661         if (*nz == 0)
10662                 *nz = 1;
10663         *op = rmin;
10664         xp->x = yp->y = zp->z = dr;
10665         xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
10666         return 0;
10667 }
10668
10669 const Cube *
10670 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
10671 {
10672         if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
10673                 return NULL;
10674         return mp->bset->cubes[index];
10675 }
10676
10677 int
10678 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
10679 {
10680         int i;
10681         if (mp == NULL || mp->bset == NULL)
10682                 return -1;
10683         for (i = 0; i < mp->bset->ncubes; i++) {
10684                 if (mp->bset->cubes[i]->idn == mono)
10685                         return i;
10686         }
10687         return -1;
10688 }
10689
10690 int
10691 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
10692 {
10693         int n;
10694         if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
10695                 return -1;
10696         CubeRelease(mp->bset->cubes[index]);
10697         if (index < n - 1)
10698                 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
10699         if (--(mp->bset->ncubes) == 0) {
10700                 free(mp->bset->cubes);
10701                 mp->bset->cubes = NULL;
10702         }
10703         return mp->bset->ncubes;
10704 }
10705
10706 int
10707 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
10708 {
10709         const Cube *cp;
10710         int i, j, k, n;
10711         FILE *fp;
10712         if (mp == NULL || mp->bset == NULL)
10713                 return -1;  /*  Molecule or the basis set information is empty  */
10714         cp = MoleculeGetCubeAtIndex(mp, index);
10715         if (cp == NULL)
10716                 return -2;  /*  MO not yet calculated  */
10717         fp = fopen(fname, "wb");
10718         if (fp == NULL)
10719                 return -3;  /*  Cannot create file  */
10720
10721         /*  Comment lines  */
10722         fprintf(fp, "%s MO=%d\n", comment, cp->idn);
10723         fprintf(fp, " MO coefficients\n");
10724         
10725         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms), cp->origin.x, cp->origin.y, cp->origin.z);
10726         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx, cp->dx.x, cp->dx.y, cp->dx.z);
10727         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny, cp->dy.x, cp->dy.y, cp->dy.z);
10728         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz, cp->dz.x, cp->dz.y, cp->dz.z);
10729         
10730         /*  Atomic information  */
10731         for (i = 0; i < mp->bset->natoms; i++) {
10732                 Vector *vp = mp->bset->pos + i;
10733                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
10734                 /*  The second number should actually be the effective charge  */
10735                 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber, vp->x, vp->y, vp->z);
10736         }
10737         fprintf(fp, "%5d%5d\n", 1, 1);
10738         
10739         /*  3D data  */
10740         for (i = n = 0; i < cp->nx; i++) {
10741                 for (j = 0; j < cp->ny; j++) {
10742                         for (k = 0; k < cp->nz; k++) {
10743                                 fprintf(fp, " %12.5e", cp->dp[n++]);
10744                                 if (k == cp->nz - 1 || k % 6 == 5)
10745                                         fprintf(fp, "\n");
10746                         }
10747                 }
10748         }
10749         fclose(fp);
10750         return 0;
10751 }