OSDN Git Service

The atomic radii for graphical display are corrected; the van der Waals radii are...
[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 #include <float.h>
23
24 #include "Missing.h"
25 #include "Dcd.h"
26 #include "MD/MDCore.h"
27 #include "MD/MDPressure.h"
28
29 static Molecule *sMoleculeRoot = NULL;
30 static int sMoleculeUntitledCount = 0;
31
32 Int gSizeOfAtomRecord = sizeof(Atom);
33
34 /*  These are the pasteboard data type. Since the internal representation of the
35         pasteboard data includes binary data that may be dependent on the software version,
36     the revision number is appended to these strings on startup (See MyApp::OnInit())  */
37 char *gMoleculePasteboardType = "Molecule";
38 char *gParameterPasteboardType = "Parameter";
39
40 #pragma mark ====== Utility function ======
41
42 int
43 strlen_limit(const char *s, int limit)
44 {
45         int len;
46         for (len = 0; *s != 0 && (limit < 0 || len < limit); s++, len++);
47         return len;
48 }
49
50 #pragma mark ======  Atom handling  ======
51
52 static Atom *
53 s_AtomDuplicate(Atom *dst, const Atom *src, Int copy_frame)
54 {
55         if (dst == NULL) {
56                 dst = (Atom *)malloc(gSizeOfAtomRecord);
57                 if (dst == NULL)
58                         return NULL;
59         }
60         memmove(dst, src, gSizeOfAtomRecord);
61         if (src->aniso != NULL) {
62                 dst->aniso = (Aniso *)malloc(sizeof(Aniso));
63                 if (dst->aniso != NULL)
64                         memmove(dst->aniso, src->aniso, sizeof(Aniso));
65         }
66         if (src->frames != NULL && copy_frame) {
67                 dst->frames = (Vector *)malloc(sizeof(Vector) * src->nframes);
68                 if (dst->frames != NULL) {
69                         memmove(dst->frames, src->frames, sizeof(Vector) * src->nframes);
70                         dst->nframes = src->nframes;
71                 } else {
72                         dst->nframes = 0;
73                 }
74         }
75         if (src->connect.count > ATOM_CONNECT_LIMIT) {
76                 dst->connect.u.ptr = NULL;
77                 dst->connect.count = 0;
78                 NewArray(&(dst->connect.u.ptr), &(dst->connect.count), sizeof(Int), src->connect.count);
79                 memmove(dst->connect.u.ptr, src->connect.u.ptr, sizeof(Int) * src->connect.count);
80         }
81         if (src->anchor != NULL) {
82                 dst->anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
83                 if (dst->anchor != NULL)
84                         memmove(dst->anchor, src->anchor, sizeof(PiAnchor));
85                 if (dst->anchor->connect.count > ATOM_CONNECT_LIMIT) {
86                         dst->anchor->connect.u.ptr = NULL;
87                         dst->anchor->connect.count = 0;
88                         NewArray(&(dst->anchor->connect.u.ptr), &(dst->anchor->connect.count), sizeof(Int), src->anchor->connect.count);
89                         memmove(dst->anchor->connect.u.ptr, src->anchor->connect.u.ptr, sizeof(Int) * src->anchor->connect.count);
90                 }
91                 if (dst->anchor->ncoeffs > 0) {
92                         NewArray(&(dst->anchor->coeffs), &(dst->anchor->ncoeffs), sizeof(Double), src->anchor->ncoeffs);
93                         memmove(dst->anchor->coeffs, src->anchor->coeffs, sizeof(Double) * src->anchor->ncoeffs);
94                 }
95         }
96         return dst;
97 }
98
99 Atom *
100 AtomDuplicate(Atom *dst, const Atom *src)
101 {
102         return s_AtomDuplicate(dst, src, 1);
103 }
104
105 Atom *
106 AtomDuplicateNoFrame(Atom *dst, const Atom *src)
107 {
108         return s_AtomDuplicate(dst, src, 0);
109 }
110
111 void
112 AtomClean(Atom *ap)
113 {
114         if (ap->aniso != NULL) {
115                 free(ap->aniso);
116                 ap->aniso = NULL;
117         }
118         if (ap->frames != NULL) {
119                 free(ap->frames);
120                 ap->frames = NULL;
121                 ap->nframes = 0;
122         }
123         if (ap->connect.count > ATOM_CONNECT_LIMIT) {
124                 ap->connect.count = 0;
125                 free(ap->connect.u.ptr);
126                 ap->connect.u.ptr = NULL;
127         }
128 }
129
130 void
131 CubeRelease(Cube *cp)
132 {
133         if (cp != NULL) {
134                 if (cp->dp != NULL)
135                         free(cp->dp);
136                 free(cp);
137         }
138 }
139
140 void
141 BasisSetRelease(BasisSet *bset)
142 {
143         int i;
144         if (bset == NULL)
145                 return;
146         if (bset->shells != NULL)
147                 free(bset->shells);
148         if (bset->priminfos != NULL)
149                 free(bset->priminfos);
150         if (bset->mo != NULL)
151                 free(bset->mo);
152         if (bset->cns != NULL)
153                 free(bset->cns);
154         if (bset->moenergies != NULL)
155                 free(bset->moenergies);
156         if (bset->scfdensities != NULL)
157                 free(bset->scfdensities);
158 /*      if (bset->pos != NULL)
159                 free(bset->pos); */
160         if (bset->nuccharges != NULL)
161                 free(bset->nuccharges);
162         if (bset->cubes != NULL) {
163                 for (i = 0; i < bset->ncubes; i++) {
164                         CubeRelease(bset->cubes[i]);
165                 }
166                 free(bset->cubes);
167         }
168         free(bset);
169 }
170
171 Int *
172 AtomConnectData(AtomConnect *ac)
173 {
174         if (ac == NULL)
175                 return NULL;
176         return ATOM_CONNECT_PTR(ac);
177 }
178
179 void
180 AtomConnectResize(AtomConnect *ac, Int nconnects)
181 {
182         Int *p;
183         if (ac == NULL)
184                 return;
185         if (nconnects <= ATOM_CONNECT_LIMIT) {
186                 if (ac->count > ATOM_CONNECT_LIMIT) {
187                         p = ac->u.ptr;
188                         memmove(ac->u.data, p, sizeof(Int) * nconnects);
189                         free(p);
190                 }
191         } else {
192                 if (ac->count <= ATOM_CONNECT_LIMIT) {
193                         p = NULL;
194                         ac->count = 0;
195                         NewArray(&p, &(ac->count), sizeof(Int), nconnects);
196                         memmove(p, ac->u.data, sizeof(Int) * ac->count);
197                         ac->u.ptr = p;
198                 } else if (ac->count < nconnects) {
199                         /*  Reallocate  */
200                         AssignArray(&(ac->u.ptr), &(ac->count), sizeof(Int), nconnects - 1, NULL);
201                 }
202         }
203         ac->count = nconnects;
204 }
205
206 void
207 AtomConnectInsertEntry(AtomConnect *ac, Int idx, Int connect)
208 {
209         Int n, *p;
210         if (ac == NULL)
211                 return;
212         if (idx > ac->count)
213                 idx = ac->count;
214         else if (idx < 0) {
215                 /*  Insert after the last component that is smaller than connect
216                     (i.e. keep them sorted)  */
217                 p = ATOM_CONNECT_PTR(ac);
218                 for (idx = 0; idx < ac->count; idx++) {
219                         if (p[idx] >= connect)
220                                 break;
221                 }
222         }
223         AtomConnectResize(ac, ac->count + 1);
224         n = ac->count - idx - 1;  /*  Number of entries to be moved towards the end  */
225         p = ATOM_CONNECT_PTR(ac);
226         if (n > 0) {
227                 memmove(p + idx + 1, p + idx, sizeof(Int) * n);
228         }
229         p[idx] = connect;
230 }
231
232 void
233 AtomConnectDeleteEntry(AtomConnect *ac, Int idx)
234 {
235         Int n, *p;
236         if (ac == NULL)
237                 return;
238         if (idx < 0 || idx >= ac->count)
239                 return;
240         n = ac->count - idx - 1;  /*  Number of entries to be moved towards the top  */
241         p = ATOM_CONNECT_PTR(ac);
242         if (n > 0) {
243                 memmove(p + idx, p + idx + 1, sizeof(Int) * n);
244         }
245         AtomConnectResize(ac, ac->count - 1);
246 }
247
248 int
249 AtomConnectHasEntry(AtomConnect *ac, Int ent)
250 {
251         Int n, *p;
252         if (ac == NULL)
253                 return 0;
254         p = ATOM_CONNECT_PTR(ac);
255         for (n = 0; n < ac->count; n++) {
256                 if (ent == p[n])
257                         return 1;
258         }
259         return 0;
260 }
261
262 #pragma mark ====== Accessor types ======
263
264 MolEnumerable *
265 MolEnumerableNew(Molecule *mol, int kind)
266 {
267         MolEnumerable *mseq = (MolEnumerable *)calloc(sizeof(MolEnumerable), 1);
268         if (mseq != NULL) {
269                 mseq->mol = MoleculeRetain(mol);
270                 mseq->kind = kind;
271         }
272         return mseq;
273 }
274
275 void
276 MolEnumerableRelease(MolEnumerable *mseq)
277 {
278         if (mseq != NULL) {
279                 MoleculeRelease(mseq->mol);
280                 free(mseq);
281         }
282 }
283
284 AtomRef *
285 AtomRefNew(Molecule *mol, int idx)
286 {
287         AtomRef *aref = (AtomRef *)calloc(sizeof(AtomRef), 1);
288         if (aref != NULL) {
289                 aref->mol = MoleculeRetain(mol);
290                 aref->idx = idx;
291         }
292         return aref;
293 }
294
295 void
296 AtomRefRelease(AtomRef *aref)
297 {
298         if (aref != NULL) {
299                 MoleculeRelease(aref->mol);
300                 free(aref);
301         }
302 }
303
304 #pragma mark ====== Creation of molecules ======
305
306 Molecule *
307 MoleculeNew(void)
308 {
309         char name[40];
310         Molecule *mp = (Molecule *)calloc(sizeof(Molecule), 1);
311         if (mp == NULL)
312                 Panic("Cannot allocate new molecule record");
313         snprintf(name, sizeof name, "Untitled %d", sMoleculeUntitledCount++);
314         ObjectInit((Object *)mp, (Object **)&sMoleculeRoot, name);
315         mp->mview = MainView_new();
316         mp->mview->mol = mp;
317         return mp;
318 }
319
320 Molecule *
321 MoleculeNewWithName(const char *name)
322 {
323         Molecule *mp = MoleculeNew();
324         MoleculeSetName(mp, name);
325         return mp;
326 }
327
328 Molecule *
329 MoleculeInitWithAtoms(Molecule *mp, const Atom *atoms, int natoms)
330 {
331         int i;
332         if (mp == NULL)
333                 mp = MoleculeNew();
334         if (natoms == 0)
335                 return mp;
336         if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
337                 Panic("Cannot allocate memory for atoms");
338         for (i = 0; i < natoms; i++)
339                 AtomDuplicate(mp->atoms + i, atoms + i);
340         mp->nframes = -1;  /*  Should be recalculated later  */
341         return mp;
342 }
343
344 Molecule *
345 MoleculeInitWithMolecule(Molecule *mp2, Molecule *mp)
346 {
347         MoleculeFlushFrames(mp);
348         MoleculeInitWithAtoms(mp2, mp->atoms, mp->natoms);
349         if (mp->nbonds > 0) {
350                 if (NewArray(&mp2->bonds, &mp2->nbonds, sizeof(Int)*2, mp->nbonds) == NULL)
351                         goto error;
352                 memmove(mp2->bonds, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
353         }
354         if (mp->nangles > 0) {
355                 if (NewArray(&mp2->angles, &mp2->nangles, sizeof(Int)*3, mp->nangles) == NULL)
356                         goto error;
357                 memmove(mp2->angles, mp->angles, sizeof(Int) * 3 * mp->nangles);
358         }
359         if (mp->ndihedrals > 0) {
360                 if (NewArray(&mp2->dihedrals, &mp2->ndihedrals, sizeof(Int)*4, mp->ndihedrals) == NULL)
361                         goto error;
362                 memmove(mp2->dihedrals, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
363         }
364         if (mp->nimpropers > 0) {
365                 if (NewArray(&mp2->impropers, &mp2->nimpropers, sizeof(Int)*4, mp->nimpropers) == NULL)
366                         goto error;
367                 memmove(mp2->impropers, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
368         }
369         if (mp->nresidues > 0) {
370                 if (NewArray(&mp2->residues, &mp2->nresidues, sizeof(mp->residues[0]), mp->nresidues) == NULL)
371                         goto error;
372                 memmove(mp2->residues, mp->residues, sizeof(mp->residues[0]) * mp->nresidues);
373         }
374         if (mp->cell != NULL) {
375                 mp2->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
376                 memmove(mp2->cell, mp->cell, sizeof(XtalCell));
377         }
378         if (mp->nsyms > 0) {
379                 NewArray(&(mp2->syms), &(mp2->nsyms), sizeof(Transform), mp->nsyms);
380                 memmove(mp2->syms, mp->syms, sizeof(Transform) * mp2->nsyms);
381         }
382
383         /*      mp2->useFlexibleCell = mp->useFlexibleCell; */
384         if (mp->nframe_cells > 0) {
385                 if (NewArray(&mp2->frame_cells, &mp2->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells) == NULL)
386                         goto error;
387                 memmove(mp2->frame_cells, mp->frame_cells, sizeof(Vector) * 4 * mp->nframe_cells);
388         }
389         
390         /* FIXME: should bset (basis set info) and elpot be duplicated or not?  */
391
392         if (mp->par != NULL)
393                 mp2->par = ParameterDuplicate(mp->par);
394         if (mp->arena != NULL) {
395                 md_arena_new(mp2);
396                 md_arena_init_from_arena(mp2->arena, mp->arena);
397         }
398         
399         return mp2;
400   error:
401         Panic("Cannot allocate memory for duplicate molecule");
402         return NULL;  /*  Not reached  */
403 }
404
405 /*  Assign a unique name to this parameter record  */
406 void
407 MoleculeSetName(Molecule *mp, const char *name)
408 {
409         ObjectSetName((Object *)mp, name, (Object *)sMoleculeRoot);
410 }
411
412 const char *
413 MoleculeGetName(Molecule *mp)
414 {
415         return ObjectGetName((Object *)mp);
416 }
417
418 Molecule *
419 MoleculeWithName(const char *name)
420 {
421         return (Molecule *)ObjectWithName(name, (Object *)sMoleculeRoot);
422 }
423
424 void
425 MoleculeSetPath(Molecule *mol, const char *fname)
426 {
427         char *buf, *cwd;
428         if (mol == NULL || fname == NULL)
429                 return;
430         if (fname[0] == '/' || (isalpha(fname[0]) && fname[1] == ':')) {
431                 /*  Full path  */
432                 buf = strdup(fname);
433         } else {
434                 cwd = getcwd(NULL, 0);
435                 asprintf(&buf, "%s/%s", cwd, fname);
436                 free(cwd);
437         }
438         if (mol->path != NULL) {
439                 if (strcmp(mol->path, buf) == 0) {
440                         /*  No change  */
441                         free(buf);
442                         return;
443                 }
444                 free((void *)(mol->path));
445         }
446         mol->path = buf;
447         if (mol->arena != NULL) {
448                 md_close_output_files(mol->arena);
449         }
450 }
451
452 const char *
453 MoleculeGetPath(Molecule *mol)
454 {
455         if (mol == NULL)
456                 return NULL;
457         return mol->path;
458 }
459
460 Molecule *
461 MoleculeRetain(Molecule *mp)
462 {
463         ObjectIncrRefCount((Object *)mp);
464         MoleculeRetainExternalObj(mp);
465         return mp;
466 }
467
468 void
469 MoleculeClear(Molecule *mp)
470 {
471         if (mp == NULL)
472                 return;
473         if (mp->arena != NULL) {
474                 md_arena_set_molecule(mp->arena, NULL);
475                 mp->arena = NULL;
476         }
477         if (mp->par != NULL) {
478                 ParameterRelease(mp->par);
479                 mp->par = NULL;
480         }
481         if (mp->bset != NULL) {
482                 BasisSetRelease(mp->bset);
483                 mp->bset = NULL;
484         }
485         if (mp->atoms != NULL) {
486                 int i;
487                 for (i = 0; i < mp->natoms; i++)
488                         AtomClean(mp->atoms + i);
489                 free(mp->atoms);
490                 mp->atoms = NULL;
491                 mp->natoms = 0;
492         }
493         if (mp->bonds != NULL) {
494                 free(mp->bonds);
495                 mp->bonds = NULL;
496                 mp->nbonds = 0;
497         }
498         if (mp->angles != NULL) {
499                 free(mp->angles);
500                 mp->angles = NULL;
501                 mp->nangles = 0;
502         }
503         if (mp->dihedrals != NULL) {
504                 free(mp->dihedrals);
505                 mp->dihedrals = NULL;
506                 mp->ndihedrals = 0;
507         }
508         if (mp->impropers != NULL) {
509                 free(mp->impropers);
510                 mp->impropers = NULL;
511                 mp->nimpropers = 0;
512         }
513         if (mp->residues != NULL) {
514                 free(mp->residues);
515                 mp->residues = NULL;
516                 mp->nresidues = 0;
517         }
518         if (mp->cell != NULL) {
519                 free(mp->cell);
520                 mp->cell = NULL;
521         }
522         if (mp->syms != NULL) {
523                 free(mp->syms);
524                 mp->syms = NULL;
525                 mp->nsyms = 0;
526         }
527         if (mp->selection != NULL) {
528                 IntGroupRelease(mp->selection);
529                 mp->selection = NULL;
530         }
531         if (mp->frame_cells != NULL) {
532                 free(mp->frame_cells);
533                 mp->frame_cells = NULL;
534                 mp->nframe_cells = 0;
535         }
536         if (mp->bset != NULL) {
537                 BasisSetRelease(mp->bset);
538                 mp->bset = NULL;
539         }
540         if (mp->mcube != NULL) {
541                 free(mp->mcube->dp);
542                 free(mp->mcube->radii);
543                 free(mp->mcube->c[0].cubepoints);
544                 free(mp->mcube->c[0].triangles);
545                 free(mp->mcube->c[1].cubepoints);
546                 free(mp->mcube->c[1].triangles);
547                 free(mp->mcube);
548                 mp->mcube = NULL;
549         }
550         if (mp->par != NULL) {
551                 ParameterRelease(mp->par);
552                 mp->par = NULL;
553         }
554         if (mp->elpots != NULL) {
555                 free(mp->elpots);
556                 mp->elpots = NULL;
557                 mp->nelpots = 0;
558         }
559         if (mp->path != NULL) {
560                 free((void *)mp->path);
561                 mp->path = NULL;
562         }
563 }
564
565 void
566 MoleculeRelease(Molecule *mp)
567 {
568         if (mp == NULL)
569                 return;
570         MoleculeReleaseExternalObj(mp);
571         if (ObjectDecrRefCount((Object *)mp) == 0) {
572                 MoleculeClear(mp);
573                 mp->mview->mol = NULL;
574                 MainView_release(mp->mview);
575                 ObjectDealloc((Object *)mp, (Object **)&sMoleculeRoot);
576         }
577 }
578
579 void
580 MoleculeExchange(Molecule *mp1, Molecule *mp2)
581 {
582         Molecule mp_temp;
583         struct MainView *mview1, *mview2;
584         struct MDArena *arena1, *arena2;
585         /*  mview and arena must be kept as they are  */
586         mview1 = mp1->mview;
587         mview2 = mp2->mview;
588         arena1 = mp1->arena;
589         arena2 = mp2->arena;
590         /*  'natoms' is the first member to be copied  */
591         int ofs = offsetof(Molecule, natoms);
592         memmove((char *)(&mp_temp) + ofs, (char *)mp1 + ofs, sizeof(Molecule) - ofs);
593         memmove((char *)mp1 + ofs, (char *)mp2 + ofs, sizeof(Molecule) - ofs);
594         memmove((char *)mp2 + ofs, (char *)(&mp_temp) + ofs, sizeof(Molecule) - ofs);
595         mp1->arena = arena1;
596         mp2->arena = arena2;
597         mp1->mview = mview1;
598         mp2->mview = mview2;
599 /*      if (mp1->arena != NULL && mp1->arena->mol == mp2)
600                 mp1->arena->mol = mp1;
601         if (mp1->arena != NULL && mp1->arena->xmol == mp2)
602                 mp1->arena->xmol = mp1;
603         if (mp2->arena != NULL && mp2->arena->mol == mp1)
604                 mp2->arena->mol = mp2;
605         if (mp2->arena != NULL && mp2->arena->xmol == mp1)
606                 mp2->arena->xmol = mp2; */
607 }
608
609 #pragma mark ====== Mutex ======
610
611 void
612 MoleculeLock(Molecule *mol)
613 {
614         if (mol == NULL || mol->mutex == NULL)
615                 return;
616         MoleculeCallback_lockMutex(mol->mutex);
617 }
618
619 void
620 MoleculeUnlock(Molecule *mol)
621 {
622         if (mol == NULL || mol->mutex == NULL)
623                 return;
624         MoleculeCallback_unlockMutex(mol->mutex);
625 }
626
627 #pragma mark ====== Modify count ======
628
629 void
630 MoleculeIncrementModifyCount(Molecule *mp)
631 {
632         if (mp != NULL) {
633                 if (++(mp->modifyCount) == 1)
634                         MoleculeCallback_notifyModification(mp, 0);
635         /*      fprintf(stderr, "MoleculeIncrementModifyCount: %d\n", mp->modifyCount); */
636         }
637 }
638
639 void
640 MoleculeClearModifyCount(Molecule *mp)
641 {
642         if (mp != NULL) {
643                 mp->modifyCount = 0;
644         /*      fprintf(stderr, "MoleculeClearModifyCount: %d\n", mp->modifyCount); */
645         }
646 }
647
648 #pragma mark ====== File handling functions ======
649
650 static const char *
651 guessMoleculeType(const char *fname)
652 {
653         char buf[1024], *p;
654         FILE *fp;
655         const char *retval = NULL;
656         fp = fopen(fname, "rb");
657         if (fp != NULL) {
658                 memset(buf, 0, sizeof buf);
659                 if (fread(buf, 1, sizeof buf - 1, fp) > 0) {
660                         if (strncmp(buf, "PSF", 3) == 0)
661                                 retval = "psf";
662                         else if (((p = strstr(buf, "ATOM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r'))
663                         || ((p = strstr(buf, "HETATM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r')))
664                                 retval = "pdb";
665                         else
666                                 retval = "???";  /*  unknown  */
667                 }
668                 fclose(fp);
669         }
670         return retval;
671 }
672
673 static int
674 guessElement(Atom *ap)
675 {
676         int atomicNumber = -1;
677         if (ap->atomicNumber > 0)
678                 atomicNumber = ap->atomicNumber;
679         else {
680                 atomicNumber = GuessAtomicNumber(ap->element, ap->weight);
681                 if (atomicNumber <= 0 && ap->aname[0] != 0)
682                         atomicNumber = GuessAtomicNumber(ap->aname, ap->weight);
683         }
684         if (atomicNumber >= 0) {
685                 ap->atomicNumber = atomicNumber;
686                 if (ap->weight <= 0)
687                         ap->weight = WeightForAtomicNumber(atomicNumber);
688                 if (ap->element[0] == 0)
689                         ElementToString(atomicNumber, ap->element);
690         }
691         return atomicNumber;
692 }
693
694 static int
695 sReadLineWithInterrupt(char *buf, int size, FILE *stream, int *lineNumber)
696 {
697         static int lastLineNumber = 0;
698         if (lineNumber != NULL) {
699                 if (*lineNumber == 0)
700                         lastLineNumber = 0;
701                 else if (*lineNumber >= lastLineNumber + 1000) {
702                         if (MyAppCallback_checkInterrupt() != 0)
703                                 return -1;  /*  User interrupt  */
704                         lastLineNumber = *lineNumber;
705                 }
706         }
707         return ReadLine(buf, size, stream, lineNumber);
708 }
709
710 static int
711 s_append_asprintf(char **buf, const char *fmt, ...)
712 {
713         int len;
714         char *s;
715         va_list va;
716         va_start(va, fmt);
717         vasprintf(&s, fmt, va);
718         len = (*buf == NULL ? 0 : strlen(*buf));
719         if (s == NULL)
720                 return len;
721         len += strlen(s);
722         if (*buf == NULL) {
723                 *buf = malloc(len + 1);
724                 **buf = 0;
725         } else {
726                 *buf = realloc(*buf, len + 1);
727         }
728         strcat(*buf, s);
729         free(s);
730         return len;
731 }
732
733 int
734 MoleculeLoadFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
735 {
736         int retval;
737         if (ftype == NULL || *ftype == 0) {
738                 const char *cp;
739                 cp = strrchr(fname, '.');
740                 if (cp != NULL)
741                         ftype = cp + 1;
742                 else {
743                         cp = guessMoleculeType(fname);
744                         if (strcmp(cp, "???") != 0)
745                                 ftype = cp;
746                 }
747         }
748         if (strcasecmp(ftype, "psf") == 0) {
749                 retval = MoleculeLoadPsfFile(mp, fname, errbuf);
750         } else if (strcasecmp(ftype, "pdb") == 0) {
751                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
752         } else if (strcasecmp(ftype, "tep") == 0) {
753                 retval = MoleculeLoadTepFile(mp, fname, errbuf);
754         } else if (strcasecmp(ftype, "res") == 0 || strcasecmp(ftype, "ins") == 0) {
755                 retval = MoleculeLoadShelxFile(mp, fname, errbuf);
756         } else if (strcasecmp(ftype, "fchk") == 0 || strcasecmp(ftype, "fch") == 0) {
757                 retval = MoleculeLoadGaussianFchkFile(mp, fname, errbuf);
758         } else {
759                 s_append_asprintf(errbuf, "Unknown format %s", ftype);
760                 return 1;
761         }
762 /*      if (retval != 0) {
763                 retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
764         } */
765         if (retval == 0)
766                 MoleculeSetPath(mp, fname);
767         return retval;
768 }
769
770 int
771 MoleculeLoadMbsfFile(Molecule *mp, const char *fname, char **errbuf)
772 {
773         FILE *fp;
774         char buf[1024];
775         int i, j, k, err, fn, nframes, nwarnings;
776         int lineNumber;
777         int ibuf[12];
778         Int iibuf[4];
779         double dbuf[12];
780         int mview_ibuf[18];
781         double mview_dbuf[10];
782         char cbuf[12][8];
783         const char **pp;
784         char *bufp, *valp, *comp;
785         Int *ip;
786         Double *dp;
787         Vector v;
788         Atom *ap;
789         const int kUndefined = -10000000;
790         err = 0;
791         *errbuf = NULL;
792         nwarnings = 0;
793         if (mp->natoms != 0 || mp->par != NULL || mp->arena != NULL) {
794                 s_append_asprintf(errbuf, "The molecule must be empty");
795                 return 1;
796         }
797         fp = fopen(fname, "rb");
798         if (fp == NULL) {
799                 s_append_asprintf(errbuf, "Cannot open file");
800                 return 1;
801         }
802         for (i = 0; i < 10; i++)
803                 mview_dbuf[i] = kUndefined;
804         for (i = 0; i < 18; i++)
805                 mview_ibuf[i] = kUndefined;
806         /*      flockfile(fp); */
807         lineNumber = 0;
808         fn = 0;
809         nframes = 0;
810         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
811                 if (strncmp(buf, "!:", 2) != 0)
812                         continue;   /*  Skip until section header is found  */
813                 bufp = buf;
814                 strsep(&bufp, " \t\n");
815                 if (strcmp(buf, "!:atoms") == 0) {
816                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
817                                 if (buf[0] == '!')
818                                         continue;
819                                 if (buf[0] == '\n')
820                                         break;
821                                 /* idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge */
822                                 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) {
823                                         s_append_asprintf(errbuf, "line %d: coordinates cannot be read for atom %d", lineNumber, mp->natoms + 1);
824                                         goto err_exit;
825                                 }
826                                 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
827                                 strncpy(ap->segName, cbuf[0], 4);
828                                 ap->resSeq = ibuf[1];
829                                 strncpy(ap->resName, cbuf[1], 4);
830                                 strncpy(ap->aname, cbuf[2], 4);
831                                 ap->type = AtomTypeEncodeToUInt(cbuf[3]);
832                                 ap->charge = dbuf[0];
833                                 ap->weight = dbuf[1];
834                                 strncpy(ap->element, cbuf[4], 2);
835                                 ap->atomicNumber = ibuf[2];
836                                 ap->occupancy = dbuf[2];
837                                 ap->tempFactor = dbuf[3];
838                                 ap->intCharge = ibuf[3];
839                         }
840                         continue;
841                 } else if (strcmp(buf, "!:atoms_symop") == 0) {
842                         i = 0;
843                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
844                                 if (buf[0] == '!')
845                                         continue;
846                                 if (buf[0] == '\n')
847                                         break;
848                                 /* idx symop symbase */
849                                 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
850                                         s_append_asprintf(errbuf, "line %d: symmetry operations cannot be read for atom %d", lineNumber, i + 1);
851                                         goto err_exit;
852                                 }
853                                 if (i >= mp->natoms) {
854                                         s_append_asprintf(errbuf, "line %d: too many atomic symmetry info\n", lineNumber);
855                                         goto err_exit;
856                                 }
857                                 ap = ATOM_AT_INDEX(mp->atoms, i);
858                                 ap->symop.sym = ibuf[1] / 1000000;
859                                 ap->symop.dx = (ibuf[1] % 1000000) / 10000;
860                                 ap->symop.dy = (ibuf[1] % 10000) / 100;
861                                 ap->symop.dz = ibuf[1] % 100;
862                                 ap->symbase = ibuf[2];
863                                 i++;
864                         }
865                         continue;
866                 } else if (strcmp(buf, "!:atoms_fix") == 0) {
867                         i = 0;
868                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
869                                 if (buf[0] == '!')
870                                         continue;
871                                 if (buf[0] == '\n')
872                                         break;
873                                 /* idx fix_force fix_pos */
874                                 if (sscanf(buf, "%d %lf %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]) < 5) {
875                                         s_append_asprintf(errbuf, "line %d: fix atom info cannot be read for atom %d", lineNumber, i + 1);
876                                         goto err_exit;
877                                 }
878                                 if (i >= mp->natoms) {
879                                         s_append_asprintf(errbuf, "line %d: too many fix atom info\n", lineNumber);
880                                         goto err_exit;
881                                 }
882                                 ap = ATOM_AT_INDEX(mp->atoms, i);
883                                 ap->fix_force = dbuf[0];
884                                 ap->fix_pos.x = dbuf[1];
885                                 ap->fix_pos.y = dbuf[2];
886                                 ap->fix_pos.z = dbuf[3];
887                                 i++;
888                         }
889                         continue;
890                 } else if (strcmp(buf, "!:uff_types") == 0) {
891                         i = 0;
892                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
893                                 if (buf[0] == '!')
894                                         continue;
895                                 if (buf[0] == '\n')
896                                         break;
897                                 /* idx uff_type */
898                                 if (sscanf(buf, "%d %6s", &ibuf[0], cbuf[0]) < 2) {
899                                         s_append_asprintf(errbuf, "line %d: uff type info cannot be read for atom %d", lineNumber, i + 1);
900                                         goto err_exit;
901                                 }
902                                 if (i >= mp->natoms) {
903                                         s_append_asprintf(errbuf, "line %d: too many uff type info\n", lineNumber);
904                                         goto err_exit;
905                                 }
906                                 ap = ATOM_AT_INDEX(mp->atoms, i);
907                                 strncpy(ap->uff_type, cbuf[0], 5);
908                                 ap->uff_type[5] = 0;
909                                 i++;
910                         }
911                 } else if (strcmp(buf, "!:mm_exclude") == 0) {
912                         i = 0;
913                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
914                                 if (buf[0] == '!')
915                                         continue;
916                                 if (buf[0] == '\n')
917                                         break;
918                                 /* idx mm_exclude periodic_exclude */
919                                 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
920                                         s_append_asprintf(errbuf, "line %d: mm_exclude flags cannot be read for atom %d", lineNumber, i + 1);
921                                         goto err_exit;
922                                 }
923                                 if (i >= mp->natoms) {
924                                         s_append_asprintf(errbuf, "line %d: too many mm_exclude flags\n", lineNumber);
925                                         goto err_exit;
926                                 }
927                                 ap = ATOM_AT_INDEX(mp->atoms, i);
928                                 ap->mm_exclude = (ibuf[1] != 0);
929                                 ap->periodic_exclude = (ibuf[2] != 0);
930                                 i++;
931                         }
932                         continue;
933                 } else if (strcmp(buf, "!:pi_anchor") == 0) {
934                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
935                                 if (buf[0] == '!')
936                                         continue;
937                                 if (buf[0] == '\n')
938                                         break;
939                                 /* idx count */
940                                 if ((j = sscanf(buf, "%d %d", &ibuf[0], &ibuf[1])) < 2) {
941                                         s_append_asprintf(errbuf, "line %d: bad format for pi_anchor", lineNumber);
942                                         goto err_exit;
943                                 }
944                                 i = ibuf[0];
945                                 ap = ATOM_AT_INDEX(mp->atoms, i);
946                                 if (ap->anchor != NULL) {
947                                         s_append_asprintf(errbuf, "line %d: warning: duplicate pi_anchor entry", lineNumber);
948                                         AtomConnectResize(&ap->anchor->connect, 0);
949                                         free(ap->anchor->coeffs);
950                                         free(ap->anchor);
951                                 }
952                                 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
953                                 if (ibuf[1] < 2 || ibuf[1] >= mp->natoms) {
954                                         s_append_asprintf(errbuf, "line %d: bad number of components for pi_anchor", lineNumber);
955                                         goto err_exit;
956                                 }
957                                 AtomConnectResize(&ap->anchor->connect, ibuf[1]);
958                                 ip = AtomConnectData(&ap->anchor->connect);
959                                 NewArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), ibuf[1]);
960                                 j = ibuf[1];
961                                 for (i = 0; i < j; i++) {
962                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
963                                                 s_append_asprintf(errbuf, "line %d: unexpected end of file while reading pi_anchors", lineNumber);
964                                                 goto err_exit;
965                                         }
966                                         if (sscanf(buf, "%d %lf", &ibuf[0], &dbuf[0]) < 2) {
967                                                 s_append_asprintf(errbuf, "line %d: bad format for pi_anchor", lineNumber);
968                                                 goto err_exit;
969                                         }
970                                         if (ibuf[0] < 0 || ibuf[0] >= mp->natoms) {
971                                                 s_append_asprintf(errbuf, "line %d: atom index out of range", lineNumber);
972                                                 goto err_exit;
973                                         }
974                                         if (dbuf[0] <= 0.0) {
975                                                 s_append_asprintf(errbuf, "line %d: the pi anchor weights should be positive", lineNumber);
976                                                 goto err_exit;
977                                         }
978                                         ip[i] = ibuf[0];
979                                         ap->anchor->coeffs[i] = dbuf[0];
980                                 }
981                         }
982                         continue;
983                 } else if (strcmp(buf, "!:positions") == 0) {
984                         i = 0;
985                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
986                                 if (buf[0] == '!')
987                                         continue;
988                                 if (buf[0] == '\n')
989                                         break;
990                                 /* idx x y z */
991                                 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) {
992                                         s_append_asprintf(errbuf, "line %d: atom position cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
993                                         goto err_exit;
994                                 }
995                                 if (j > 4 && nframes != 0) {
996                                         s_append_asprintf(errbuf, "line %d: atom position sigma can only be given for frame 0", lineNumber);
997                                         goto err_exit;
998                                 }
999                                 if (j > 4 && j != 7) {
1000                                         s_append_asprintf(errbuf, "line %d: atom position sigma cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
1001                                         goto err_exit;
1002                                 }
1003                                 if (i >= mp->natoms) {
1004                                         s_append_asprintf(errbuf, "line %d: too many atom position records\n", lineNumber);
1005                                         goto err_exit;
1006                                 }
1007                                 v.x = dbuf[0];
1008                                 v.y = dbuf[1];
1009                                 v.z = dbuf[2];
1010                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1011                                 if (nframes > 0) {
1012                                         AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes, &v);
1013                                         if (nframes == 1)
1014                                                 ap->frames[0] = ap->r;
1015                                 }
1016                                 ap->r = v;
1017                                 if (j == 7) {
1018                                         ap->sigma.x = dbuf[3];
1019                                         ap->sigma.y = dbuf[4];
1020                                         ap->sigma.z = dbuf[5];
1021                                 }
1022                                 i++;
1023                         }
1024                         nframes++;
1025                         if (nframes >= 2) {
1026                                 mp->nframes = nframes;
1027                                 mp->cframe = nframes - 1;
1028                         } else {
1029                                 mp->nframes = mp->cframe = 0;
1030                         }
1031                         continue;
1032                 } else if (strcmp(buf, "!:bonds") == 0) {
1033                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1034                                 if (buf[0] == '!')
1035                                         continue;
1036                                 if (buf[0] == '\n')
1037                                         break;
1038                                 /* from1 to1 from2 to2 from3 to3 from4 to4 */ 
1039                                 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]);
1040                                 if (i < 2 || i % 2 != 0) {
1041                                         s_append_asprintf(errbuf, "line %d: bad bond format", lineNumber);
1042                                         goto err_exit;
1043                                 }
1044                                 for (j = 0; j < i; j += 2) {
1045                                         iibuf[0] = ibuf[j];
1046                                         iibuf[1] = ibuf[j + 1];
1047                                         if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[0] == iibuf[1]) {
1048                                                 s_append_asprintf(errbuf, "line %d: warning: bad bond specification (%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1]);
1049                                                 nwarnings++;
1050                                         } else if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[0])->connect), iibuf[1])) {
1051                                                 s_append_asprintf(errbuf, "line %d: warning: bond %d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1]);
1052                                                 nwarnings++;
1053                                         } else {
1054                                                 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, iibuf);
1055                                                 AtomConnectInsertEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[0])->connect), -1, iibuf[1]);
1056                                                 AtomConnectInsertEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[1])->connect), -1, iibuf[0]);
1057                                         }
1058                                 }
1059                         }
1060                         continue;
1061                 } else if (strcmp(buf, "!:bond_orders") == 0) {
1062                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1063                                 if (buf[0] == '!')
1064                                         continue;
1065                                 if (buf[0] == '\n')
1066                                         break;
1067                                 /* b1 b2 b3 b4 */
1068                                 i = sscanf(buf, "%lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]);
1069                                 if (i == 0) {
1070                                         s_append_asprintf(errbuf, "line %d: bad bond order format", lineNumber);
1071                                         goto err_exit;
1072                                 }
1073                                 for (j = 0; j < i; j++) {
1074                                         AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbondOrders, &dbuf[j]);
1075                                 }
1076                         }
1077                         if (mp->nbondOrders > mp->nbonds) {
1078                                 s_append_asprintf(errbuf, "line %d: warning: the number of bond order info (%d) exceeds number of bonds (%d) - ignoring excess info\n", lineNumber, mp->nbondOrders, mp->nbonds);
1079                                 nwarnings++;
1080                                 mp->nbondOrders = mp->nbonds;
1081                         } else if (mp->nbondOrders < mp->nbonds) {
1082                                 s_append_asprintf(errbuf, "line %d: warning: the number of bond order info (%d) is less than number of bonds (%d)\n", lineNumber, mp->nbondOrders, mp->nbonds);
1083                                 nwarnings++;
1084                                 j = mp->nbondOrders;
1085                                 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbonds - 1, NULL);
1086                                 for (i = j; i < mp->nbonds; i++)
1087                                         mp->bondOrders[i] = 0.0;
1088                         }
1089                         continue;
1090                         
1091                 } else if (strcmp(buf, "!:angles") == 0) {
1092                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1093                                 if (buf[0] == '!')
1094                                         continue;
1095                                 if (buf[0] == '\n')
1096                                         break;
1097                                 /* a1 b1 c1 a2 b2 c2 a3 b3 c3 */ 
1098                                 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]);
1099                                 if (i == 0 || i % 3 != 0) {
1100                                         s_append_asprintf(errbuf, "line %d: bad angle format", lineNumber);
1101                                         goto err_exit;
1102                                 }
1103                                 for (j = 0; j < i; j += 3) {
1104                                         iibuf[0] = ibuf[j];
1105                                         iibuf[1] = ibuf[j + 1];
1106                                         iibuf[2] = ibuf[j + 2];
1107                                         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]) {
1108                                                 s_append_asprintf(errbuf, "line %d: warning: bad angle specification (%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1109                                                 nwarnings++;
1110                                         } else if (MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[2]) == 0) {
1111                                                 s_append_asprintf(errbuf, "line %d: warning: angle with non-bonded atoms (%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1112                                                 nwarnings++;                                            
1113                                         } else if (MoleculeLookupAngle(mp, iibuf[0], iibuf[1], iibuf[2]) >= 0) {
1114                                                 s_append_asprintf(errbuf, "line %d: warning: angle %d-%d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1115                                                 nwarnings++;
1116                                         } else {
1117                                                 AssignArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, mp->nangles, iibuf);
1118                                         }
1119                                 }
1120                         }
1121                         continue;
1122                 } else if (strcmp(buf, "!:dihedrals") == 0) {
1123                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1124                                 if (buf[0] == '!')
1125                                         continue;
1126                                 if (buf[0] == '\n')
1127                                         break;
1128                                 /* a1 b1 c1 d1 a2 b2 c2 d2 */ 
1129                                 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]);
1130                                 if (i == 0 || i % 4 != 0) {
1131                                         s_append_asprintf(errbuf, "line %d: bad dihedral format", lineNumber);
1132                                         goto err_exit;
1133                                 }
1134                                 for (j = 0; j < i; j += 4) {
1135                                         iibuf[0] = ibuf[j];
1136                                         iibuf[1] = ibuf[j + 1];
1137                                         iibuf[2] = ibuf[j + 2];
1138                                         iibuf[3] = ibuf[j + 3];
1139                                         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]) {
1140                                                 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]);
1141                                                 nwarnings++;
1142                                         } else if (MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[2]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[3]) == 0) {
1143                                                 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]);
1144                                                 nwarnings++;                                            
1145                                         } else if (MoleculeLookupDihedral(mp, iibuf[0], iibuf[1], iibuf[2], iibuf[3]) >= 0) {
1146                                                 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]);
1147                                                 nwarnings++;
1148                                         } else {
1149                                                 AssignArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, mp->ndihedrals, iibuf);
1150                                         }
1151                                 }
1152                         }
1153                         continue;
1154                 } else if (strcmp(buf, "!:impropers") == 0) {
1155                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1156                                 if (buf[0] == '!')
1157                                         continue;
1158                                 if (buf[0] == '\n')
1159                                         break;
1160                                 /* a1 b1 c1 d1 a2 b2 c2 d2 */ 
1161                                 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]);
1162                                 if (i == 0 || i % 4 != 0) {
1163                                         s_append_asprintf(errbuf, "line %d: bad improper format", lineNumber);
1164                                         goto err_exit;
1165                                 }
1166                                 for (j = 0; j < i; j += 4) {
1167                                         iibuf[0] = ibuf[j];
1168                                         iibuf[1] = ibuf[j + 1];
1169                                         iibuf[2] = ibuf[j + 2];
1170                                         iibuf[3] = ibuf[j + 3];
1171                                         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]) {
1172                                                 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]);
1173                                                 nwarnings++;
1174                                         } else if (MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[1]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[3]) == 0) {
1175                                                 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]);
1176                                                 nwarnings++;                                            
1177                                         } else if (MoleculeLookupImproper(mp, iibuf[0], iibuf[1], iibuf[2], iibuf[3]) >= 0) {
1178                                                 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]);
1179                                                 nwarnings++;
1180                                         } else {
1181                                                 AssignArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, mp->nimpropers, iibuf);
1182                                         }
1183                                 }
1184                         }
1185                         continue;
1186                 } else if (strcmp(buf, "!:xtalcell") == 0 && mp->cell == NULL) {
1187                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1188                                 if (buf[0] == '!')
1189                                         continue;
1190                                 if (buf[0] == '\n')
1191                                         break;
1192                                 /* a b c alpha beta gamma [sigmaflag] */ 
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: bad xtalcell format", lineNumber);
1195                                         goto err_exit;
1196                                 }
1197                                 MoleculeSetCell(mp, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], 0);
1198                                 if (j == 7 && ibuf[0] != 0) {
1199                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1200                                                 s_append_asprintf(errbuf, "line %d: sigma for xtalcell are missing", lineNumber);
1201                                                 goto err_exit;
1202                                         }
1203                                         if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1204                                                 s_append_asprintf(errbuf,"line %d: bad xtalcell sigma format", lineNumber);
1205                                                 goto err_exit;
1206                                         }
1207                                         if (mp->cell != NULL) {
1208                                                 mp->cell->has_sigma = 1;
1209                                                 for (i = 0; i < 6; i++) {
1210                                                         mp->cell->cellsigma[i] = dbuf[i];
1211                                                 }
1212                                         } else {
1213                                                 s_append_asprintf(errbuf, "line %d: cell sigma are given while cell is not given", lineNumber);
1214                                         }
1215                                 }
1216                         }
1217                         continue;
1218                 } else if (strcmp(buf, "!:symmetry_operations") == 0) {
1219                         i = 0;
1220                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1221                                 Transform tr;
1222                                 if (buf[0] == '!')
1223                                         continue;
1224                                 if (buf[0] == '\n')
1225                                         break;
1226                                 /* a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3 */
1227                                 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1228                                         s_append_asprintf(errbuf, "line %d: bad symmetry_operation format", lineNumber);
1229                                         goto err_exit;
1230                                 }
1231                                 if (i < 3) {
1232                                         tr[i] = dbuf[0];
1233                                         tr[i + 3] = dbuf[1];
1234                                         tr[i + 6] = dbuf[2];
1235                                 } else {
1236                                         tr[9] = dbuf[0];
1237                                         tr[10] = dbuf[1];
1238                                         tr[11] = dbuf[2];
1239                                 }
1240                                 i++;
1241                                 if (i == 4) {
1242                                         AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1243                                         i = 0;
1244                                 }
1245                         }
1246                         continue;
1247                 } else if (strcmp(buf, "!:anisotropic_thermal_parameters") == 0) {
1248                         i = 0;
1249                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1250                                 if (buf[0] == '!')
1251                                         continue;
1252                                 if (buf[0] == '\n')
1253                                         break;
1254                                 /* b11 b22 b33 b12 b13 b23 [has_sigma] */
1255                                 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) {
1256                                         s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters cannot be read for atom %d", lineNumber, i + 1);
1257                                         goto err_exit;
1258                                 }
1259                                 if (i >= mp->natoms) {
1260                                         s_append_asprintf(errbuf, "line %d: too many anisotropic thermal parameters\n", lineNumber);
1261                                         goto err_exit;
1262                                 }
1263                                 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) {
1264                                         /*  Skip it  */
1265                                 } else {
1266                                         MoleculeSetAniso(mp, i, 0, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], NULL);
1267                                 }
1268                                 if (j == 7 && ibuf[0] != 0) {
1269                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1270                                                 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma missing", lineNumber);
1271                                                 goto err_exit;
1272                                         }
1273                                         if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1274                                                 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma cannot be read for atom %d", lineNumber, i + 1);
1275                                                 goto err_exit;
1276                                         }
1277                                         ap = ATOM_AT_INDEX(mp->atoms, i);
1278                                         if (ap->aniso == NULL) {
1279                                                 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma are given while the parameters are not given", lineNumber);
1280                                                 goto err_exit;
1281                                         }
1282                                         ap->aniso->has_bsig = 1;
1283                                         for (j = 0; j < 6; j++)
1284                                                 ap->aniso->bsig[j] = dbuf[j];
1285                                 }
1286                                 i++;
1287                         }
1288                         continue;
1289                 } else if (strcmp(buf, "!:periodic_box") == 0) {
1290                         Vector vs[5];
1291                         Byte has_sigma = 0;
1292                         i = 0;
1293                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1294                                 if (buf[0] == '!')
1295                                         continue;
1296                                 if (buf[0] == '\n')
1297                                         break;
1298                                 /* 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] */
1299                                 if (i < 4) {
1300                                         if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1301                                                 s_append_asprintf(errbuf, "line %d: bad periodic_box format", lineNumber);
1302                                                 goto err_exit;
1303                                         }
1304                                         vs[i].x = dbuf[0];
1305                                         vs[i].y = dbuf[1];
1306                                         vs[i].z = dbuf[2];
1307                                         i++;
1308                                         continue;
1309                                 }
1310                                 if ((j = sscanf(buf, "%d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3])) < 3) {
1311                                         s_append_asprintf(errbuf, "line %d: bad periodic_box format", lineNumber);
1312                                         goto err_exit;
1313                                 }
1314                                 if (j == 4 && ibuf[3] != 0)
1315                                         has_sigma = 1;
1316                                 cbuf[0][0] = ibuf[0];
1317                                 cbuf[0][1] = ibuf[1];
1318                                 cbuf[0][2] = ibuf[2];
1319                                 MoleculeSetPeriodicBox(mp, vs, vs + 1, vs + 2, vs + 3, cbuf[0], 0);
1320                                 if (has_sigma) {
1321                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1322                                                 s_append_asprintf(errbuf, "line %d: sigma for cell parameters are missing", lineNumber);
1323                                                 goto err_exit;
1324                                         }
1325                                         if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1326                                                 s_append_asprintf(errbuf, "line %d: bad periodic_box sigma format", lineNumber);
1327                                                 goto err_exit;
1328                                         }
1329                                         if (mp->cell != NULL) {
1330                                                 mp->cell->has_sigma = 1;
1331                                                 for (i = 0; i < 6; i++) {
1332                                                         mp->cell->cellsigma[i] = dbuf[i];
1333                                                 }
1334                                         } else {
1335                                                 s_append_asprintf(errbuf, "line %d: cell sigma are given while cell is not given", lineNumber);
1336                                         }
1337                                 }
1338                                 break;
1339                         }
1340                         continue;
1341                 } else if (strcmp(buf, "!:frame_periodic_boxes") == 0) {
1342                         Vector vs[5];
1343                         i = 0;
1344                 /*      mp->useFlexibleCell = 1;  *//*  The presence of this block causes asserting this flag  */
1345                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1346                                 if (buf[0] == '!')
1347                                         continue;
1348                                 if (buf[0] == '\n')
1349                                         break;
1350                                 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1351                                         s_append_asprintf(errbuf, "line %d: bad frame_periodic_box format", lineNumber);
1352                                         goto err_exit;
1353                                 }
1354                                 vs[i].x = dbuf[0];
1355                                 vs[i].y = dbuf[1];
1356                                 vs[i].z = dbuf[2];
1357                                 i++;
1358                                 if (i == 4) {
1359                                         AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells, vs);
1360                                         i = 0;
1361                                 }
1362                         }
1363                         if (mp->cframe < mp->nframe_cells) {
1364                                 /*  mp->cframe should already have been set when positions are read  */
1365                                 Vector *vp = &mp->frame_cells[mp->cframe * 4];
1366                                 static char defaultFlags[] = {1, 1, 1};
1367                                 char *flags = (mp->cell != NULL ? mp->cell->flags : defaultFlags);
1368                                 MoleculeSetPeriodicBox(mp, vp, vp + 1, vp + 2, vp + 3, flags, 0);
1369                         }
1370                         continue;
1371                 } else if (strcmp(buf, "!:md_parameters") == 0) {
1372                         MDArena *arena;
1373                         if (mp->arena == NULL)
1374                                 mp->arena = md_arena_new(NULL);
1375                         arena = mp->arena;
1376                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1377                                 if (buf[0] == '!')
1378                                         continue;
1379                                 if (buf[0] == '\n')
1380                                         break;
1381                                 bufp = buf;
1382                                 comp = strsep(&bufp, " \t");
1383                                 if (bufp != NULL) {
1384                                         while (*bufp == ' ' || *bufp == '\t')
1385                                                 bufp++;
1386                                         valp = strsep(&bufp, "\n");
1387                                 } else valp = NULL;
1388                                 if (strcmp(comp, "alchem_flags") == 0) {
1389                                         j = (valp == NULL ? 0 : atoi(valp));
1390                                         if (j > 0) {
1391                                                 valp = (char *)malloc(j);
1392                                                 i = 0;
1393                                                 while ((k = fgetc(fp)) >= 0) {
1394                                                         ungetc(k, fp);
1395                                                         if (k < '0' || k > '9') {
1396                                                                 s_append_asprintf(errbuf, "line %d: too few flags in alchem_flags block", lineNumber + 1);
1397                                                                 free(valp);
1398                                                                 goto err_exit;
1399                                                         }
1400                                                         ReadLine(buf, sizeof buf, fp, &lineNumber);
1401                                                         bufp = buf;
1402                                                         while (*bufp != 0) {
1403                                                                 if (*bufp >= '0' && *bufp <= '2') {
1404                                                                         if (i >= j) {
1405                                                                                 s_append_asprintf(errbuf, "line %d: too many flags in alchem_flags block", lineNumber);
1406                                                                                 free(valp);
1407                                                                                 goto err_exit;
1408                                                                         }
1409                                                                         valp[i++] = *bufp - '0';
1410                                                                 } else if (*bufp != ' ' && *bufp != '\t' && *bufp != '\n') {
1411                                                                         s_append_asprintf(errbuf, "line %d: strange character (0x%02x) in alchem_flags block", lineNumber, (int)*bufp);
1412                                                                         free(valp);
1413                                                                         goto err_exit;
1414                                                                 }
1415                                                                 bufp++;
1416                                                         }
1417                                                         if (i == j)
1418                                                                 break;
1419                                                 }
1420                                                 md_set_alchemical_flags(arena, j, valp);
1421                                                 free(valp);
1422                                         }
1423                                         continue;
1424                                 }
1425                                 /*  In the following, the redundant "!= NULL" is to suppress suprious warning  */
1426                                 if ((strcmp(comp, "log_file") == 0 && (pp = &arena->log_result_name) != NULL)
1427                                         || (strcmp(comp, "coord_file") == 0 && (pp = &arena->coord_result_name) != NULL)
1428                                         || (strcmp(comp, "vel_file") == 0 && (pp = &arena->vel_result_name) != NULL)
1429                                         || (strcmp(comp, "force_file") == 0 && (pp = &arena->force_result_name) != NULL)
1430                                         || (strcmp(comp, "debug_file") == 0 && (pp = &arena->debug_result_name) != NULL)) {
1431                                         if (*valp == 0 || strstr(valp, "(null)") == valp)
1432                                                 *pp = NULL;
1433                                         else {
1434                                                 valp = strdup(valp);
1435                                                 if (valp != NULL) {
1436                                                         char *valp1 = strchr(valp, '\n');
1437                                                         if (valp1 != NULL)
1438                                                                 *valp1 = 0;
1439                                                 }
1440                                                 *pp = valp;
1441                                         }
1442                                 } else if ((strcmp(comp, "debug_output_level") == 0 && (ip = &arena->debug_output_level) != NULL)
1443                                                    || (strcmp(comp, "coord_output_freq") == 0 && (ip = &arena->coord_output_freq) != NULL)
1444                                                    || (strcmp(comp, "energy_output_freq") == 0 && (ip = &arena->energy_output_freq) != NULL)
1445                                                    || (strcmp(comp, "coord_frame") == 0 && (ip = &arena->coord_result_frame) != NULL)
1446                                                    || (strcmp(comp, "andersen_freq") == 0 && (ip = &arena->andersen_thermo_freq) != NULL)
1447                                                    || (strcmp(comp, "random_seed") == 0 && (ip = &arena->random_seed) != NULL)
1448                                                    || (strcmp(comp, "use_xplor_shift") == 0 && (ip = &arena->use_xplor_shift) != NULL)
1449                                                    || (strcmp(comp, "relocate_center") == 0 && (ip = &arena->relocate_center) != NULL)
1450                                                    || (strcmp(comp, "surface_potential_freq") == 0 && (ip = &arena->surface_potential_freq) != NULL)
1451                                                    || (strcmp(comp, "use_graphite") == 0 && (ip = &arena->use_graphite) != NULL)) {
1452                                         *ip = (valp == NULL ? 0 : atoi(valp));
1453                                 } else if ((strcmp(comp, "timestep") == 0 && (dp = &arena->timestep) != NULL)
1454                                                    || (strcmp(comp, "cutoff") == 0 && (dp = &arena->cutoff) != NULL)
1455                                                    || (strcmp(comp, "electro_cutoff") == 0 && (dp = &arena->electro_cutoff) != NULL)
1456                                                    || (strcmp(comp, "pairlist_distance") == 0 && (dp = &arena->pairlist_distance) != NULL)
1457                                                    || (strcmp(comp, "switch_distance") == 0 && (dp = &arena->switch_distance) != NULL)
1458                                                    || (strcmp(comp, "temperature") == 0 && (dp = &arena->temperature) != NULL)
1459                                                    || (strcmp(comp, "andersen_coupling") == 0 && (dp = &arena->andersen_thermo_coupling) != NULL)
1460                                                    || (strcmp(comp, "dielectric") == 0 && (dp = &arena->dielectric) != NULL)
1461                                                    || (strcmp(comp, "gradient_convergence") == 0 && (dp = &arena->gradient_convergence) != NULL)
1462                                                    || (strcmp(comp, "coordinate_convergence") == 0 && (dp = &arena->coordinate_convergence) != NULL)
1463                                                    || (strcmp(comp, "scale14_vdw") == 0 && (dp = &arena->scale14_vdw) != NULL)
1464                                                    || (strcmp(comp, "scale14_elect") == 0 && (dp = &arena->scale14_elect) != NULL)
1465                                                    || (strcmp(comp, "surface_probe_radius") == 0 && (dp = &arena->probe_radius) != NULL)
1466                                                    || (strcmp(comp, "surface_tension") == 0 && (dp = &arena->surface_tension) != NULL)
1467                                                    || (strcmp(comp, "alchemical_lambda") == 0 && (dp = &arena->alchem_lambda) != NULL)
1468                                                    || (strcmp(comp, "alchemical_delta_lambda") == 0 && (dp = &arena->alchem_dlambda) != NULL)) {
1469                                         *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1470                                 }
1471                         }
1472                         continue;
1473                 } else if (strcmp(buf, "!:pressure_control_parameters") == 0) {
1474                         MDPressureArena *pressure;
1475                         if (mp->arena == NULL)
1476                                 mp->arena = md_arena_new(mp);
1477                         if (mp->arena->pressure == NULL)
1478                                 mp->arena->pressure = pressure_new();
1479                         pressure = mp->arena->pressure;
1480                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1481                                 if (buf[0] == '!')
1482                                         continue;
1483                                 if (buf[0] == '\n')
1484                                         break;
1485                                 bufp = buf;
1486                                 comp = strsep(&bufp, " \t");
1487                                 if (bufp != NULL) {
1488                                         while (*bufp == ' ' || *bufp == '\t')
1489                                                 bufp++;
1490                                         valp = strsep(&bufp, "\n");
1491                                 } else valp = NULL;
1492                                 if (strcmp(comp, "pressure") == 0) {
1493                                         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) {
1494                                                 s_append_asprintf(errbuf, "line %d: bad format", lineNumber);
1495                                                 goto err_exit;
1496                                         }
1497                                         for (i = 0; i < 9; i++)
1498                                                 pressure->apply[i] = dbuf[i];
1499                                 } else if (strcmp(comp, "pressure_cell_flexibility") == 0) {
1500                                         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) {
1501                                                 s_append_asprintf(errbuf, "line %d: bad format", lineNumber);
1502                                                 goto err_exit;
1503                                         }
1504                                         for (i = 0; i < 8; i++)
1505                                                 pressure->cell_flexibility[i] = dbuf[i];
1506                                 } else if ((strcmp(comp, "pressure_freq") == 0 && (ip = &pressure->freq) != NULL)) {
1507                                         *ip = (valp == NULL ? 0 : atoi(valp));
1508                                 } else if ((strcmp(comp, "pressure_coupling") == 0 && (dp = &pressure->coupling) != NULL)
1509                                                    || (strcmp(comp, "pressure_fluctuate_cell_origin") == 0 && (dp = &pressure->fluctuate_cell_origin) != NULL)
1510                                                    || (strcmp(comp, "pressure_fluctuate_cell_orientation") == 0 && (dp = &pressure->fluctuate_cell_orientation) != NULL)) {
1511                                         *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1512                                 }
1513                         }
1514                         continue;
1515                 } else if (strcmp(buf, "!:velocity") == 0) {
1516                         i = 0;
1517                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1518                                 if (buf[0] == '!')
1519                                         continue;
1520                                 if (buf[0] == '\n')
1521                                         break;
1522                                 /* idx vx vy vz */
1523                                 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1524                                         s_append_asprintf(errbuf, "line %d: atom velocity cannot be read for atom %d", lineNumber, i + 1);
1525                                         goto err_exit;
1526                                 }
1527                                 if (i >= mp->natoms) {
1528                                         s_append_asprintf(errbuf, "line %d: too many atom velocity records\n", lineNumber);
1529                                         goto err_exit;
1530                                 }
1531                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1532                                 ap->v.x = dbuf[0];
1533                                 ap->v.y = dbuf[1];
1534                                 ap->v.z = dbuf[2];
1535                                 i++;
1536                         }
1537                         continue;
1538                 } else if (strcmp(buf, "!:force") == 0) {
1539                         i = 0;
1540                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1541                                 if (buf[0] == '!')
1542                                         continue;
1543                                 if (buf[0] == '\n')
1544                                         break;
1545                                 /* idx fx fy fz */
1546                                 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1547                                         s_append_asprintf(errbuf, "line %d: atom force cannot be read for atom %d", lineNumber, i + 1);
1548                                         goto err_exit;
1549                                 }
1550                                 if (i >= mp->natoms) {
1551                                         s_append_asprintf(errbuf, "line %d: too many atom force records\n", lineNumber);
1552                                         goto err_exit;
1553                                 }
1554                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1555                                 ap->f.x = dbuf[0];
1556                                 ap->f.y = dbuf[1];
1557                                 ap->f.z = dbuf[2];
1558                                 i++;
1559                         }
1560                         continue;
1561                 } else if (strcmp(buf, "!:parameter") == 0 || strcmp(buf, "!:parameters") == 0) {
1562                         Parameter *par = mp->par;
1563                         if (par == NULL) {
1564                                 mp->par = ParameterNew();
1565                                 par = mp->par;
1566                         }
1567                         bufp = NULL;
1568                         i = 0;
1569                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1570                                 if (buf[0] == '!')
1571                                         continue;
1572                                 if (buf[0] == '\n')
1573                                         break;
1574                                 j = ParameterReadFromString(par, buf, &bufp, fname, lineNumber, 0);
1575                                 if (j < 0) {
1576                                         s_append_asprintf(errbuf, "%s", bufp);
1577                                         free(bufp);
1578                                         goto err_exit;
1579                                 }
1580                                 i += j;
1581                         }
1582                         if (bufp != NULL) {
1583                                 s_append_asprintf(errbuf, "%s", bufp);
1584                                 free(bufp);
1585                         }
1586                         continue;
1587                 } else if (strcmp(buf, "!:trackball") == 0) {
1588                         i = 0;
1589                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1590                                 if (buf[0] == '!')
1591                                         continue;
1592                                 if (buf[0] == '\n')
1593                                         break;
1594                                 /* scale; trx try trz; theta_deg x y z */
1595                                 if ((i == 0 && sscanf(buf, "%lf", &mview_dbuf[0]) < 1)
1596                                         || (i == 1 && sscanf(buf, "%lf %lf %lf",
1597                                                                                  &mview_dbuf[1], &mview_dbuf[2], &mview_dbuf[3]) < 3)
1598                                         || (i == 2 && sscanf(buf, "%lf %lf %lf %lf",
1599                                                                                  &mview_dbuf[4], &mview_dbuf[5], &mview_dbuf[6], &mview_dbuf[7]) < 4)) {
1600                                         s_append_asprintf(errbuf, "line %d: bad trackball format", lineNumber);
1601                                         goto err_exit;
1602                                 }
1603                                 i++;
1604                         }
1605                         continue;
1606                 } else if (strcmp(buf, "!:view") == 0) {
1607                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1608                                 if (buf[0] == '!')
1609                                         continue;
1610                                 if (buf[0] == '\n')
1611                                         break;
1612                                 bufp = buf;
1613                                 comp = strsep(&bufp, " \t");
1614                                 if (bufp != NULL) {
1615                                         while (*bufp == ' ' || *bufp == '\t')
1616                                                 bufp++;
1617                                         valp = strsep(&bufp, "\n");
1618                                 } else valp = NULL;
1619                                 /*  In the following, the redundant "!= NULL" is to suppress suprious warning  */
1620                                 if ((strcmp(comp, "show_unit_cell") == 0 && (i = 1))
1621                                         || (strcmp(comp, "show_periodic_box") == 0 && (i = 2))
1622                                         || (strcmp(comp, "show_expanded_atoms") == 0 && (i = 3))
1623                                         || (strcmp(comp, "show_ellipsoids") == 0 && (i = 4))
1624                                         || (strcmp(comp, "show_hydrogens") == 0 && (i = 5))
1625                                         || (strcmp(comp, "show_dummy_atoms") == 0 && (i = 6))
1626                                         || (strcmp(comp, "show_rotation_center") == 0 && (i = 7))
1627                                         || (strcmp(comp, "show_graphite_flag") == 0 && (i = 8))
1628                                         || (strcmp(comp, "show_periodic_image_flag") == 0 && (i = 9))
1629                                         || (strcmp(comp, "show_graphite") == 0 && (i = 10))
1630                                         || (strcmp(comp, "atom_resolution") == 0 && (i = 11))
1631                                         || (strcmp(comp, "bond_resolution") == 0 && (i = 12))) {
1632                                         mview_ibuf[i - 1] = atoi(valp);
1633                                 } else if ((strcmp(comp, "atom_radius") == 0 && (i = 8))
1634                                         || (strcmp(comp, "bond_radius") == 0 && (i = 9))) {
1635                                         mview_dbuf[i] = strtod(valp, NULL);
1636                                 } else if (strcmp(comp, "show_periodic_image") == 0) {
1637                                         sscanf(valp, "%d %d %d %d %d %d",
1638                                                    &mview_ibuf[12], &mview_ibuf[13], &mview_ibuf[14],
1639                                                    &mview_ibuf[15], &mview_ibuf[16], &mview_ibuf[17]);
1640                                 }
1641                         }
1642                         continue;
1643                 }
1644                 /*  Unknown sections are silently ignored  */
1645         }
1646
1647         MoleculeCleanUpResidueTable(mp);
1648         if (mp->arena != NULL)
1649                 md_arena_set_molecule(mp->arena, mp);
1650
1651         fclose(fp);
1652         if (mp->mview != NULL) {
1653                 if (mview_ibuf[0] != kUndefined)
1654                         mp->mview->showUnitCell = mview_ibuf[0];
1655                 if (mview_ibuf[1] != kUndefined)
1656                         mp->mview->showPeriodicBox = mview_ibuf[1];
1657                 if (mview_ibuf[2] != kUndefined)
1658                         mp->mview->showExpandedAtoms = mview_ibuf[2];
1659                 if (mview_ibuf[3] != kUndefined)
1660                         mp->mview->showEllipsoids = mview_ibuf[3];
1661                 if (mview_ibuf[4] != kUndefined)
1662                         mp->mview->showHydrogens = mview_ibuf[4];
1663                 if (mview_ibuf[5] != kUndefined)
1664                         mp->mview->showDummyAtoms = mview_ibuf[5];
1665                 if (mview_ibuf[6] != kUndefined)
1666                         mp->mview->showRotationCenter = mview_ibuf[6];
1667                 if (mview_ibuf[7] != kUndefined)
1668                         mp->mview->showGraphiteFlag = mview_ibuf[7];
1669                 if (mview_ibuf[8] != kUndefined)
1670                         mp->mview->showPeriodicImageFlag = mview_ibuf[8];
1671                 if (mview_ibuf[9] != kUndefined)
1672                         mp->mview->showGraphite = mview_ibuf[9];
1673                 if (mview_ibuf[10] != kUndefined && mview_ibuf[10] >= 6)
1674                         mp->mview->atomResolution = mview_ibuf[10];
1675                 if (mview_ibuf[11] != kUndefined && mview_ibuf[11] >= 4)
1676                         mp->mview->bondResolution = mview_ibuf[11];
1677                 for (i = 0; i < 6; i++) {
1678                         if (mview_ibuf[12 + i] != kUndefined)
1679                                 mp->mview->showPeriodicImage[i] = mview_ibuf[12 + i];
1680                 }
1681                 if (mview_dbuf[8] != kUndefined)
1682                         mp->mview->atomRadius = mview_dbuf[8];
1683                 if (mview_dbuf[9] != kUndefined)
1684                         mp->mview->bondRadius = mview_dbuf[9];          
1685                 if (mp->mview->track != NULL) {
1686                         if (mview_dbuf[0] != kUndefined)
1687                                 TrackballSetScale(mp->mview->track, mview_dbuf[0]);
1688                         if (mview_dbuf[1] != kUndefined)
1689                                 TrackballSetTranslate(mp->mview->track, mview_dbuf + 1);
1690                         if (mview_dbuf[4] != kUndefined)
1691                                 TrackballSetRotate(mp->mview->track, mview_dbuf + 4);
1692                 }
1693         }
1694
1695         return 0;
1696
1697 err_exit:
1698         fclose(fp);
1699         /*  The content of mp may be broken, so make it empty  */
1700         MoleculeClear(mp);
1701         return -1;      
1702 }
1703
1704 int
1705 MoleculeLoadPsfFile(Molecule *mp, const char *fname, char **errbuf)
1706 {
1707         FILE *fp;
1708         char buf[1024];
1709         char *p;
1710         int section = -1;
1711         int i, j, err, fn;
1712         int lineNumber;
1713         Int ibuf[12];
1714         Vector *frames = NULL;
1715         Atom *ap;
1716         err = 0;
1717         *errbuf = NULL;
1718         if (mp == NULL)
1719                 mp = MoleculeNew();
1720         else MoleculeClear(mp);
1721         fp = fopen(fname, "rb");
1722         if (fp == NULL) {
1723                 s_append_asprintf(errbuf, "Cannot open file");
1724                 return 1;
1725         }
1726 /*      flockfile(fp); */
1727         lineNumber = 0;
1728         fn = 0;
1729         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1730                 if (strncmp(buf, "PSF", 3) == 0) {
1731                         section = 0;
1732                         continue;
1733                 } else {
1734                         for (p = buf; *p != 0 && isspace(*p); p++) {}
1735                         if (*p == 0) {
1736                                 section++;
1737                                 continue;
1738                         }
1739                 }
1740                 if (strstr(buf, "!COORD") != NULL) {
1741                         /*  Extended psf file with coordinates  */
1742                         if (fn > 0) {
1743                                 /*  Allocate a temporary storage for frames  */
1744                                 size_t size = sizeof(Vector) * mp->natoms * fn;
1745                                 if (frames == NULL)
1746                                         frames = (Vector *)malloc(size);
1747                                 else
1748                                         frames = (Vector *)realloc(frames, size);
1749                                 if (frames == NULL)
1750                                         goto panic;
1751                         }
1752                         /*  Read coordinates  */
1753                         for (i = 0; i < mp->natoms; i++) {
1754                                 double dval[3];
1755                                 Vector r;
1756                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1757                                         err = 1;
1758                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading coordinates (frame %d)", lineNumber, fn);
1759                                         goto exit;
1760                                 }
1761                                 if (sscanf(buf, "%lg %lg %lg", dval, dval + 1, dval + 2) != 3) {
1762                                         err = 1;
1763                                         s_append_asprintf(errbuf, "line %d: coordinates cannot be read for atom %d", lineNumber, i + 1);
1764                                         goto exit;
1765                                 }
1766                                 r.x = dval[0];
1767                                 r.y = dval[1];
1768                                 r.z = dval[2];
1769                                 if (fn == 0)
1770                                         ATOM_AT_INDEX(mp->atoms, i)->r = r;
1771                                 else
1772                                         frames[mp->natoms * (fn - 1) + i] = r;
1773                         }
1774                         fn++;
1775                         continue;
1776                 }
1777                 
1778                 if (section == 2) {
1779                         /*  Atoms  */
1780                         Int natoms;
1781                         ReadFormat(buf, "I8", &natoms);
1782                         if (natoms == 0)
1783                                 continue;
1784                         if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
1785                                 goto panic;
1786                         mp->nresidues = 0;
1787                         for (i = 0; i < natoms; i++) {
1788                                 struct {
1789                                         char segName[5], resName[4], atomName[5], atomType[3], element[3];
1790                                         Int serial;
1791                                 } w;
1792                                 memset(&w, 0, sizeof(w));
1793                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1794                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1795                                         err = 1;
1796                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading atoms", lineNumber);
1797                                         goto exit;
1798                                 }
1799                                 ReadFormat(buf, "I8 x1 S4 I5 x1 S3 x2 S4 x1 S4 F16 F10",
1800                                         &w.serial, w.segName, &ap->resSeq, w.resName, w.atomName, 
1801                                         w.atomType, &ap->charge, &ap->weight);
1802                                 strncpy(ap->segName, w.segName, 4);
1803                                 strncpy(ap->resName, w.resName, 3);
1804                                 strncpy(ap->aname, w.atomName, 4);
1805                                 ap->type = AtomTypeEncodeToUInt(w.atomType);
1806                                 /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
1807                                 ap->atomicNumber = GuessAtomicNumber(w.atomName, ap->weight);
1808                                 ElementToString(ap->atomicNumber, w.element);
1809                                 strncpy(ap->element, w.element, 2);
1810                         /*      w.element[0] = 0;
1811                                 for (p = w.atomName; *p != 0; p++) {
1812                                         if (isalpha(*p) && *p != '_') {
1813                                                 w.element[0] = toupper(*p);
1814                                                 if (isalpha(p[1]) && p[1] != '_') {
1815                                                         w.element[1] = toupper(p[1]);
1816                                                         w.element[2] = 0;
1817                                                 } else {
1818                                                         w.element[1] = 0;
1819                                                 }
1820                                                 break;
1821                                         }
1822                                 }
1823                                 strncpy(ap->element, w.element, 2);
1824                                 ap->atomicNumber = ElementToInt(w.element); */
1825                                 if (w.resName[0] == 0)
1826                                         strncpy(ap->resName, "XXX", 3);
1827                                 if (ap->resSeq > mp->nresidues)
1828                                         mp->nresidues = ap->resSeq;
1829                         }
1830                         if (mp->residues != NULL)
1831                                 free(mp->residues);
1832                         if (NewArray(&mp->residues, &mp->nresidues, sizeof(char (*)[4]), mp->nresidues + 1) == 0)
1833                                 goto panic;
1834                         for (i = 0; i < mp->natoms; i++) {
1835                                 j = mp->atoms[i].resSeq;
1836                                 if (mp->residues[j][0] == 0)
1837                                         strncpy(mp->residues[j], mp->atoms[i].resName, 4);
1838                         }
1839                         continue;
1840                 } else if (section == 3) {
1841                         /*  Bonds  */
1842                         Int nbonds;
1843                         Int *bp;
1844                         ReadFormat(buf, "I8", &nbonds);
1845                         if (nbonds == 0)
1846                                 continue;
1847                         if (NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, nbonds) == NULL)
1848                                 goto panic;
1849                         bp = mp->bonds;
1850                         for (i = 0; i < nbonds; i += 4) {
1851                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1852                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading bonds", lineNumber);
1853                                         err = 1;
1854                                         goto exit;
1855                                 }
1856                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1857                                         ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1858                                 for (j = 0; j < 4 && i + j < nbonds; j++) {
1859                                         Int b1, b2;
1860                                         Atom *ap;
1861                                         b1 = ibuf[j * 2] - 1;    /* Internal atom number is 0-based */
1862                                         b2 = ibuf[j * 2 + 1] - 1;
1863                                         if (b1 < 0 || b1 >= mp->natoms || b2 < 0 || b2 >= mp->natoms) {
1864                                                 s_append_asprintf(errbuf, "line %d: The bond %d-%d includes non-existent atom", lineNumber, b1+1, b2+1);
1865                                                 err = 1;
1866                                                 goto exit;
1867                                         }
1868                                         *bp++ = b1;
1869                                         *bp++ = b2;
1870                                         ap = ATOM_AT_INDEX(mp->atoms, b1);
1871                                         AtomConnectInsertEntry(&ap->connect, -1, b2);
1872                                         ap = ATOM_AT_INDEX(mp->atoms, b2);
1873                                         AtomConnectInsertEntry(&ap->connect, -1, b1);
1874                                 }
1875                         }
1876                         continue;
1877                 } else if (section == 4) {
1878                         /*  Angles  */
1879                         Int nangles;
1880                         Int *gp;
1881                         ReadFormat(buf, "I8", &nangles);
1882                         if (nangles == 0)
1883                                 continue;
1884                         if (NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, nangles) == NULL)
1885                                 goto panic;
1886                         gp = mp->angles;
1887                         for (i = 0; i < nangles; i += 3) {
1888                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1889                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading angles", lineNumber);
1890                                         err = 1;
1891                                         goto exit;
1892                                 }
1893                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1894                                         ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7, ibuf + 8);
1895                                 for (j = 0; j < 3 && i + j < nangles; j++) {
1896                                         Int a1, a2, a3;
1897                                         a1 = ibuf[j * 3] - 1;   /* Internal atom number is 0-based */
1898                                         a2 = ibuf[j * 3 + 1] - 1;
1899                                         a3 = ibuf[j * 3 + 2] - 1;
1900                                         if (a1 < 0 || a1 >= mp->natoms || a2 < 0 || a2 >= mp->natoms || a3 < 0 || a3 >= mp->natoms) {
1901                                                 s_append_asprintf(errbuf, "line %d: The angle %d-%d-%d includes non-existent atom", lineNumber, a1+1, a2+1, a3+1);
1902                                                 err = 1;
1903                                                 goto exit;
1904                                         }
1905                                         *gp++ = a1;
1906                                         *gp++ = a2;
1907                                         *gp++ = a3;
1908                                 }
1909                         }
1910                         continue;
1911                 } else if (section == 5 || section == 6) {
1912                         /*  Dihedrals and Impropers  */
1913                         Int ndihedrals;
1914                         Int *dp;
1915                         ReadFormat(buf, "I8", &ndihedrals);
1916                         if (ndihedrals == 0)
1917                                 continue;
1918                         if (section == 5) {
1919                                 if (NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, ndihedrals) == NULL)
1920                                         goto panic;
1921                                 dp = mp->dihedrals;
1922                         } else {
1923                                 if (NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, ndihedrals) == NULL)
1924                                         goto panic;
1925                                 dp = mp->impropers;
1926                         }
1927                         for (i = 0; i < ndihedrals; i += 2) {
1928                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1929                                         fclose(fp);
1930                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading %s", lineNumber, (section == 5 ? "dihedral" : "improper"));
1931                                         err = 1;
1932                                         goto exit;
1933                                 }
1934                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3, ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1935                                 for (j = 0; j < 2 && i + j < ndihedrals; j++) {
1936                                         Int d1, d2, d3, d4;
1937                                         d1 = ibuf[j * 4] - 1;   /*  Internal atom number is 0-based  */
1938                                         d2 = ibuf[j * 4 + 1] - 1;
1939                                         d3 = ibuf[j * 4 + 2] - 1;
1940                                         d4 = ibuf[j * 4 + 3] - 1;
1941                                         if (d1 < 0 || d1 >= mp->natoms || d2 < 0 || d2 >= mp->natoms || d3 < 0 || d3 >= mp->natoms || d4 < 0 || d4 >= mp->natoms) {
1942                                                 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);
1943                                                 err = 1;
1944                                                 goto exit;
1945                                         }
1946                                         *dp++ = d1;
1947                                         *dp++ = d2;
1948                                         *dp++ = d3;
1949                                         *dp++ = d4;
1950                                 }
1951                         }
1952                         continue;
1953                 }
1954         }
1955         
1956         /*  Create frames for each atom if necessary  */
1957         if (fn > 1) {
1958                 for (i = 0; i < mp->natoms; i++) {
1959                         ap = ATOM_AT_INDEX(mp->atoms, i);
1960                         ap->frames = (Vector *)malloc(sizeof(Vector) * fn);
1961                         if (ap->frames == NULL)
1962                                 goto panic;
1963                         ap->nframes = fn;
1964                         for (j = 0; j < fn; j++)
1965                                 ap->frames[j] = frames[mp->natoms * j + i];
1966                 }
1967                 free(frames);
1968                 frames = NULL;
1969         }
1970
1971   exit:
1972 /*      funlockfile(fp); */
1973         fclose(fp);
1974         mp->nframes = -1;  /*  Should be recalculated later  */
1975         if (err)
1976                 return 1;
1977         else if (section == -1)
1978                 return -1;
1979         return 0;
1980   panic:
1981         Panic("low memory while reading structure file %s", fname);
1982         return 1; /* not reached */
1983 }
1984
1985 /* ("-x", "y", "-z+0.5") -> (-1,0,0,0,0,1,0,0,0,0,-1,0.5)  */
1986 static int
1987 sMoleculeSymopStringsToTransform(char **symops, Transform tr)
1988 {
1989         int i;
1990         char *symop;
1991         memset(tr, 0, sizeof(Transform));
1992         for (i = 0; i < 3; i++) {
1993                 symop = symops[i];
1994                 if (symop == NULL)
1995                         return 1;
1996                 while (*symop != 0) {
1997                         int sn = 1;
1998                         while (isspace(*symop))
1999                                 symop++;
2000                         if (*symop == 0 || *symop == '\r' || *symop == 'n')
2001                                 break;
2002                         if (*symop == '-') {
2003                                 sn = -1;
2004                                 symop++;
2005                         } else if (*symop == '+') {
2006                                 sn = 1;
2007                                 symop++;
2008                         }
2009                         while (isspace(*symop))
2010                                 symop++;
2011                         if (*symop == '.' || isdigit(*symop)) {
2012                                 /*  Numerical offset  */
2013                                 double d = strtod(symop, &symop);
2014                                 if (*symop == '/') {
2015                                         double dd = strtod(symop + 1, &symop);
2016                                         if (dd > 0)
2017                                                 d /= dd;
2018                                         else
2019                                                 return 1;  /*  Bad format  */
2020                                 }
2021                                 tr[9 + i] = d * sn;
2022                         } else if (*symop == 'x' || *symop == 'X') {
2023                                 tr[i] = sn;
2024                                 symop++;
2025                         } else if (*symop == 'y' || *symop == 'Y') {
2026                                 tr[i + 3] = sn;
2027                                 symop++;
2028                         } else if (*symop == 'z' || *symop == 'Z') {
2029                                 tr[i + 6] = sn;
2030                                 symop++;
2031                         } else return 1;  /*  Bad format  */
2032                 } /* end while (*symop != 0) */
2033         }
2034         return 0;
2035 }
2036
2037 static void
2038 sMoleculeGenerateSymopWithTransform(Molecule *mp, Transform gtr, int num)
2039 {
2040         int i, j;
2041         Transform tr;
2042         if (num <= 0)
2043                 num = mp->nsyms;
2044         for (i = 0; i < num; i++) {
2045                 memmove(tr, mp->syms[i], sizeof(Transform));
2046                 TransformMul(tr, gtr, tr);
2047                 for (j = 9; j < 12; j++) {
2048                         if (tr[j] >= 1.0)
2049                                 tr[j] -= 1.0;
2050                         else if (tr[j] <= 0.0)
2051                                 tr[j] += 1.0;
2052                 }
2053                 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
2054         }
2055 }
2056
2057 static char *
2058 sChomp(char *buf)
2059 {
2060         char *p = buf + strlen(buf) - 1;
2061         if (p >= buf && (*p == '\n' || *p == '\r')) {
2062                 *p = 0;
2063                 if (--p >= buf && (*p == '\n' || *p == '\r')) {
2064                         *p = 0;
2065                 }
2066         }
2067         return buf;
2068 }
2069
2070 int
2071 MoleculeLoadTepFile(Molecule *mp, const char *fname, char **errbuf)
2072 {
2073         FILE *fp;
2074         char buf[1024];
2075         int section = -1;
2076         int lineNumber;
2077         int cellType;
2078         Int ibuf[12];
2079         Double fbuf[12];
2080         Int *bonds, nbonds;
2081         *errbuf = NULL;
2082         if (mp == NULL)
2083                 mp = MoleculeNew();
2084         fp = fopen(fname, "rb");
2085         if (fp == NULL) {
2086                 s_append_asprintf(errbuf, "Cannot open file");
2087                 return 1;
2088         }
2089         lineNumber = 0;
2090         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2091                 if (section == -1) {
2092                         /*  Title  */
2093                         section = 0;
2094                         continue;
2095                 }
2096                 if (section == 0) {
2097                         /*  XtalCell  */
2098                         ReadFormat(buf, "I1F8F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5);
2099                         cellType = ibuf[0];
2100                         MoleculeSetCell(mp, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], 0);
2101                         section = 1;
2102                         continue;
2103                 }
2104                 if (section == 1) {
2105                         /*  Symmetry  */
2106                         Transform tr;
2107                         if (cellType == 0) {
2108                                 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);
2109                                 tr[0] = fbuf[1];
2110                                 tr[3] = fbuf[2];
2111                                 tr[6] = fbuf[3];
2112                                 tr[1] = fbuf[5];
2113                                 tr[4] = fbuf[6];
2114                                 tr[7] = fbuf[7];
2115                                 tr[2] = fbuf[9];
2116                                 tr[5] = fbuf[10];
2117                                 tr[8] = fbuf[11];
2118                                 tr[9] = fbuf[0];
2119                                 tr[10] = fbuf[4];
2120                                 tr[11] = fbuf[8];
2121                         } else {
2122                                 char *symops[3], *brks;
2123                                 sChomp(buf);
2124                                 memset(tr, 0, sizeof(Transform));
2125                                 ReadFormat(buf, "I1", ibuf);
2126                                 symops[0] = strtok_r(buf + 1, ", ", &brks);
2127                                 symops[1] = strtok_r(NULL, ", ", &brks);
2128                                 symops[2] = strtok_r(NULL, ", ", &brks);
2129                                 if (sMoleculeSymopStringsToTransform(symops, tr)) {
2130                                         s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2131                                         return 1;
2132                                 }
2133                         }
2134                         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2135                                 goto panic;
2136                         if (ibuf[0] != 0)
2137                                 section = 2;
2138                         continue;
2139                 }
2140                 if (section == 2) {      /*  Atoms  */
2141                         char name[8];
2142                         Atom *ap;
2143                         int atomType;
2144                         int atomIndex = mp->natoms;
2145                         ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2146                         memset(ap, 0, gSizeOfAtomRecord);
2147                         ReadFormat(buf, "S6x3x9x9F9F9F9F9", name, fbuf, fbuf+1, fbuf+2, fbuf+3);
2148                         strncpy(ap->aname, name, 4);
2149                         ap->r.x = fbuf[0];
2150                         ap->r.y = fbuf[1];
2151                         ap->r.z = fbuf[2];
2152                         MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2153                 /*      ap->atomicNumber = AtomNameToElement(ap->name);
2154                         ElementToString(ap->atomicNumber, ap->element); */
2155                 /*      sAtomSetElement(ap, -1, ap->name); */
2156                         guessElement(ap);
2157                         atomType = fbuf[3];
2158                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2159                                 s_append_asprintf(errbuf, "unexpected end of file");
2160                                 return 1;
2161                         }
2162                         ReadFormat(buf, "I1F8F9F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2163                         atomType = fbuf[6];
2164                         if ((atomType >= 0 && atomType <= 5) || (atomType >= 8 && atomType <= 10)) { 
2165                                 /*  Anisotropic thermal parameters  */
2166                                 MoleculeSetAniso(mp, atomIndex, atomType, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[5], fbuf[4], NULL);
2167                         }
2168                         if (ibuf[0] != 0)
2169                                 section = 3;
2170                         continue;
2171                 }
2172         }
2173         fclose(fp);
2174         MoleculeGuessBonds(mp, 0.0, &nbonds, &bonds);
2175         if (nbonds > 0) {
2176                 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2177                 free(bonds);
2178         }
2179         mp->nframes = -1;  /*  Should be recalculated later  */
2180         return 0;
2181   panic:
2182         Panic("low memory while reading structure file %s", fname);
2183         return -1; /* not reached */
2184 }
2185
2186 int
2187 MoleculeLoadShelxFile(Molecule *mp, const char *fname, char **errbuf)
2188 {
2189         FILE *fp;
2190         char buf[1024];
2191         char *p1, *p2;
2192         int n;
2193         int lineNumber;
2194         int latticeType;
2195         int currentResSeq = 0;
2196         char currentResName[6];
2197         Transform tr;
2198         int ibuf[12];
2199         float fbuf[12];
2200         Double dbuf[12];
2201         Int nsfacs = 0;
2202         Int nbonds, *bonds;
2203         char (*sfacs)[4] = NULL;
2204
2205         *errbuf = NULL;
2206         if (mp == NULL)
2207                 mp = MoleculeNew();
2208         currentResName[0] = 0;
2209         fp = fopen(fname, "rb");
2210         if (fp == NULL) {
2211                 s_append_asprintf(errbuf, "Cannot open file");
2212                 return 1;
2213         }
2214         lineNumber = 0;
2215         tr[0] = tr[4] = tr[8] = 1;
2216         tr[1] = tr[2] = tr[3] = tr[5] = tr[6] = tr[7] = tr[9] = tr[10] = tr[11] = 0;
2217         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), 0, tr) == 0)
2218                 goto panic;
2219         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2220                 if (strncmp(buf, "CELL", 4) == 0) {
2221                         /*  XtalCell  */
2222                         sscanf(buf + 4, " %f %f %f %f %f %f %f", fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2223                         MoleculeSetCell(mp, fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], fbuf[6], 0);
2224                         continue;
2225                 } else if (strncmp(buf, "SFAC", 4) == 0) {
2226                         sChomp(buf);
2227                         for (p1 = strtok_r(buf + 4, " ", &p2); p1 != NULL; p1 = strtok_r(NULL, " ", &p2)) {
2228                                 char *pp = (char *)AssignArray(&sfacs, &nsfacs, 4, nsfacs, NULL);
2229                                 if (pp == NULL)
2230                                         goto panic;
2231                                 strncpy(pp, p1, 3);
2232                                 pp[3] = 0;
2233                         }
2234                         continue;
2235                 } else if (strncmp(buf, "LATT", 4) == 0) {
2236                         sscanf(buf + 4, " %d", &latticeType);
2237                         continue;
2238                 } else if (strncmp(buf, "SYMM", 4) == 0) {
2239                         char *symops[3], *brks;
2240                         memset(tr, 0, sizeof(Transform));
2241                 //      ReadFormat(buf + 4, "I1", ibuf);
2242                         sChomp(buf);
2243                         symops[0] = strtok_r(buf + 4, ",", &brks);
2244                         symops[1] = strtok_r(NULL, ",", &brks);
2245                         symops[2] = strtok_r(NULL, ",", &brks);
2246                         if (sMoleculeSymopStringsToTransform(symops, tr)) {
2247                                 s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2248                                 return 1;
2249                         }
2250                         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2251                                 goto panic;
2252                         continue;
2253                 } else if (strncmp(buf, "RESI", 4) == 0) {
2254                         for (p1 = buf + 4; isspace(*p1); p1++);
2255                         if (isalpha(*p1)) {
2256                                 for (p2 = p1 + 1; isalnum(*p2); p2++);
2257                                 *p2 = 0;
2258                                 strncpy(currentResName, p1, 4);
2259                                 currentResName[4] = 0;
2260                                 p1 = p2 + 1;
2261                         } else currentResName[0] = 0;
2262                         sscanf(buf + 4, " %d", &currentResSeq);
2263                         continue;
2264                 } else {
2265                         /* Atom name: [A-Za-z]{1,2}[0-9]*  */
2266                         for (p1 = buf; p1 < buf + 2 && (isalpha(*p1) && *p1 != '_'); p1++);
2267                         if (p1 > buf) {
2268                                 while (isdigit(*p1))
2269                                         p1++;
2270                         }
2271                         if (p1 > buf && p1 <= buf + 4 && isspace(*p1)) {
2272                                 /*  Atom  */
2273                                 Atom *ap;
2274                                 char cont[4];
2275                                 int atomIndex = mp->natoms;
2276                                 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2277                                 memset(ap, 0, gSizeOfAtomRecord);
2278                                 strncpy(ap->aname, buf, 4);
2279                                 n = sscanf(p1, " %d %f %f %f %f %f %f %2s", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, cont);
2280                                 if (n == 8 && strcmp(cont, "=") == 0) {
2281                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2282                                                 s_append_asprintf(errbuf, "line %d: unexpected end of file within the atom cards", lineNumber);
2283                                                 return 1;
2284                                         }
2285                                         sscanf(buf, " %f %f %f %f", fbuf+6, fbuf+7, fbuf+8, fbuf+9);
2286                                         n = 10;   /*  Aniso  */
2287                                 } else n = 5; /*  Iso  */
2288                                 ap->r.x = fbuf[0];
2289                                 ap->r.y = fbuf[1];
2290                                 ap->r.z = fbuf[2];
2291                                 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2292                                 ap->occupancy = fbuf[3];
2293                                 if (ap->aname[0] != 'Q' && ibuf[0] >= 1 && ibuf[0] <= nsfacs) {
2294                                         strncpy(ap->element, sfacs[ibuf[0] - 1], 2);
2295                                         ap->element[2] = 0;
2296                                 /*      sAtomSetElement(ap, -1, sfacs[ibuf[0] - 1]); */
2297                                 /*      strncpy(ap->element, sfacs[ibuf[0] - 1], 4);
2298                                         ap->atomicNumber = ElementToInt(ap->element); */
2299                         /*      } else {
2300                                         sAtomSetElement(ap, -1, ap->name); */
2301                                 /*      ap->atomicNumber = AtomNameToElement(ap->name);
2302                                         ElementToString(ap->atomicNumber, ap->element); */
2303                                 }
2304                                 guessElement(ap);
2305                                 if (n == 10 || fbuf[4] >= 0.0) {
2306                                         int i, c, j;
2307                                         /*  Read in the standard deviations  */
2308                                         ReadLine(buf, sizeof buf, fp, &lineNumber);
2309                                         for (i = 0; i < 9; i++) {
2310                                                 j = 3 + i * 8;
2311                                                 c = buf[j + 8];
2312                                                 buf[j + 8] = 0;
2313                                                 dbuf[i] = strtod(buf + j, NULL);
2314                                                 buf[j + 8] = c;
2315                                         }
2316                                         ap->sigma.x = dbuf[0];
2317                                         ap->sigma.y = dbuf[1];
2318                                         ap->sigma.z = dbuf[2];
2319                                 }
2320                                 if (n == 5)
2321                                         ap->tempFactor = fbuf[4] * 78.9568352087147; /* 8*pi*pi */
2322                                 else
2323                                         MoleculeSetAniso(mp, atomIndex, 8, fbuf[4], fbuf[5], fbuf[6], fbuf[9], fbuf[7], fbuf[8], dbuf);
2324                                 ap->resSeq = currentResSeq;
2325                                 strncpy(ap->resName, currentResName, 4);
2326                         }
2327                         continue;
2328                 }
2329         }
2330         fclose(fp);
2331
2332         /*  Add symmetry operations according to the lattice type  */
2333         switch (latticeType < 0 ? -latticeType : latticeType) {
2334                 static Transform tr_i = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0.5};
2335                 static Transform tr_c = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0};
2336                 static Transform tr_a = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.5};
2337                 static Transform tr_b = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5};
2338                 static Transform tr_r1 = {0, 1, 0, -1, -1, 0, 0, 0, 1, 0, 0, 0};
2339                 static Transform tr_r2 = {-1, -1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0};
2340                 case 1:  /* P */
2341                         break;
2342                 case 2:  /* I */
2343                         sMoleculeGenerateSymopWithTransform(mp, tr_i, 0);
2344                         break;
2345                 case 3:  /* Rhombohedral obverse on hexagonal axes  */
2346                         n = mp->nsyms;
2347                         sMoleculeGenerateSymopWithTransform(mp, tr_r1, n);
2348                         sMoleculeGenerateSymopWithTransform(mp, tr_r2, n);
2349                         break;
2350                 case 4:  /* F */
2351                         n = mp->nsyms;
2352                         sMoleculeGenerateSymopWithTransform(mp, tr_a, n);
2353                         sMoleculeGenerateSymopWithTransform(mp, tr_b, n);
2354                         sMoleculeGenerateSymopWithTransform(mp, tr_c, n);
2355                         break;
2356                 case 5:  /* A */
2357                         sMoleculeGenerateSymopWithTransform(mp, tr_a, 0);
2358                         break;
2359                 case 6:  /* B */
2360                         sMoleculeGenerateSymopWithTransform(mp, tr_b, 0);
2361                         break;
2362                 case 7:  /* C */
2363                         sMoleculeGenerateSymopWithTransform(mp, tr_c, 0);
2364                         break;
2365         }
2366                 
2367         if (latticeType > 0) {
2368                 static Transform tr_inv = {-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0};
2369                 sMoleculeGenerateSymopWithTransform(mp, tr_inv, 0);
2370         }
2371         
2372         MoleculeGuessBonds(mp, 0.0, &nbonds, &bonds);
2373         if (nbonds > 0) {
2374                 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2375                 free(bonds);
2376         }
2377         mp->nframes = -1;  /*  Should be recalculated later  */
2378         return 0;
2379   panic:
2380         Panic("low memory while reading structure file %s", fname);
2381         return -1; /* not reached */
2382 }
2383
2384 /*  Add one gaussian orbital shell information (not undoable)  */
2385 int
2386 MoleculeAddGaussianOrbitalShell(Molecule *mol, Int sym, Int nprims, Int a_idx)
2387 {
2388         BasisSet *bset;
2389         ShellInfo *shellp;
2390         if (mol == NULL)
2391                 return -1;  /*  Molecule is empty  */
2392         bset = mol->bset;
2393         if (bset == NULL) {
2394                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2395                 if (bset == NULL)
2396                         return -2;  /*  Low memory  */
2397         }
2398         shellp = AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), bset->nshells, NULL);
2399         if (shellp == NULL)
2400                 return -2;  /*  Low memory  */
2401         switch (sym) {
2402                 case 0:  shellp->sym = kGTOType_S;  shellp->ncomp = 1; break;
2403                 case 1:  shellp->sym = kGTOType_P;  shellp->ncomp = 3; break;
2404                 case -1: shellp->sym = kGTOType_SP; shellp->ncomp = 4; break;
2405                 case 2:  shellp->sym = kGTOType_D;  shellp->ncomp = 6; break;
2406                 case -2: shellp->sym = kGTOType_D5; shellp->ncomp = 5; break;
2407                 case 3:  shellp->sym = kGTOType_F;  shellp->ncomp = 10; break;
2408                 case -3: shellp->sym = kGTOType_F7; shellp->ncomp = 7; break;
2409                 case 4:  shellp->sym = kGTOType_G;  shellp->ncomp = 15; break;
2410                 case -4: shellp->sym = kGTOType_G9; shellp->ncomp = 9; break;
2411                 default:
2412                         return -3;  /* Unsupported shell type  */
2413         }
2414         shellp->nprim = nprims;
2415         shellp->a_idx = a_idx;
2416         if (bset->shells < shellp) {
2417                 shellp->m_idx = shellp[-1].m_idx + shellp[-1].ncomp;
2418                 shellp->p_idx = shellp[-1].p_idx + shellp[-1].nprim;
2419         } else {
2420                 shellp->m_idx = 0;
2421                 shellp->p_idx = 0;
2422         }
2423         return 0;
2424 }
2425
2426 /*  Add a set of gaussian primitive coefficients (not undoable)  */
2427 int
2428 MoleculeAddGaussianPrimitiveCoefficients(Molecule *mol, Double exponent, Double contraction, Double contraction_sp)
2429 {
2430         BasisSet *bset;
2431         PrimInfo *primp;
2432         if (mol == NULL)
2433                 return -1;  /*  Molecule is empty  */
2434         bset = mol->bset;
2435         if (bset == NULL) {
2436                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2437                 if (bset == NULL)
2438                         return -2;  /*  Low memory  */
2439         }
2440         primp = AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), bset->npriminfos, NULL);
2441         if (primp == NULL)
2442                 return -2;  /*  Low memory  */
2443         primp->A = exponent;
2444         primp->C = contraction;
2445         primp->Csp = contraction_sp;
2446         return 0;
2447 }
2448
2449 /*  Set MO coefficients for idx-th MO  */
2450 int
2451 MoleculeSetMOCoefficients(Molecule *mol, Int idx, Double energy, Int ncomps, Double *coeffs)
2452 {
2453         BasisSet *bset;
2454         int i, n;
2455         if (mol == NULL)
2456                 return -1;  /*  Molecule is empty  */
2457         bset = mol->bset;
2458         if (bset == NULL) {
2459                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2460                 if (bset == NULL)
2461                         return -2;  /*  Low memory  */
2462         }
2463         if (bset->nmos == 0) {
2464                 if (bset->nshells > 0) {
2465                         /*  Shell info is already set: calculate the number of MOs from there  */
2466                         for (i = n = 0; i < bset->nshells; i++)
2467                                 n += bset->shells[i].ncomp;
2468                         bset->ncomps = n;
2469                 } else if (ncomps > 0) {
2470                         bset->ncomps = ncomps;
2471                 }
2472                 if (bset->rflag == 0)
2473                         bset->nmos = bset->ncomps * 2;
2474                 else
2475                         bset->nmos = bset->ncomps;
2476                 if (bset->nmos <= 0)
2477                         return -3;  /*  Bad or inconsistent number of MOs  */
2478                 bset->mo = (Double *)calloc(sizeof(Double), bset->nmos * bset->ncomps);
2479                 bset->moenergies = (Double *)calloc(sizeof(Double), bset->nmos);
2480                 if (bset->mo == NULL || bset->moenergies == NULL) {
2481                         if (bset->mo != NULL)
2482                                 free(bset->mo);
2483                         if (bset->moenergies != NULL)
2484                                 free(bset->moenergies);
2485                         bset->mo = NULL;
2486                         bset->moenergies = NULL;
2487                         bset->nmos = 0;
2488                         return -2;  /*  Low memory  */
2489                 }
2490         }
2491         if (idx < 0 || idx >= bset->nmos)
2492                 return -4;  /*  Bad MO index  */
2493         if (energy != -1000000)
2494                 bset->moenergies[idx] = energy;
2495         if (ncomps < bset->ncomps)
2496                 return -5;  /*  Insufficient number of data provided  */
2497         memmove(bset->mo + (idx * bset->ncomps), coeffs, sizeof(Double) * bset->ncomps);
2498         if (bset->cns != NULL) {
2499                 /*  Clear the cached values  */
2500                 free(bset->cns);
2501                 bset->cns = NULL;
2502                 bset->ncns = 0;
2503         }
2504         return 0;
2505 }
2506
2507 /*  Get MO coefficients for idx-th MO  */
2508 /*  Caution: *ncoeffs and *coeffs should be valid _before_ calling this function, i.e.  */
2509 /*  *ncoeffs = 0 && *coeffs = NULL or *coeffs is a valid memory pointer and *ncoeffs  */
2510 /*  properly designates the memory size as an array of Doubles.  */
2511 int
2512 MoleculeGetMOCoefficients(Molecule *mol, Int idx, Double *energy, Int *ncoeffs, Double **coeffs)
2513 {
2514         BasisSet *bset;
2515         if (mol == NULL)
2516                 return -1;  /*  Molecule is empty  */
2517         bset = mol->bset;
2518         if (bset == NULL || bset->ncomps <= 0)
2519                 return -2;  /*  No basis set info  */
2520         if (idx < 0 || idx >= bset->nmos)
2521                 return -3;  /*  MO index out of range  */
2522         if (energy != NULL)
2523                 *energy = bset->moenergies[idx];
2524         if (ncoeffs != NULL && coeffs != NULL) {
2525                 if (*ncoeffs < bset->ncomps || *coeffs == NULL) {
2526                         if (*coeffs != NULL)
2527                                 free(*coeffs);  /*  Caution: possible cause of SIGBUS if *coeff is not initialized properly */
2528                         *coeffs = (Double *)calloc(sizeof(Double), bset->ncomps);
2529                         *ncoeffs = bset->ncomps;
2530                 }
2531                 memmove(*coeffs, bset->mo + (idx * bset->ncomps), sizeof(Double) * bset->ncomps);
2532         }
2533         return 0;
2534 }
2535
2536 /*  Allocate BasisSet record. rflag: UHF, 0; RHF, 1; ROHF, 2
2537     ne_alpha: number of alpha electrons, ne_beta: number of beta electrons
2538     The natoms and pos are copied from mol.  */
2539 int
2540 MoleculeAllocateBasisSetRecord(Molecule *mol, Int rflag, Int ne_alpha, Int ne_beta)
2541 {
2542         BasisSet *bset;
2543 /*      int i;
2544         Atom *ap; */
2545         if (mol == NULL || mol->natoms == 0)
2546                 return -1;  /*  Molecule is empty  */
2547         bset = mol->bset;
2548         if (bset == NULL) {
2549                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2550                 if (bset == NULL)
2551                         return -2;  /*  Low memory  */
2552         }
2553         bset->natoms_bs = mol->natoms;
2554         bset->ne_alpha = ne_alpha;
2555         bset->ne_beta = ne_beta;
2556         bset->rflag = rflag;
2557         return 0;
2558 }
2559
2560 static void
2561 sSeparateTokens(char *inString, char **outPtr, int size)
2562 {
2563         char *p;
2564         int i;
2565         for (i = 0; i < size; i++) {
2566                 p = strtok((i == 0 ? inString : NULL), " \r\n");
2567                 if (p == NULL)
2568                         break;
2569                 outPtr[i] = p;
2570         }
2571         while (i < size) {
2572                 outPtr[i++] = NULL;
2573         }
2574 }
2575
2576 static int
2577 sReadNumberArray(void *basep, Int *countp, Int size, Int num, FILE *fp, int *lnp)
2578 {
2579         char buf[256];
2580         Int i, n;
2581         *((void **)basep) = NULL;
2582         *countp = 0;
2583         if (AssignArray(basep, countp, size, num - 1, NULL) == NULL)
2584                 return 4;  /*  Out of memory  */
2585         n = 0;
2586         while (ReadLine(buf, sizeof buf, fp, lnp) > 0) {
2587                 char *tokens[16], *p;
2588                 sSeparateTokens(buf, tokens, 16);
2589                 for (i = 0; i < 16; i++) {
2590                         if (tokens[i] == NULL)
2591                                 break;
2592                         if (size == sizeof(Int)) {
2593                                 (*((Int **)basep))[n] = strtol(tokens[i], &p, 0);
2594                         } else if (size == sizeof(Double)) {
2595                                 (*((Double **)basep))[n] = strtod(tokens[i], &p);
2596                         } else return -1;  /*  Internal error  */
2597                         if (tokens[i] == p || *p != 0)
2598                                 return 1;  /*  Non-digit character  */
2599                         if (++n == num) {
2600                                 if (i < 15 && tokens[i + 1] != NULL)
2601                                         return 2;  /*  Too many data  */
2602                                 return 0;  /*  All data are successfully read  */
2603                         }
2604                 }
2605         }
2606         return 3;  /*  Unexpected EOF  */                       
2607 }
2608
2609 static int
2610 sSetupGaussianCoefficients(BasisSet *bset)
2611 {
2612         ShellInfo *sp;
2613         PrimInfo *pp;
2614         int i, j, k;
2615         Double *dp, d;
2616         
2617         /*  Cache the contraction coefficients for efficient calculation  */
2618         /*  Sum up the number of components for all primitives  */
2619         for (i = k = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2620                 sp->cn_idx = k;
2621                 k += sp->nprim * sp->ncomp;
2622         }
2623         /*  Allocate memory for the cached values  */
2624         if (AssignArray(&bset->cns, &bset->ncns, sizeof(Double), k - 1, NULL) == NULL)
2625                 return 1;
2626         /*  Iterate over all primitives  */
2627         dp = bset->cns;
2628         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2629                 for (j = 0, pp = bset->priminfos + sp->p_idx; j < sp->nprim; j++, pp++) {
2630                         switch (sp->sym) {
2631                                 case kGTOType_S:
2632                                         // (8 alpha^3/pi^3)^0.25 exp(-alpha r^2)
2633                                         *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2634                                         break;
2635                                 case kGTOType_P:
2636                                         // (128 alpha^5/pi^3)^0.25 [x|y|z]exp(-alpha r^2)
2637                                         d = pp->C * pow(pp->A, 1.25) * 1.425410941;
2638                                         *dp++ = d;
2639                                         *dp++ = d;
2640                                         *dp++ = d;
2641                                         break;
2642                                 case kGTOType_SP:
2643                                         *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2644                                         d = pp->Csp * pow(pp->A, 1.25) * 1.425410941;
2645                                         *dp++ = d;
2646                                         *dp++ = d;
2647                                         *dp++ = d;
2648                                         break;
2649                                 case kGTOType_D:
2650                                         //  xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2)
2651                                         //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2652                                         d = pp->C * pow(pp->A, 1.75);
2653                                         dp[0] = dp[1] = dp[2] = d * 1.645922781;
2654                                         dp[3] = dp[4] = dp[5] = d * 2.850821881;
2655                                         dp += 6;
2656                                         break;
2657                                 case kGTOType_D5:
2658                                         //  3zz-rr:   (128 alpha^7/9pi^3)^0.25 (3zz-rr)exp(-alpha r^2)
2659                                         //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2660                                         //  xx-yy:    (128 alpha^7/pi^3)^0.25 (xx-yy)exp(-alpha r^2)
2661                                         d = pp->C * pow(pp->A, 1.75);
2662                                         dp[0] = d * 0.822961390;
2663                                         dp[1] = dp[2] = dp[4] = d * 2.850821881;
2664                                         dp[3] = d * 1.425410941;
2665                                         dp += 5;
2666                                         break;
2667                                 /*  TODO: Support F/F7 and G/G9 type orbitals  */
2668                         }
2669                 }
2670         }
2671         return 0;
2672 }
2673
2674 int
2675 MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char **errbuf)
2676 {
2677         FILE *fp;
2678         char buf[1024];
2679         int lineNumber;
2680         int natoms, nbasis, i, j, k, n, mxbond, retval, ncomps, nprims, nelec;
2681         BasisSet *bset;
2682         ShellInfo *sp;
2683         PrimInfo *pp;
2684         Int nary;
2685         Int *iary;
2686         Double *dary;
2687         Atom *ap;
2688 /*      Vector *vp; */
2689         Double w;
2690
2691         *errbuf = NULL;
2692         if (mp == NULL)
2693                 mp = MoleculeNew();
2694         bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2695         if (bset == NULL)
2696                 goto panic;
2697         mp->bset = bset;
2698         fp = fopen(fname, "rb");
2699         if (fp == NULL) {
2700                 s_append_asprintf(errbuf, "Cannot open file");
2701                 return 1;
2702         }
2703         lineNumber = 0;
2704         natoms = nbasis = -1;
2705         mxbond = 0;
2706         ncomps = 0;
2707         nelec = 0;
2708         nprims = 0;
2709         nary = 0;
2710         iary = NULL;
2711         dary = NULL;
2712         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2713                 char *tokens[16];
2714                 char *p = buf + 41;
2715                 if (lineNumber == 2) {
2716                         /*  job info line  */
2717                         if (buf[10] == 'U')
2718                                 bset->rflag = 0;  /*  UHF  */
2719                         else if (buf[11] == 'O')
2720                                 bset->rflag = 2;  /*  ROHF  */
2721                         else bset->rflag = 1; /*  RHF  */
2722                         continue;
2723                 }
2724                 while (p > buf && *p == ' ')
2725                         p--;
2726                 p[1] = 0;
2727                 sSeparateTokens(buf + 42, tokens, 16);
2728                 if (strcmp(buf, "Number of atoms") == 0) {
2729                         if (tokens[1] == NULL || (natoms = atoi(tokens[1])) <= 0) {
2730                                 s_append_asprintf(errbuf, "Line %d: strange number of atoms: %s", lineNumber, tokens[1]);
2731                                 retval = 2;
2732                                 goto cleanup;
2733                         }
2734                         bset->natoms_bs = natoms;
2735                         /*  Allocate atom records (all are empty for now)  */
2736                         AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms - 1, NULL);
2737                         /*  Also allocate atom position array for MO calculations  */
2738                 /*      AssignArray(&bset->pos, &bset->natoms, sizeof(Vector), natoms - 1, NULL); */
2739                         /*  Also allocate nuclear charge array  */
2740                         bset->nuccharges = (Double *)calloc(sizeof(Double), natoms);
2741                 } else if (strcmp(buf, "Number of electrons") == 0) {
2742                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2743                                 s_append_asprintf(errbuf, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
2744                                 retval = 2;
2745                                 goto cleanup;
2746                         }
2747                         nelec = i;
2748                 } else if (strcmp(buf, "Number of alpha electrons") == 0) {
2749                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2750                                 s_append_asprintf(errbuf, "Line %d: strange number of alpha electrons: %s", lineNumber, tokens[1]);
2751                                 retval = 2;
2752                                 goto cleanup;
2753                         }
2754                         bset->ne_alpha = i;
2755                 } else if (strcmp(buf, "Number of beta electrons") == 0) {
2756                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2757                                 s_append_asprintf(errbuf, "Line %d: strange number of beta electrons: %s", lineNumber, tokens[1]);
2758                                 retval = 2;
2759                                 goto cleanup;
2760                         }
2761                         bset->ne_beta = i;
2762                         if (bset->ne_alpha + bset->ne_beta != nelec) {
2763                                 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);
2764                                 retval = 2;
2765                                 goto cleanup;
2766                         }
2767                 } else if (strcmp(buf, "Number of basis functions") == 0) {
2768                         if (tokens[1] == NULL || (nbasis = atoi(tokens[1])) <= 0) {
2769                                 s_append_asprintf(errbuf, "Line %d: strange number of basis functions: %s", lineNumber, tokens[1]);
2770                                 retval = 2;
2771                                 goto cleanup;
2772                         }
2773                 } else if (strcmp(buf, "Atomic numbers") == 0) {
2774                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2775                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2776                                 retval = 2;
2777                                 goto cleanup;
2778                         }
2779                         if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms, fp, &lineNumber) != 0) {
2780                                 s_append_asprintf(errbuf, "Line %d: cannot read atomic numbers", lineNumber);
2781                                 retval = 2;
2782                                 goto cleanup;
2783                         }
2784                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2785                                 ap->atomicNumber = iary[i];
2786                                 bset->nuccharges[i] = iary[i];
2787                                 ElementToString(ap->atomicNumber, ap->element);
2788                                 memmove(ap->aname, ap->element, 4);
2789                                 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
2790                                         ap->weight = w;
2791                         }
2792                         free(iary);
2793                         iary = NULL;
2794                 } else if (strcmp(buf, "Nuclear charges") == 0) {
2795                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2796                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2797                                 retval = 2;
2798                                 goto cleanup;
2799                         }
2800                         if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms, fp, &lineNumber) != 0) {
2801                                 s_append_asprintf(errbuf, "Line %d: cannot read nuclear charges", lineNumber);
2802                                 retval = 2;
2803                                 goto cleanup;
2804                         }
2805                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2806                                 bset->nuccharges[i] = dary[i];
2807                         }
2808                         free(iary);
2809                         iary = NULL;
2810                 } else if (strcmp(buf, "Current cartesian coordinates") == 0) {
2811                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * 3) {
2812                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of cartesian coordinates: %s", lineNumber, tokens[2]);
2813                                 retval = 2;
2814                                 goto cleanup;
2815                         }
2816                         if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms * 3, fp, &lineNumber) != 0) {
2817                                 s_append_asprintf(errbuf, "Line %d: cannot read cartesian coordinates", lineNumber);
2818                                 retval = 2;
2819                                 goto cleanup;
2820                         }
2821                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2822                                 ap->r.x = dary[i * 3] * kBohr2Angstrom;
2823                                 ap->r.y = dary[i * 3 + 1] * kBohr2Angstrom;
2824                                 ap->r.z = dary[i * 3 + 2] * kBohr2Angstrom;
2825                         }
2826                         free(dary);
2827                         dary = NULL;
2828                 } else if (strcmp(buf, "MxBond") == 0) {
2829                         if (tokens[1] == NULL || (mxbond = atoi(tokens[1])) <= 0) {
2830                                 s_append_asprintf(errbuf, "Line %d: strange number of bonds per atom: %s", lineNumber, tokens[1]);
2831                                 retval = 2;
2832                                 goto cleanup;
2833                         }
2834                 } else if (strcmp(buf, "IBond") == 0) {
2835                         Int *bonds;
2836                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * mxbond) {
2837                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of bonds: %s", lineNumber, tokens[2]);
2838                                 retval = 2;
2839                                 goto cleanup;
2840                         }
2841                         if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms * mxbond, fp, &lineNumber) != 0) {
2842                                 s_append_asprintf(errbuf, "Line %d: cannot read bond information", lineNumber);
2843                                 retval = 2;
2844                                 goto cleanup;
2845                         }
2846                         bonds = (Int *)malloc(sizeof(Int) * (mxbond * 2 + 1));
2847                         for (i = 0; i < natoms; i++) {
2848                                 for (j = k = 0; j < mxbond; j++) {
2849                                         n = iary[i * mxbond + j] - 1;
2850                                         if (n > i) {
2851                                                 /*  Connect atom i and atom n  */
2852                                                 bonds[k++] = i;
2853                                                 bonds[k++] = n;
2854                                         }
2855                                 }
2856                                 if (k > 0) {
2857                                         bonds[k] = kInvalidIndex;
2858                                         MoleculeAddBonds(mp, k / 2, bonds, NULL, 1);
2859                                 }
2860                         }
2861                         free(iary);
2862                         free(bonds);
2863                         iary = NULL;
2864                 } else if (strcmp(buf, "Shell types") == 0) {
2865                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0) {
2866                                 s_append_asprintf(errbuf, "Line %d: wrong number of shell types: %s", lineNumber, tokens[2]);
2867                                 retval = 2;
2868                                 goto cleanup;
2869                         }
2870                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2871                                 s_append_asprintf(errbuf, "Line %d: cannot read shell types", lineNumber);
2872                                 retval = 2;
2873                                 goto cleanup;
2874                         }
2875                         /*  Allocate ShellInfo table and store shell type information  */
2876                         AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), nary - 1, NULL);
2877                         for (i = n = 0, sp = bset->shells; i < nary; i++, sp++) {
2878                                 switch (iary[i]) {
2879                                         case 0:  sp->sym = kGTOType_S;  sp->ncomp = 1; break;
2880                                         case 1:  sp->sym = kGTOType_P;  sp->ncomp = 3; break;
2881                                         case -1: sp->sym = kGTOType_SP; sp->ncomp = 4; break;
2882                                         case 2:  sp->sym = kGTOType_D;  sp->ncomp = 6; break;
2883                                         case -2: sp->sym = kGTOType_D5; sp->ncomp = 5; break;
2884                                         case 3:  sp->sym = kGTOType_F;  sp->ncomp = 10; break;
2885                                         case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break;
2886                                         case 4:  sp->sym = kGTOType_G;  sp->ncomp = 15; break;
2887                                         case -4: sp->sym = kGTOType_G9; sp->ncomp = 9; break;
2888                                         default:
2889                                                 s_append_asprintf(errbuf, "Line %d: unsupported shell type %d", lineNumber, iary[i]);
2890                                                 retval = 2;
2891                                                 goto cleanup;
2892                                 }
2893                                 sp->m_idx = n;
2894                                 n += sp->ncomp;
2895                         }
2896                         bset->ncomps = ncomps = n;
2897                         free(iary);
2898                         iary = NULL;
2899                 } else if (strcmp(buf, "Number of primitives per shell") == 0) {
2900                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2901                                 s_append_asprintf(errbuf, "Line %d: wrong size of the primitive table: %s", lineNumber, tokens[2]);
2902                                 retval = 2;
2903                                 goto cleanup;
2904                         }
2905                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2906                                 s_append_asprintf(errbuf, "Line %d: cannot read primitive table", lineNumber);
2907                                 retval = 2;
2908                                 goto cleanup;
2909                         }
2910                         for (i = n = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2911                                 sp->nprim = iary[i];
2912                                 sp->p_idx = n;
2913                                 n += sp->nprim;
2914                         }
2915                         nprims = n;
2916                         free(iary);
2917                         iary = NULL;
2918                 } else if (strcmp(buf, "Shell to atom map") == 0) {
2919                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2920                                 s_append_asprintf(errbuf, "Line %d: wrong size of the shell-to-atom map: %s", lineNumber, tokens[2]);
2921                                 retval = 2;
2922                                 goto cleanup;
2923                         }
2924                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2925                                 s_append_asprintf(errbuf, "Line %d: cannot read shell-to-atom table", lineNumber);
2926                                 retval = 2;
2927                                 goto cleanup;
2928                         }
2929                         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2930                                 sp->a_idx = iary[i] - 1;
2931                         }
2932                         free(iary);
2933                         iary = NULL;
2934                 } else if (strcmp(buf, "Primitive exponents") == 0) {
2935                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nprims) {
2936                                 s_append_asprintf(errbuf, "Line %d: wrong number of primitive exponents: %s", lineNumber, tokens[2]);
2937                                 retval = 2;
2938                                 goto cleanup;
2939                         }
2940                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2941                                 s_append_asprintf(errbuf, "Line %d: cannot read primitive exponents", lineNumber);
2942                                 retval = 2;
2943                                 goto cleanup;
2944                         }
2945                         /*  Allocate PrimInfo table  */
2946                         AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), nprims - 1, NULL);
2947                         for (i = 0, pp = bset->priminfos; i < nprims; i++, pp++) {
2948                                 pp->A = dary[i];
2949                         }
2950                         free(dary);
2951                         dary = NULL;
2952                 } else if (strcmp(buf, "Contraction coefficients") == 0) {
2953                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2954                                 s_append_asprintf(errbuf, "Line %d: wrong number of contraction coefficients: %s", lineNumber, tokens[2]);
2955                                 retval = 2;
2956                                 goto cleanup;
2957                         }
2958                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2959                                 s_append_asprintf(errbuf, "Line %d: cannot read contraction coefficients", lineNumber);
2960                                 retval = 2;
2961                                 goto cleanup;
2962                         }
2963                         for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2964                                 pp->C = dary[i];
2965                         }
2966                         free(dary);
2967                         dary = NULL;
2968                 } else if (strcmp(buf, "P(S=P) Contraction coefficients") == 0) {
2969                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2970                                 s_append_asprintf(errbuf, "Line %d: wrong number of P(S=P) contraction coefficients: %s", lineNumber, tokens[2]);
2971                                 retval = 2;
2972                                 goto cleanup;
2973                         }
2974                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2975                                 s_append_asprintf(errbuf, "Line %d: cannot read P(S=P) contraction coefficients", lineNumber);
2976                                 retval = 2;
2977                                 goto cleanup;
2978                         }
2979                         for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2980                                 pp->Csp = dary[i];
2981                         }
2982                         free(dary);
2983                         dary = NULL;
2984                 } else if (strcmp(buf, "Alpha Orbital Energies") == 0) {
2985                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2986                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha orbitals: %s", lineNumber, tokens[2]);
2987                                 retval = 2;
2988                                 goto cleanup;
2989                         }
2990                         if (sReadNumberArray(&bset->moenergies, &bset->nmos, sizeof(Double), i, fp, &lineNumber) != 0) {
2991                                 s_append_asprintf(errbuf, "Line %d: cannot read alpha orbital energies", lineNumber);
2992                                 retval = 2;
2993                                 goto cleanup;
2994                         }
2995                 } else if (strcmp(buf, "Alpha MO coefficients") == 0) {
2996                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2997                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha MO coefficients: %s", lineNumber, tokens[2]);
2998                                 retval = 2;
2999                                 goto cleanup;
3000                         }
3001                         if (sReadNumberArray(&bset->mo, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3002                                 s_append_asprintf(errbuf, "Line %d: cannot read MO coefficients", lineNumber);
3003                                 retval = 2;
3004                                 goto cleanup;
3005                         }
3006                 } else if (strcmp(buf, "Beta Orbital Energies") == 0) {
3007                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
3008                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta orbitals: %s", lineNumber, tokens[2]);
3009                                 retval = 2;
3010                                 goto cleanup;
3011                         }
3012                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3013                                 s_append_asprintf(errbuf, "Line %d: cannot read beta orbital energies", lineNumber);
3014                                 retval = 2;
3015                                 goto cleanup;
3016                         }
3017                         bset->moenergies = (Double *)realloc(bset->moenergies, sizeof(Double) * 2 * ncomps);
3018                         bset->nmos = ncomps * 2;
3019                         bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);
3020                         memmove(bset->moenergies + ncomps, dary, sizeof(Double) * ncomps);
3021                         memset(bset->mo + ncomps * ncomps, 0, sizeof(Double) * ncomps * ncomps);
3022                         free(dary);
3023                         dary = NULL;
3024                 } else if (strcmp(buf, "Beta MO coefficients") == 0) {
3025                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
3026                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta MO coefficients: %s", lineNumber, tokens[2]);
3027                                 retval = 2;
3028                                 goto cleanup;
3029                         }
3030                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3031                                 s_append_asprintf(errbuf, "Line %d: cannot read alpha MO coefficients", lineNumber);
3032                                 retval = 2;
3033                                 goto cleanup;
3034                         }
3035                         bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);  /*  Should be unnecessary, just in case  */
3036                         memmove(bset->mo + ncomps * ncomps, dary, sizeof(Double) * ncomps * ncomps);
3037                         free(dary);
3038                         dary = NULL;
3039                 } else if (strcmp(buf, "Total SCF Density") == 0) {
3040                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * (ncomps + 1) / 2) {
3041                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of SCF densities: %s", lineNumber, tokens[2]);
3042                                 retval = 2;
3043                                 goto cleanup;
3044                         }
3045                         if (sReadNumberArray(&bset->scfdensities, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3046                                 s_append_asprintf(errbuf, "Line %d: cannot read SCF densities", lineNumber);
3047                                 retval = 2;
3048                                 goto cleanup;
3049                         }
3050                 }
3051         }
3052         if (mp->natoms == 0) {
3053                 s_append_asprintf(errbuf, "Atom information is missing");
3054                 retval = 2;
3055                 goto cleanup;
3056         }
3057         if (bset->shells == NULL || bset->priminfos == NULL) {
3058                 s_append_asprintf(errbuf, "Gaussian primitive information is missing");
3059                 retval = 2;
3060                 goto cleanup;
3061         }
3062         if (bset->mo == NULL) {
3063                 s_append_asprintf(errbuf, "MO coefficients were not found");
3064                 retval = 2;
3065                 goto cleanup;
3066         }
3067         if (sSetupGaussianCoefficients(bset) != 0) {
3068                 s_append_asprintf(errbuf, "Internal error during setup MO calculation");
3069                 retval = 2;
3070                 goto cleanup;
3071         }
3072         mp->nframes = -1;
3073         retval = 0;
3074 cleanup:
3075         fclose(fp);
3076         if (iary != NULL)
3077                 free(iary);
3078         if (dary != NULL)
3079                 free(dary);
3080         if (retval != 0) {
3081                 if (mp->bset != NULL) {
3082                         BasisSetRelease(mp->bset);
3083                         mp->bset = NULL;
3084                 }
3085         }
3086         return retval;
3087 panic:
3088         Panic("low memory while reading fchk file %s", fname);
3089         return -1; /* not reached */    
3090 }
3091
3092 int
3093 MoleculeLoadGamessDatFile(Molecule *mol, const char *fname, char **errbuf)
3094 {
3095         FILE *fp;
3096         int newmol = 0;
3097         char buf[1024];
3098         int lineNumber, i, j, k, len, natoms = 0;
3099         int nframes = 0;
3100         int n1;
3101         int retval = 0;
3102         int ival[8];
3103         double dval[8];
3104         char sval[16];
3105         Vector *vbuf = NULL;
3106         IntGroup *ig;
3107         int optimizing = 0, status = 0;
3108         
3109         *errbuf = NULL;
3110         if (mol == NULL) {
3111                 mol = MoleculeNew();
3112         }
3113         if (mol->natoms == 0)
3114                 newmol = 1;
3115
3116         fp = fopen(fname, "rb");
3117         if (fp == NULL) {
3118                 s_append_asprintf(errbuf, "Cannot open file");
3119                 return 1;
3120         }
3121         
3122         /*  ESP is cleared (not undoable!)  */
3123         if (mol->elpots != NULL) {
3124                 free(mol->elpots);
3125                 mol->elpots = NULL;
3126                 mol->nelpots = 0;
3127         }
3128         
3129         lineNumber = 0;
3130         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3131         redo:
3132                 n1 = 0;
3133                 if (strncmp(buf, " $DATA", 6) == 0) {
3134                         /*  Initial geometry  */
3135                         if (!newmol) {
3136                                 vbuf = (Vector *)calloc(sizeof(Vector), mol->natoms);
3137                         }
3138                         i = 0;
3139                         ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Title  */
3140                         ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Symmetry  */
3141                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3142                                 if (strncmp(buf, " $END", 5) == 0)
3143                                         break;
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 $DATA section", lineNumber);
3146                                         retval = 2;
3147                                         goto exit_loop;
3148                                 }
3149                                 if (newmol) {
3150                                         Atom a;
3151                                         memset(&a, 0, sizeof(a));
3152                                         strncpy(a.aname, sval, 4);
3153                                         a.r.x = dval[1];
3154                                         a.r.y = dval[2];
3155                                         a.r.z = dval[3];
3156                                         a.atomicNumber = (Int)dval[0];
3157                                         strncpy(a.element, ElementToString(a.atomicNumber, sval), 3);
3158                                         a.type = AtomTypeEncodeToUInt(a.element);
3159                                         a.weight = WeightForAtomicNumber(a.atomicNumber);
3160                                         MoleculeCreateAnAtom(mol, &a, mol->natoms);
3161                                 } else {
3162                                         Atom *ap;
3163                                         if (i >= mol->natoms) {
3164                                                 s_append_asprintf(errbuf, "Line %d: too many atoms", lineNumber);
3165                                                 retval = 3;
3166                                                 goto exit_loop;
3167                                         }
3168                                         if ((ap = ATOM_AT_INDEX(mol->atoms, i))->atomicNumber != dval[0]) {
3169                                                 s_append_asprintf(errbuf, "Line %d: atomic number does not match", lineNumber);
3170                                                 retval = 4;
3171                                                 goto exit_loop;
3172                                         }
3173                                         vbuf[i].x = dval[1];
3174                                         vbuf[i].y = dval[2];
3175                                         vbuf[i].z = dval[3];
3176                                 }
3177                                 /*  Skip until a blank line is found  */
3178                                 /*  2013.6.11. Line including "PM3" is also recognized as the end of atom  */
3179                                 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3180                                         for (j = 0; buf[j] == ' '; j++);
3181                                         if (buf[j] == '\n' || strncmp(buf + j, "PM3", 3) == 0)
3182                                                 break;
3183                                 }
3184                                 i++;
3185                         }
3186                         natoms = i;
3187                         if (!newmol) {
3188                                 /*  Set atom positions  */
3189                                 IntGroup *ig;
3190                                 if (natoms < mol->natoms) {
3191                                         s_append_asprintf(errbuf, "Line %d: too few atoms", lineNumber);
3192                                         retval = 5;
3193                                         goto exit_loop;
3194                                 }
3195                                 ig = IntGroupNewWithPoints(0, natoms, -1);
3196                                 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, natoms, vbuf);
3197                                 IntGroupRelease(ig);
3198                         }
3199                         if (vbuf == NULL)
3200                                 vbuf = (Vector *)calloc(sizeof(Vector), natoms);
3201                         nframes = MoleculeGetNumberOfFrames(mol);
3202                         if (status < 0)
3203                                 break;
3204                         continue;
3205                 } else if (strstr(buf, "DATA FROM NSERCH") != NULL || (strstr(buf, "RESULTS FROM SUCCESSFUL") != NULL && (n1 = 1))) {
3206                         /*  Skip until the separator line is read (three or four lines)  */
3207                         i = 0;
3208                         do {
3209                                 if (i++ >= 4) {
3210                                         s_append_asprintf(errbuf, "Line %d: the separator line at the top of the coordinates is not found: bad format?", lineNumber);
3211                                         retval = 6;
3212                                         goto exit_loop;
3213                                 }
3214                                 ReadLine(buf, sizeof buf, fp, &lineNumber);
3215                         } while (strstr(buf, "----------------------------") == NULL);
3216                         for (i = 0; i < natoms; i++) {
3217                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3218                                         s_append_asprintf(errbuf, "Unexpected end of file in reading NSERCH data");
3219                                         retval = 6;
3220                                         goto exit_loop;
3221                                 }
3222                                 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3223                                         s_append_asprintf(errbuf, "Line %d: bad format in NSERCH coordinate data", lineNumber);
3224                                         retval = 6;
3225                                         goto exit_loop;
3226                                 }
3227                                 vbuf[i].x = dval[1];
3228                                 vbuf[i].y = dval[2];
3229                                 vbuf[i].z = dval[3];
3230                         }
3231                         ig = IntGroupNewWithPoints(nframes, 1, -1);
3232                         MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, natoms, vbuf, 0, NULL);
3233                         IntGroupRelease(ig);
3234                         nframes++;
3235                         if (n1 == 0)
3236                                 optimizing = 1;  /*  Flag to skip reading the VEC group  */
3237                         else
3238                                 optimizing = 0;
3239                         continue;
3240                 } else if (strstr(buf, "E(UHF)") != NULL || (strstr(buf, "E(RHF)") != NULL && (n1 = 1)) || (strstr(buf, "E(ROHF)") != NULL && (n1 = 2))) {
3241                         if (mol->bset == NULL) {
3242                                 i = MoleculeAllocateBasisSetRecord(mol, n1, 0, 0);
3243                                 if (i != 0) {
3244                                         s_append_asprintf(errbuf, "Line %d: cannot allocate basis set internal buffer", lineNumber);
3245                                         retval = 8;
3246                                         goto exit_loop;
3247                                 }
3248                         }
3249                 } else if (strncmp(buf, " $VEC", 5) == 0) {
3250                         Double *coeffs;
3251                         /*  Read the vec group  */
3252                         if (mol->bset == NULL || mol->bset->ncomps == 0)
3253                                 continue;  /*  Just ignore  */
3254                         if (optimizing)
3255                                 continue;  /*  Ignore VEC group during optimization  */
3256                         coeffs = (Double *)calloc(sizeof(Double), mol->bset->ncomps);
3257                         if (coeffs == NULL) {
3258                                 s_append_asprintf(errbuf, "Line %d: low memory during $VEC", lineNumber);
3259                                 retval = 9;
3260                                 goto exit_loop;
3261                         }
3262                         i = k = 0;
3263                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3264                                 len = strlen(buf);
3265                                 if (strncmp(buf, " $END", 5) == 0)
3266                                         break;
3267                                 while ((j = 5 + (k % 5) * 15) <= len && buf[j] != 0 && buf[j] != '\n') {
3268                                         strncpy(sval, buf + j, 15);
3269                                         sval[15] = 0;
3270                                         coeffs[k] = strtod(sval, NULL);
3271                                         k++;
3272                                         if ((k % 5) == 0)
3273                                                 break;
3274                                 }
3275                                 if (k < mol->bset->ncomps)
3276                                         continue;
3277                                 j = MoleculeSetMOCoefficients(mol, i, -1000000, k, coeffs);
3278                                 if (j != 0) {
3279                                         s_append_asprintf(errbuf, "Line %d: cannot set coefficients for MO %d", lineNumber, i + 1);
3280                                         free(coeffs);
3281                                         retval = 10;
3282                                         goto exit_loop;
3283                                 }
3284                                 i++;
3285                                 k = 0;
3286                         }
3287                         if (status < 0)
3288                                 break;
3289                         continue;
3290                 } else if ((strstr(buf, "ELECTRIC POTENTIAL") != NULL || strstr(buf, "ELECTROSTATIC POTENTIAL") != NULL) && strstr(buf, "ELPOTT") != NULL) {
3291                         i = 0;
3292                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3293                                 Elpot *ep;
3294                                 if (strstr(buf, "TOTAL NUMBER OF GRID POINTS") != NULL)
3295                                         continue;
3296                                 if (sscanf(buf, "%d %lf %lf %lf %lf", &ival[0], &dval[0], &dval[1], &dval[2], &dval[3]) < 5)
3297                                         break;
3298                                 ep = AssignArray(&mol->elpots, &mol->nelpots, sizeof(Elpot), i, NULL);
3299                                 ep->pos.x = dval[0];
3300                                 ep->pos.y = dval[1];
3301                                 ep->pos.z = dval[2];
3302                                 ep->esp = dval[3];
3303                                 i++;
3304                         }
3305                         if (status > 0)
3306                                 goto redo;  /*  This section has no end line, so the last line should be processed again  */
3307                         else break;    /*  End of file encountered or interrupted */
3308                 }  /*  TODO: read MOLPLT info if present  */
3309         }
3310         if (status < 0) {
3311                 s_append_asprintf(errbuf, "User interrupt at line %d", lineNumber);
3312                 retval = 11;
3313         }
3314 exit_loop:
3315         if (vbuf != NULL)
3316                 free(vbuf);
3317         if (mol->natoms > 0)
3318                 retval = 0;  /*  Return the partially constructed molecule  */
3319         if (newmol && mol->nbonds == 0) {
3320                 /*  Guess bonds  */
3321                 Int nbonds, *bonds;
3322                 MoleculeGuessBonds(mol, 0.0, &nbonds, &bonds);
3323                 if (nbonds > 0) {
3324                         MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
3325                         free(bonds);
3326                 }
3327         }
3328         return 0;
3329 }
3330
3331 int
3332 MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
3333 {
3334         int retval;
3335         if (ftype == NULL || *ftype == 0) {
3336                 const char *cp;
3337                 cp = strrchr(fname, '.');
3338                 if (cp != NULL)
3339                         ftype = cp + 1;
3340                 else {
3341                         cp = guessMoleculeType(fname);
3342                         if (strcmp(cp, "???") != 0)
3343                                 ftype = cp;
3344                 }
3345         }
3346         if (strcasecmp(ftype, "pdb") == 0) {
3347                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3348         }
3349         if (retval != 0) {
3350                 /*  Try all formats once again  */
3351                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3352         }
3353         return retval;
3354 }
3355
3356 int
3357 MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char **errbuf)
3358 {
3359         FILE *fp;
3360         char buf[1024];
3361         char *p;
3362         int lineNumber;
3363         int i, j, new_unit, retval;
3364         Atom *ap;
3365         IntGroup *ig;
3366         Vector *vp = NULL;
3367         Int ibuf[12];
3368         Int entries = 0;
3369         retval = 0;
3370         *errbuf = NULL;
3371         fp = fopen(fname, "rb");
3372         if (fp == NULL) {
3373                 s_append_asprintf(errbuf, "Cannot open file");
3374                 return -1;
3375         }
3376 /*      flockfile(fp); */
3377         if (mp->natoms == 0)
3378                 new_unit = 1;
3379         else {
3380                 /*  Allocate buffer for undo-capable modification  */
3381                 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
3382                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3383                         /*  Retain current position if the atom info is missing in the input file  */
3384                         vp[i] = ap->r;
3385                 }
3386                 new_unit = 0;
3387         }
3388         lineNumber = 0;
3389         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3390                 if (strncmp(buf, "END", 3) == 0)
3391                         break;
3392                 if (strncmp(buf, "HETATM", 6) == 0 || strncmp(buf, "ATOM", 4) == 0) {
3393                         struct {
3394                                 Int serial, intCharge, resSeq;
3395                                 Vector r;
3396                                 Double occ, temp;
3397                                 char segName[5], resName[4], atomName[5], resSeqStr[5], atomType[3], element[3], occStr[6];
3398                         } w;
3399                         memset(&w, 0, sizeof(w));
3400                         ReadFormat(buf, "x6 I5 x1 S4 x1 S3 x1 x1 S4 x1 x3 F8 F8 F8 S6 F6 x6 S4 S2 I2",
3401                                 &w.serial, w.atomName, w.resName, w.resSeqStr, &w.r.x, &w.r.y, &w.r.z,
3402                                 w.occStr, &w.temp, w.segName, w.element, &w.intCharge);
3403                         if (w.atomName[0] == 0) {
3404                                 continue;  /*  Atom name is empty  */
3405                         }
3406                         /*  A workaround for residue number >= 10000 (XPLOR style)  */
3407                         if (w.resSeqStr[0] >= 'A' && w.resSeqStr[0] <= 'Z') {
3408                                 w.resSeq = (w.resSeqStr[0] - 'A' + 10) * 1000 + atoi(w.resSeqStr + 1);
3409                         } else {
3410                                 w.resSeq = atoi(w.resSeqStr);
3411                         }
3412                         if (w.element[0] == 0) {
3413                                 /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
3414                                 for (p = w.atomName; *p != 0; p++) {
3415                                         if (isalpha(*p) && *p != '_') {
3416                                                 w.element[0] = toupper(*p);
3417                                                 if (isalpha(p[1]) && p[1] != '_') {
3418                                                         w.element[1] = toupper(p[1]);
3419                                                         w.element[2] = 0;
3420                                                 } else {
3421                                                         w.element[1] = 0;
3422                                                 }
3423                                                 break;
3424                                         }
3425                                 }
3426                         }
3427                         if (w.occStr[0] == 0)
3428                                 w.occ = 1.0;
3429                         else
3430                                 w.occ = atof(w.occStr);
3431                         if (w.serial <= 0) {
3432                                 s_append_asprintf(errbuf, "line %d: non-positive atom number %d", lineNumber, w.serial);
3433                                 retval = 1;
3434                                 goto abort;
3435                         }
3436                         w.serial--;  /*  The internal atom number is 0-based  */
3437                         if (w.serial >= mp->natoms) {
3438                                 if (new_unit) {
3439                                         /*  Create a new atom entry  */
3440                                         ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, w.serial, NULL);
3441                                 } else {
3442                                         s_append_asprintf(errbuf, "line %d: the atom number %d does not exist in the structure file", lineNumber, w.serial+1);
3443                                         retval = 1;
3444                                         goto abort;
3445                                 }
3446                         }
3447                         if (new_unit) {
3448                                 ap = ATOM_AT_INDEX(mp->atoms, w.serial);
3449                                 ap->r = w.r;
3450                                 ap->occupancy = w.occ;
3451                                 ap->tempFactor = w.temp;
3452                                 if (w.segName[0] == 0)
3453                                         strncpy(w.segName, "MAIN", 4);
3454                                 strncpy(ap->segName, w.segName, 4);
3455                                 ap->resSeq = w.resSeq;
3456                                 strncpy(ap->resName, w.resName, 4);
3457                                 strncpy(ap->aname, w.atomName, 4);
3458                                 strncpy(ap->element, w.element, 2);
3459                                 ap->element[2] = 0;
3460                                 ap->atomicNumber = ElementToInt(ap->element);
3461                                 ap->type = AtomTypeEncodeToUInt(ap->element);
3462                                 ap->weight = WeightForAtomicNumber(ap->atomicNumber);
3463                                 ap->intCharge = w.intCharge;
3464                                 if (ap->resSeq > 0) {
3465                                         if (ap->resSeq < mp->nresidues) {
3466                                                 /*  Update the resName according to residues[]  */
3467                                                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
3468                                         } else {
3469                                                 /*  Register the resName to residues[]  */
3470                                                 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, w.resName);
3471                                         }
3472                                 } else {
3473                                         ap->resSeq = 0;
3474                                         strcpy(ap->resName, "XXX");
3475                                         if (mp->nresidues == 0)
3476                                                 AssignArray(&mp->residues, &mp->nresidues, 4, 0, ap->resName);
3477                                 }
3478                                 i = ElementToInt(ap->element);
3479                                 if (i >= 0)
3480                                         ap->weight = gElementParameters[i].weight;
3481                         } else {
3482                                 /*  Not a new unit: only the atom position is updated  */
3483                                 vp[w.serial] = w.r;
3484                         }
3485                         entries++;
3486                 } else if (strncmp(buf, "CONECT", 6) == 0 && new_unit) {
3487                         i = ReadFormat(buf, "x6 I5I5I5I5I5I5I5I5I5I5I5I5",
3488                                 ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
3489                                 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7,
3490                                 ibuf + 8, ibuf + 9, ibuf + 10, ibuf + 11);
3491                         if (i >= 2) {
3492                                 Int bbuf[25];
3493                                 int bi;
3494                                 for (j = 0; j < i; j++) {
3495                                         if (ibuf[j] < 0 || ibuf[j] > mp->natoms) {
3496                                                 s_append_asprintf(errbuf, "line %d: The CONECT record contains non-existent atom %d", lineNumber, ibuf[j]);
3497                                                 retval = 1;
3498                                                 goto abort;
3499                                         } else if (ibuf[j] == 0)
3500                                                 break;
3501                                 }
3502                                 i = j;
3503                                 if (i < 2)
3504                                         continue;
3505                                 for (j = 1, bi = 0; j < i; j++) {
3506                                         if (ibuf[0] < ibuf[j]) {
3507                                                 if (MoleculeLookupBond(mp, ibuf[0], ibuf[j]) >= 0) {
3508                                                         s_append_asprintf(errbuf, "line %d: warning: duplicate bond %d-%d\n", lineNumber, ibuf[0], ibuf[j]);
3509                                                 } else {
3510                                                         bbuf[bi * 2] = ibuf[0] - 1;
3511                                                         bbuf[bi * 2 + 1] = ibuf[j] - 1;
3512                                                         bi++;
3513                                                 }
3514                                         }
3515                                 }
3516                                 if (bi == 0)
3517                                         continue;
3518                                 bbuf[bi * 2] = -1;
3519                                 retval = MoleculeAddBonds(mp, bi, bbuf, NULL, 1);
3520                                 if (retval < 0) {
3521                                         s_append_asprintf(errbuf, "line %d: bad bond specification", lineNumber);
3522                                         retval = 1;
3523                                         goto abort;
3524                                 }
3525                         }
3526                 }
3527         }
3528 /*      funlockfile(fp); */
3529         fclose(fp);
3530         if (new_unit) {
3531                 /*  Renumber atoms if some atom number is unoccupied  */
3532                 int *old2new, oldidx, newidx;
3533                 old2new = (int *)calloc(sizeof(int), mp->natoms);
3534                 if (old2new == NULL) {
3535                         s_append_asprintf(errbuf, "Out of memory");
3536                         retval = 1;
3537                         goto abort;
3538                 }
3539                 for (oldidx = newidx = 0; oldidx < mp->natoms; oldidx++) {
3540                         ap = ATOM_AT_INDEX(mp->atoms, oldidx);
3541                         if (ap->aname[0] != 0) {
3542                                 old2new[oldidx] = newidx;
3543                                 if (oldidx > newidx)
3544                                         memmove(ATOM_AT_INDEX(mp->atoms, newidx), ap, gSizeOfAtomRecord);
3545                                 newidx++;
3546                         }
3547                 }
3548                 mp->natoms = newidx;
3549                 if (oldidx > newidx) {
3550                         /*  Renumber the connects and bonds  */
3551                         Int *cp;
3552                         for (i = 0; i < mp->natoms; i++) {
3553                                 ap = ATOM_AT_INDEX(mp->atoms, i);
3554                                 cp = AtomConnectData(&ap->connect);
3555                                 for (j = 0; j < ap->connect.count; j++) {
3556                                         cp[j] = old2new[cp[j]];
3557                                 }
3558                         }
3559                         for (i = 0; i < mp->nbonds * 2; i++) {
3560                                 mp->bonds[i] = old2new[mp->bonds[i]];
3561                         }
3562                 }
3563                 retval = MoleculeRebuildTablesFromConnects(mp);
3564                 if (retval != 0) {
3565                         /*  This error may not happen  */
3566                         s_append_asprintf(errbuf, "Cannot build angle/dihedral/improper tables");
3567                         retval = 1;
3568                         goto abort;
3569                 }
3570                 /*  Undo action: delete all atoms  */
3571                 {
3572                         MolAction *act;
3573                         ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3574                         act = MolActionNew(gMolActionUnmergeMolecule, ig);
3575                         act->frame = mp->cframe;
3576                         MolActionCallback_registerUndo(mp, act);
3577                         MolActionRelease(act);
3578                         IntGroupRelease(ig);
3579                 }
3580         } else {
3581                 /*  Set the new atom positions  */
3582                 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3583                 MolActionCreateAndPerform(mp, gMolActionSetAtomPositions, ig, mp->natoms, vp);
3584                 IntGroupRelease(ig);
3585                 free(vp);
3586                 vp = NULL;
3587         }
3588         mp->nframes = -1;  /*  Should be recalculated later  */
3589         if (entries == 0)
3590                 return 1;  /*  No atoms  */
3591         return 0;
3592         abort:
3593         if (fp != NULL) {
3594         /*      funlockfile(fp); */
3595                 fclose(fp);
3596         }
3597         if (vp != NULL)
3598                 free(vp);
3599         if (entries == 0)
3600                 return 1;  /*  Maybe different format?  */
3601         return retval;
3602 }
3603
3604 int
3605 MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char **errbuf)
3606 {
3607         DcdRecord dcd;
3608         SFloat32 *xp, *yp, *zp;
3609         Vector *vp, *cp;
3610         IntGroup *ig;
3611         int n, errcount = 0;
3612         *errbuf = NULL;
3613         if (mp == NULL || mp->natoms == 0) {
3614                 s_append_asprintf(errbuf, "Molecule is empty");
3615                 return 1;
3616         }
3617         n = DcdOpen(fname, &dcd);
3618         if (n != 0) {
3619                 switch (n) {
3620                         case -2: s_append_asprintf(errbuf, "Cannot open file"); break;
3621                         case 1:  s_append_asprintf(errbuf, "Premature EOF encountered"); break;
3622                         case 2:  s_append_asprintf(errbuf, "Bad block length of the first section"); break;
3623                         case 3:  s_append_asprintf(errbuf, "\"CORD\" signature is missing"); break;
3624                         case 4:  s_append_asprintf(errbuf, "Bad termination of the first section"); break;
3625                         case 5:  s_append_asprintf(errbuf, "The title section is not correct"); break;
3626                         case 6:  s_append_asprintf(errbuf, "The atom number section is not correct"); break;
3627                         default: s_append_asprintf(errbuf, "Read error in dcd file"); break;
3628                 }
3629                 errcount++;
3630         } else {
3631                 if (dcd.natoms == 0) {
3632                         s_append_asprintf(errbuf, "No atoms were found in the dcd file");
3633                         errcount++;
3634                 } else if (dcd.nframes == 0) {
3635                         s_append_asprintf(errbuf, "No frames were found in the dcd file");
3636                         errcount++;
3637                 }
3638         }
3639         if (errcount > 0) {
3640                 if (n == 0)
3641                         DcdClose(&dcd);
3642                 return 1;
3643         }
3644
3645         vp = (Vector *)calloc(sizeof(Vector), mp->natoms * dcd.nframes);
3646         if (dcd.nextra)
3647                 cp = (Vector *)calloc(sizeof(Vector), dcd.nframes * 4);
3648         else cp = NULL;
3649         xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3650         yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3651         zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3652         ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mp), dcd.nframes, -1);
3653         if (vp == NULL || xp == NULL || yp == NULL || zp == NULL || ig == NULL) {
3654                 s_append_asprintf(errbuf, "Cannot allocate memory");
3655                 if (vp) free(vp);
3656                 if (cp) free(cp);
3657                 if (xp) free(xp);
3658                 if (yp) free(yp);
3659                 if (zp) free(zp);
3660                 if (ig) IntGroupRelease(ig);
3661                 return 1;
3662         }
3663         for (n = 0; n < dcd.nframes; n++) {
3664                 int i;
3665                 Vector *vpp;
3666                 SFloat32 dcdcell[6];
3667                 if (DcdReadFrame(&dcd, n, xp, yp, zp, dcdcell)) {
3668                         s_append_asprintf(errbuf, "Read error in dcd file");
3669                         goto exit;
3670                 }
3671                 for (i = 0, vpp = &vp[n * mp->natoms]; i < dcd.natoms && i < mp->natoms; i++, vpp++) {
3672                         vpp->x = xp[i];
3673                         vpp->y = yp[i];
3674                         vpp->z = zp[i];
3675                 }
3676                 if (cp != NULL) {
3677                         Double sing;
3678                         vpp = &cp[n * 4];
3679                         /*  dcdcell = {a, gamma, b, beta, alpha, c} */
3680                         /*  angles are described either in cosines (Charmm and NAMD > 2.5) or degrees (NAMD 2.5)  */
3681                         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) {
3682                                 dcdcell[4] = cos(dcdcell[4] * kDeg2Rad);  /*  cos(alpha)  */
3683                                 dcdcell[3] = cos(dcdcell[3] * kDeg2Rad);  /*  cos(beta)  */
3684                                 dcdcell[1] = cos(dcdcell[1] * kDeg2Rad);  /*  cos(gamma)  */
3685                         }
3686                         /*  a axis lies along the cartesian x axis  */
3687                         sing = sqrt(1 - dcdcell[1] * dcdcell[1]);
3688                         vpp[0].x = dcdcell[0];
3689                         vpp[0].y = 0;
3690                         vpp[0].z = 0;
3691                         vpp[1].x = dcdcell[2] * dcdcell[1];
3692                         vpp[1].y = dcdcell[2] * sing;
3693                         vpp[1].z = 0;
3694                         vpp[2].x = dcdcell[5] * dcdcell[3];
3695                         vpp[2].y = dcdcell[5] * (dcdcell[4] - dcdcell[3] * dcdcell[1]) / sing;
3696                         vpp[2].z = sqrt(dcdcell[5] * dcdcell[5] - vpp[2].x * vpp[2].x - vpp[2].y * vpp[2].y);
3697                         vpp[3].x = vpp[3].y = vpp[3].z = 0.0;
3698                         if (mp->cell == NULL) {
3699                                 /*  Create periodicity if not present  */
3700                                 MolActionCreateAndPerform(mp, gMolActionSetBox, &vpp[0], &vpp[1], &vpp[2], &vpp[3], 7, 0);
3701                         }
3702                 }
3703         }
3704         if (MolActionCreateAndPerform(mp, gMolActionInsertFrames, ig, mp->natoms * dcd.nframes, vp, (cp == NULL ? 0 : dcd.nframes * 4), cp) != 0)
3705                 s_append_asprintf(errbuf, "Cannot insert frames");
3706         mp->startStep = dcd.nstart;
3707         mp->stepsPerFrame = dcd.ninterval;
3708         mp->psPerStep = dcd.delta;
3709 exit:
3710         DcdClose(&dcd);
3711         if (cp != NULL)
3712                 free(cp);
3713         free(vp);
3714         free(xp);
3715         free(yp);
3716         free(zp);
3717         IntGroupRelease(ig);
3718         if (errcount == 0)
3719                 return 0;
3720         else return 1;
3721 }
3722
3723 int
3724 MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
3725 {
3726         FILE *fp;
3727         char buf[1024];
3728         int lineNumber;
3729         int i, retval;
3730         Vector v[3], vv;
3731         double d[3];
3732         int n, flag;
3733         char flags[3];
3734         *errbuf = NULL;
3735         fp = fopen(fname, "rb");
3736         if (fp == NULL) {
3737                 s_append_asprintf(errbuf, "Cannot open file");
3738                 return -1;
3739         }
3740         errbuf[0] = 0;
3741         lineNumber = 0;
3742         retval = 0;
3743         flags[0] = flags[1] = flags[2] = 0;
3744         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3745                 if (strncmp(buf, "Bounding box:", 13) == 0) {
3746                         for (i = 0; i < 3; i++) {
3747                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3748                                         s_append_asprintf(errbuf, "line %d: missing %d component of the bounding box", lineNumber, i + 1);
3749                                         retval = 1;
3750                                         goto abort;
3751                                 }
3752                                 n = sscanf(buf, "%lf %lf %lf %d", &d[0], &d[1], &d[2], &flag);
3753                                 if (n < 3) {
3754                                         vv.x = vv.y = vv.z = 0.0;
3755                                         switch (i) {
3756                                                 case 0: vv.x = d[0]; break;
3757                                                 case 1: vv.y = d[0]; break;
3758                                                 case 2: vv.z = d[0]; break;
3759                                         }
3760                                         if (n == 1 || (n == 2 && d[1] != 0.0))
3761                                                 flags[i] = 1;
3762                                 } else {
3763                                         vv.x = d[0];
3764                                         vv.y = d[1];
3765                                         vv.z = d[2];
3766                                         if (n == 4)
3767                                                 flags[i] = (flag != 0);
3768                                         else
3769                                                 flags[i] = (VecLength2(vv) != 0);
3770                                 }
3771                                 v[i] = vv;
3772                         }
3773                         if (mp->cell != NULL)
3774                                 vv = mp->cell->origin;
3775                         else
3776                                 vv.x = vv.y = vv.z = 0.0;
3777                         MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
3778                 } else if (strncmp(buf, "Bounding box origin:", 20) == 0) {
3779                         if (mp->cell != NULL) {
3780                                 v[0] = mp->cell->axes[0];
3781                                 v[1] = mp->cell->axes[1];
3782                                 v[2] = mp->cell->axes[2];
3783                                 memmove(flags, mp->cell->flags, 3);
3784                         } else {
3785                                 v[0].x = 1.0; v[0].y = v[0].z = 0.0;
3786                                 v[1].y = 1.0; v[1].x = v[1].z = 0.0;
3787                                 v[2].z = 1.0; v[2].x = v[2].y = 0.0;
3788                                 flags[0] = flags[1] = flags[2] = 1.0;
3789                         }
3790                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0 || (n = sscanf(buf, "%lf %lf %lf", &d[0], &d[1], &d[2]) < 3)) {
3791                                 s_append_asprintf(errbuf, "line %d: wrong format for the bounding box origin", lineNumber);
3792                                 retval = 1;
3793                                 goto abort;
3794                         }
3795                         vv.x = d[0];
3796                         vv.y = d[1];
3797                         vv.z = d[2];
3798                         MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
3799                 }
3800         }
3801         fclose(fp);
3802         return 0;
3803 abort:
3804         if (fp != NULL)
3805                 fclose(fp);
3806         return retval;
3807 }
3808                         
3809 int
3810 MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
3811 {
3812         int retval;
3813         *errbuf = NULL;
3814         if (ftype == NULL || *ftype == 0) {
3815                 const char *cp;
3816                 cp = strrchr(fname, '.');
3817                 if (cp != NULL)
3818                         ftype = cp + 1;
3819                 else {
3820                         cp = guessMoleculeType(fname);
3821                         if (strcmp(cp, "???") != 0)
3822                                 ftype = cp;
3823                 }
3824         }
3825         if (strcasecmp(ftype, "psf") == 0) {
3826                 retval = MoleculeWriteToPsfFile(mp, fname, errbuf);
3827         } else if (strcasecmp(ftype, "pdb") == 0) {
3828                 retval = MoleculeWriteToPdbFile(mp, fname, errbuf);
3829         } else if (strcasecmp(ftype, "tep") == 0) {
3830                 retval = MoleculeWriteToTepFile(mp, fname, errbuf);
3831         } else {
3832                 s_append_asprintf(errbuf, "The file format should be specified");
3833                 retval = 1;
3834         }
3835         if (retval == 0)
3836                 MoleculeSetPath(mp, fname);
3837         return retval;
3838 }
3839
3840 int
3841 MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char **errbuf)
3842 {
3843         FILE *fp;
3844         Int i, j, k, n1, n2, n3, n_aniso, nframes, nanchors, n_uff;
3845         Atom *ap;
3846         char bufs[6][8];
3847
3848         *errbuf = NULL;
3849         fp = fopen(fname, "wb");
3850         if (fp == NULL) {
3851                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
3852                 return 1;
3853         }
3854         errbuf[0] = 0;
3855
3856         nframes = MoleculeFlushFrames(mp);
3857
3858         fprintf(fp, "!:atoms\n");
3859         fprintf(fp, "! idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge\n");
3860         n1 = n2 = n3 = n_aniso = nanchors = n_uff = 0;
3861         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3862                 strncpy(bufs[0], ap->segName, 4);
3863                 bufs[0][4] = 0;
3864                 strncpy(bufs[1], ap->resName, 4);
3865                 bufs[1][4] = 0;
3866                 strncpy(bufs[2], ap->aname, 4);
3867                 bufs[2][4] = 0;
3868                 AtomTypeDecodeToString(ap->type, bufs[3]);
3869                 bufs[3][6] = 0;
3870                 strncpy(bufs[4], ap->element, 4);
3871                 bufs[4][2] = 0;
3872                 for (j = 0; j < 5; j++) {
3873                         if (bufs[j][0] == 0) {
3874                                 bufs[j][0] = '_';
3875                                 bufs[j][1] = 0;
3876                         }
3877                         for (k = 0; k < 6; k++) {
3878                                 if (bufs[j][k] == 0)
3879                                         break;
3880                                 if (bufs[j][k] > 0 && bufs[j][k] < ' ')
3881                                         bufs[j][k] = '_';
3882                         }
3883                 }
3884                 if (SYMOP_ALIVE(ap->symop))
3885                         n1++;
3886                 if (ap->fix_force != 0)
3887                         n2++;
3888                 if (ap->mm_exclude || ap->periodic_exclude)
3889                         n3++;
3890                 if (ap->aniso != NULL)
3891                         n_aniso++;
3892                 if (ap->anchor != NULL)
3893                         nanchors++;
3894                 if (ap->uff_type[0] != 0)
3895                         n_uff++;
3896                 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);
3897         }
3898         fprintf(fp, "\n");
3899         
3900         if (n_uff > 0) {
3901                 fprintf(fp, "!:uff_type\n");
3902                 fprintf(fp, "! idx uff_type\n");
3903                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3904                         fprintf(fp, "%d %.5s\n", i, ap->uff_type);
3905                 }
3906                 fprintf(fp, "\n");
3907         }
3908         
3909         if (n1 > 0) {
3910                 fprintf(fp, "!:atoms_symop\n");
3911                 fprintf(fp, "! idx symop symbase\n");
3912                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3913                         int n;
3914                         n = ap->symop.sym * 1000000 + ap->symop.dx * 10000 + ap->symop.dy * 100 + ap->symop.dz;
3915                         fprintf(fp, "%d %d %d\n", i, n, ap->symbase);
3916                 }
3917                 fprintf(fp, "\n");
3918         }
3919         
3920         if (n2 > 0) {
3921                 fprintf(fp, "!:atoms_fix\n");
3922                 fprintf(fp, "! idx fix_force fix_pos\n");
3923                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3924                         fprintf(fp, "%d %f %f %f %f\n", i, ap->fix_force, ap->fix_pos.x, ap->fix_pos.y, ap->fix_pos.z);
3925                 }
3926                 fprintf(fp, "\n");
3927         }
3928         
3929         if (n3 > 0) {
3930                 fprintf(fp, "!:mm_exclude\n");
3931                 fprintf(fp, "! idx mm_exclude periodic_exclude\n");
3932                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3933                         fprintf(fp, "%d %d %d\n", i, ap->mm_exclude, ap->periodic_exclude);
3934                 }
3935                 fprintf(fp, "\n");
3936         }
3937         
3938         if (nanchors > 0) {
3939                 fprintf(fp, "!:pi_anchor\n");
3940                 fprintf(fp, "! idx count; n1 weight1; n2 weight2; ...; nN weightN\n");
3941                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3942                         Int *ip;
3943                         if (ap->anchor == NULL)
3944                                 continue;
3945                         k = ap->anchor->connect.count;
3946                         ip = AtomConnectData(&ap->anchor->connect);
3947                         fprintf(fp, "%d %d\n", i, k);
3948                         for (j = 0; j < k; j++) {
3949                                 fprintf(fp, "%d %f\n", ip[j], ap->anchor->coeffs[j]);
3950                         }
3951                 }
3952                 fprintf(fp, "\n");
3953         }
3954                                 
3955         n1 = nframes;
3956         if (n1 > 0)
3957                 n2 = mp->cframe;
3958         else
3959                 n2 = 0;
3960         for (i = 0; (i == n2 || i < n1); i++) {
3961                 fprintf(fp, "!:positions ; frame %d\n", i);
3962                 fprintf(fp, "! idx x y z [sx sy sz]\n");
3963                 for (j = 0, ap = mp->atoms; j < mp->natoms; j++, ap = ATOM_NEXT(ap)) {
3964                         Vector *vp;
3965                         Byte sig_flag = 0;
3966                         if (i != n2 && i < ap->nframes)
3967                                 vp = ap->frames + i;
3968                         else {
3969                                 vp = &(ap->r);
3970                                 if (ap->sigma.x != 0.0 || ap->sigma.y != 0.0 || ap->sigma.z != 0.0)
3971                                         sig_flag = 1;
3972                         }
3973                         fprintf(fp, "%d %.8f %.8f %.8f", j, vp->x, vp->y, vp->z);
3974                         if (sig_flag) {
3975                                 fprintf(fp, " %.8f %.8f %.8f", ap->sigma.x, ap->sigma.y, ap->sigma.z);
3976                         }
3977                         fprintf(fp, "\n");
3978                 }
3979                 fprintf(fp, "\n");
3980         }
3981         
3982         if (mp->nbonds > 0) {
3983                 fprintf(fp, "!:bonds\n");
3984                 fprintf(fp, "! from1 to1 from2 to2 from3 to3 from4 to4\n");
3985                 for (i = 0; i < mp->nbonds; i++) {
3986                         fprintf(fp, "%d %d%c", mp->bonds[i * 2], mp->bonds[i * 2 + 1], (i % 4 == 3 || i == mp->nbonds - 1 ? '\n' : ' '));
3987                 }
3988                 fprintf(fp, "\n");
3989         }
3990
3991         if (mp->nbondOrders > 0) {
3992                 fprintf(fp, "!:bond_orders\n");
3993                 fprintf(fp, "! order1 order2 order3 order4\n");
3994                 for (i = 0; i < mp->nbondOrders; i++) {
3995                         fprintf(fp, "%.6f%c", mp->bondOrders[i], (i % 4 == 3 || i == mp->nbondOrders - 1 ? '\n' : ' '));
3996                 }
3997                 fprintf(fp, "\n");
3998         }
3999         
4000         if (mp->nangles > 0) {
4001                 fprintf(fp, "!:angles\n");
4002                 fprintf(fp, "! a1 b1 c1 a2 b2 c2 a3 b3 c3\n");
4003                 for (i = 0; i < mp->nangles; i++) {
4004                         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' : ' '));
4005                 }
4006                 fprintf(fp, "\n");
4007         }
4008         
4009         if (mp->ndihedrals > 0) {
4010                 fprintf(fp, "!:dihedrals\n");
4011                 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
4012                 for (i = 0; i < mp->ndihedrals; i++) {
4013                         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' : ' '));
4014                 }
4015                 fprintf(fp, "\n");
4016         }
4017         
4018         if (mp->nimpropers > 0) {
4019                 fprintf(fp, "!:impropers\n");
4020                 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
4021                 for (i = 0; i < mp->nimpropers; i++) {
4022                         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' : ' '));
4023                 }
4024                 fprintf(fp, "\n");
4025         }
4026         
4027         if (mp->cell != NULL) {
4028                 fprintf(fp, "!:xtalcell\n");
4029                 fprintf(fp, "! a b c alpha beta gamma\n");
4030                 fprintf(fp, "! This information is redundant and overridden by the following periodic_box info\n");
4031                 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]);
4032                 fprintf(fp, "\n");
4033
4034                 fprintf(fp, "!:periodic_box\n");
4035                 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");
4036                 for (i = 0; i < 3; i++)
4037                         fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->axes[i].x, mp->cell->axes[i].y, mp->cell->axes[i].z);
4038                 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->origin.x, mp->cell->origin.y, mp->cell->origin.z);
4039                 fprintf(fp, "%d %d %d%s\n", mp->cell->flags[0], mp->cell->flags[1], mp->cell->flags[2], (mp->cell->has_sigma ? " 1" : ""));
4040                 if (mp->cell->has_sigma) {
4041                         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]);
4042                 }
4043                 fprintf(fp, "\n");
4044         }
4045         
4046         if (mp->nframe_cells > 0) {
4047                 fprintf(fp, "!:frame_periodic_boxes\n");
4048                 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz\n");
4049                 for (i = 0; i < mp->nframe_cells * 4; i++) {
4050                         fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->frame_cells[i].x, mp->frame_cells[i].y, mp->frame_cells[i].z);
4051                 }
4052                 fprintf(fp, "\n");
4053         }
4054         
4055         if (mp->nsyms > 0) {
4056                 fprintf(fp, "!:symmetry_operations\n");
4057                 fprintf(fp, "! a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3\n");
4058                 for (i = 0; i < mp->nsyms; i++) {
4059                         Transform *tp = mp->syms + i;
4060                         const unsigned char s_index_order[12] = {0, 3, 6, 1, 4, 7, 2, 5, 8, 9, 10, 11};
4061                         for (j = 0; j < 12; j++)
4062                                 fprintf(fp, "%11.6f%c", (*tp)[s_index_order[j]], (j % 3 == 2 ? '\n' : ' '));
4063                 }
4064                 fprintf(fp, "\n");
4065         }
4066         
4067         if (n_aniso > 0) {
4068                 fprintf(fp, "!:anisotropic_thermal_parameters\n");
4069                 fprintf(fp, "! b11 b22 b33 b12 b13 b23 [sigma; sb11 sb22 sb33 sb12 sb13 sb23]\n");
4070                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4071                         if (ap->aniso != NULL) {
4072                                 Double *bp = ap->aniso->bij;
4073                                 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" : ""));
4074                                 if (ap->aniso->has_bsig) {
4075                                         bp = ap->aniso->bsig;
4076                                         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]);
4077                                 }
4078                         } else {
4079                                 fprintf(fp, "0 0 0 0 0 0\n");
4080                         }
4081                 }
4082                 fprintf(fp, "\n");              
4083         }
4084         
4085         if (mp->arena != NULL) {
4086                 MDArena *arena = mp->arena;
4087                 fprintf(fp, "!:md_parameters\n");
4088                 fprintf(fp, "log_file %s\n", arena->log_result_name);
4089                 fprintf(fp, "coord_file %s\n", arena->coord_result_name);
4090                 fprintf(fp, "vel_file %s\n", arena->vel_result_name);
4091                 fprintf(fp, "force_file %s\n", arena->force_result_name);
4092                 fprintf(fp, "debug_file %s\n", arena->debug_result_name);
4093                 fprintf(fp, "debug_output_level %d\n", arena->debug_output_level);
4094                 fprintf(fp, "step %d\n", arena->step);
4095                 fprintf(fp, "coord_output_freq %d\n", arena->coord_output_freq);
4096                 fprintf(fp, "energy_output_freq %d\n", arena->energy_output_freq);
4097                 fprintf(fp, "coord_frame %d\n", arena->coord_result_frame);
4098                 fprintf(fp, "timestep %g\n", arena->timestep);
4099                 fprintf(fp, "cutoff %g\n", arena->cutoff);
4100                 fprintf(fp, "electro_cutoff %g\n", arena->electro_cutoff);
4101                 fprintf(fp, "pairlist_distance %g\n", arena->pairlist_distance);
4102                 fprintf(fp, "switch_distance %g\n", arena->switch_distance);
4103                 fprintf(fp, "temperature %g\n", arena->temperature);
4104                 fprintf(fp, "andersen_freq %d\n", arena->andersen_thermo_freq);
4105                 fprintf(fp, "andersen_coupling %g\n", arena->andersen_thermo_coupling);
4106                 fprintf(fp, "random_seed %d\n", arena->random_seed);
4107                 fprintf(fp, "dielectric %g\n", arena->dielectric);
4108                 fprintf(fp, "gradient_convergence %g\n", arena->gradient_convergence);
4109                 fprintf(fp, "coordinate_convergence %g\n", arena->coordinate_convergence);
4110                 fprintf(fp, "use_xplor_shift %d\n", arena->use_xplor_shift);
4111                 fprintf(fp, "scale14_vdw %g\n", arena->scale14_vdw);
4112                 fprintf(fp, "scale14_elect %g\n", arena->scale14_elect);
4113                 fprintf(fp, "relocate_center %d\n", arena->relocate_center);
4114                 fprintf(fp, "surface_probe_radius %g\n", arena->probe_radius);
4115                 fprintf(fp, "surface_tension %g\n", arena->surface_tension);
4116                 fprintf(fp, "surface_potential_freq %d\n", arena->surface_potential_freq);
4117                 fprintf(fp, "use_graphite %d\n", arena->use_graphite);
4118                 fprintf(fp, "alchemical_lambda %g\n", arena->alchem_lambda);
4119                 fprintf(fp, "alchemical_delta_lambda %g\n", arena->alchem_dlambda);
4120                 if (arena->nalchem_flags > 0) {
4121                         fprintf(fp, "alchem_flags %d", arena->nalchem_flags);
4122                         for (i = 0; i < arena->nalchem_flags; i++) {
4123                                 if (i % 60 == 0)
4124                                         fputc('\n', fp);
4125                                 else if (i % 10 == 0)
4126                                         fputc(' ', fp);
4127                                 fputc('0' + arena->alchem_flags[i], fp);
4128                         }
4129                         fputc('\n', fp);
4130                 }
4131                 if (arena->pressure != NULL) {
4132                         Double *dp;
4133                         fprintf(fp, "pressure_freq %d\n", arena->pressure->freq);
4134                         fprintf(fp, "pressure_coupling %g\n", arena->pressure->coupling);
4135                         dp = arena->pressure->apply;
4136                         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]);
4137                         dp = arena->pressure->cell_flexibility;
4138                         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]);
4139                         fprintf(fp, "pressure_fluctuate_cell_origin %g\n", arena->pressure->fluctuate_cell_origin);
4140                         fprintf(fp, "pressure_fluctuate_cell_orientation %g\n", arena->pressure->fluctuate_cell_orientation);
4141                 }
4142                 fprintf(fp, "\n");
4143
4144                 if (mp->par != NULL) {
4145                         Parameter *par = mp->par;
4146                         fprintf(fp, "!:parameters\n");
4147                         ParameterAppendToFile(par, fp);
4148                         fprintf(fp, "\n");
4149                 }
4150                 
4151                 fprintf(fp, "!:velocity\n");
4152                 fprintf(fp, "! idx vx vy vz\n");
4153                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4154                         fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->v.x, ap->v.y, ap->v.z);
4155                 }
4156                 fprintf(fp, "\n");
4157
4158                 fprintf(fp, "!:force\n");
4159                 fprintf(fp, "! idx fx fy fz\n");
4160                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4161                         fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->f.x, ap->f.y, ap->f.z);
4162                 }
4163                 fprintf(fp, "\n");
4164         }
4165         
4166         if (mp->mview != NULL) {
4167                 double f[4];
4168                 if (mp->mview->track != NULL) {
4169                         fprintf(fp, "!:trackball\n");
4170                         fprintf(fp, "! scale; trx try trz; theta_deg x y z\n");
4171                         f[0] = TrackballGetScale(mp->mview->track);
4172                         fprintf(fp, "%f\n", f[0]);
4173                         TrackballGetTranslate(mp->mview->track, f);
4174                         fprintf(fp, "%f %f %f\n", f[0], f[1], f[2]);
4175                         TrackballGetRotate(mp->mview->track, f);
4176                         fprintf(fp, "%f %f %f %f\n", f[0], f[1], f[2], f[3]);
4177                         fprintf(fp, "\n");
4178                 }
4179                 fprintf(fp, "!:view\n");
4180                 fprintf(fp, "show_unit_cell %d\n", mp->mview->showUnitCell);
4181                 fprintf(fp, "show_periodic_box %d\n", mp->mview->showPeriodicBox);
4182                 fprintf(fp, "show_expanded_atoms %d\n", mp->mview->showExpandedAtoms);
4183                 fprintf(fp, "show_ellipsoids %d\n", mp->mview->showEllipsoids);
4184                 fprintf(fp, "show_hydrogens %d\n", mp->mview->showHydrogens);
4185                 fprintf(fp, "show_dummy_atoms %d\n", mp->mview->showDummyAtoms);
4186                 fprintf(fp, "show_rotation_center %d\n", mp->mview->showRotationCenter);
4187                 fprintf(fp, "show_graphite_flag %d\n", mp->mview->showGraphiteFlag);
4188                 fprintf(fp, "show_graphite %d\n", mp->mview->showGraphite);
4189                 fprintf(fp, "show_periodic_image_flag %d\n", mp->mview->showPeriodicImageFlag);
4190                 fprintf(fp, "show_periodic_image %d %d %d %d %d %d\n",
4191                                 mp->mview->showPeriodicImage[0], mp->mview->showPeriodicImage[1],
4192                                 mp->mview->showPeriodicImage[2], mp->mview->showPeriodicImage[3],
4193                                 mp->mview->showPeriodicImage[4], mp->mview->showPeriodicImage[5]);
4194                 if (mp->mview->atomRadius != 0.2)
4195                         fprintf(fp, "atom_radius %f\n", mp->mview->atomRadius);
4196                 if (mp->mview->bondRadius != 0.1)
4197                         fprintf(fp, "bond_radius %f\n", mp->mview->bondRadius);
4198                 if (mp->mview->atomResolution != 12)
4199                         fprintf(fp, "atom_resolution %d\n", mp->mview->atomResolution);
4200                 if (mp->mview->bondResolution != 8)
4201                         fprintf(fp, "bond_resolution %d\n", mp->mview->bondResolution);
4202                 fprintf(fp, "\n");
4203         }
4204
4205         fclose(fp);
4206         return 0;
4207 }
4208
4209 int
4210 MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char **errbuf)
4211 {
4212         FILE *fp;
4213         int i;
4214         Atom *ap;
4215         *errbuf = NULL;
4216         fp = fopen(fname, "wb");
4217         if (fp == NULL) {
4218                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4219                 return 1;
4220         }
4221         fprintf(fp, "PSF\n\n");
4222         fprintf(fp, "       1 !NTITLE\n");
4223         fprintf(fp, " REMARKS FILENAME=\n");
4224         fprintf(fp, "\n");
4225         
4226         /*  Atoms  */
4227         fprintf(fp, "%8d !NATOM\n", mp->natoms);
4228         for (i = 0; i < mp->natoms; i++) {
4229                 const char *fmt;
4230                 ap = ATOM_AT_INDEX(mp->atoms, i);
4231                 fprintf(fp, "%8d ", i + 1);
4232                 if (ap->resSeq >= 10000) {
4233                         fmt = "%-3.3s %-5d ";
4234                 } else {
4235                         fmt = "%-4.4s %-4d ";
4236                 }
4237                 fprintf(fp, fmt, ap->segName, ap->resSeq);
4238                 fprintf(fp, "%-3.3s  %-4.4s %-4.4s   %12.6f  %8.4f           0\n",
4239                         ap->resName, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->charge, ap->weight);
4240         }
4241         fprintf(fp, "\n");
4242         
4243         /*  Bonds  */
4244         fprintf(fp, "%8d !NBOND: bonds\n", mp->nbonds);
4245         for (i = 0; i < mp->nbonds * 2; i++) {
4246                 fprintf(fp, "%8d", mp->bonds[i] + 1);
4247                 if (i % 8 == 7)
4248                         fprintf(fp, "\n");
4249         }
4250         if (i % 8 != 0)
4251                 fprintf(fp, "\n");
4252         fprintf(fp, "\n");
4253         
4254         /*  Angles  */
4255         fprintf(fp, "%8d !NTHETA: angles\n", mp->nangles);
4256         for (i = 0; i < mp->nangles * 3; i++) {
4257                 fprintf(fp, "%8d", mp->angles[i] + 1);
4258                 if (i % 9 == 8)
4259                         fprintf(fp, "\n");
4260         }
4261         if (i % 9 != 0)
4262                 fprintf(fp, "\n");
4263         fprintf(fp, "\n");
4264         
4265         /*  Dihedrals  */
4266         fprintf(fp, "%8d !NPHI: dihedrals\n", mp->ndihedrals);
4267         for (i = 0; i < mp->ndihedrals * 4; i++) {
4268                 fprintf(fp, "%8d", mp->dihedrals[i] + 1);
4269                 if (i % 8 == 7)
4270                         fprintf(fp, "\n");
4271         }
4272         if (i % 8 != 0)
4273                 fprintf(fp, "\n");
4274         fprintf(fp, "\n");
4275         
4276         /*  Dihedrals  */
4277         fprintf(fp, "%8d !NIMPHI: impropers\n", mp->nimpropers);
4278         for (i = 0; i < mp->nimpropers * 4; i++) {
4279                 fprintf(fp, "%8d", mp->impropers[i] + 1);
4280                 if (i % 8 == 7)
4281                         fprintf(fp, "\n");
4282         }
4283         if (i % 8 != 0)
4284                 fprintf(fp, "\n");
4285         fprintf(fp, "\n");
4286         
4287         fprintf(fp, "%8d !NDON: donors\n\n", 0);
4288         fprintf(fp, "%8d !NACC: acceptors\n\n", 0);
4289         fprintf(fp, "%8d !NNB: non-bonding exclusions\n\n", 0);
4290         for (i = 0; i < mp->natoms; i++) {
4291                 fprintf(fp, "%8d", 0);
4292                 if (i % 8 == 7)
4293                         fprintf(fp, "\n");
4294         }
4295         if (i % 8 != 0)
4296                 fprintf(fp, "\n");
4297         fprintf(fp, "\n");
4298         fprintf(fp, "%8d !NGRP: groups\n", 1);
4299         fprintf(fp, "       0       0       0\n");
4300         fprintf(fp, "\n");
4301         
4302         i = strlen(fname);
4303         if (i > 5 && strcmp(fname + i - 5, ".psfx") == 0) {
4304                 /*  Extended psf (with coordinates and other info)  */
4305                 fprintf(fp, "%8d !COORD: coordinates\n", mp->natoms);
4306                 for (i = 0; i < mp->natoms; i++) {
4307                         Vector r;
4308                         ap = ATOM_AT_INDEX(mp->atoms, i);
4309                         r = ap->r;
4310                         fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->aname);
4311                 }
4312                 fprintf(fp, "\n");
4313         }
4314                 
4315         fclose(fp);
4316         return 0;
4317 }
4318
4319 int
4320 MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char **errbuf)
4321 {
4322         FILE *fp;
4323         int i, j;
4324         Atom *ap;
4325         *errbuf = NULL;
4326         fp = fopen(fname, "wb");
4327         if (fp == NULL) {
4328                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4329                 return 1;
4330         }
4331         for (i = 0; i < mp->natoms; i++) {
4332                 char buf[6];
4333                 ap = ATOM_AT_INDEX(mp->atoms, i);
4334                 if (ap->resSeq >= 10000) {
4335                         snprintf(buf, sizeof buf, "%c%03d", 'A' + (ap->resSeq - 10000) / 1000, ap->resSeq % 1000);
4336                 } else {
4337                         snprintf(buf, sizeof buf, "%4d", ap->resSeq);
4338                 }
4339                 fprintf(fp, "ATOM  %5d %-4.4s%1.1s%-3.3s %1.1s%4.4s%1.1s   "
4340                                         "%8.3f%8.3f%8.3f %5.2f %5.2f      "
4341                                         "%-4.4s%-2.2s%-2d\n",
4342                         i + 1, ap->aname, " ", ap->resName, " ", buf, " ",
4343                         ap->r.x, ap->r.y, ap->r.z, ap->occupancy, ap->tempFactor,
4344                         ap->segName, ap->element, ap->intCharge);
4345         }
4346         for (i = 0; i < mp->natoms; i++) {
4347                 Int *cp;
4348                 ap = ATOM_AT_INDEX(mp->atoms, i);
4349                 cp = AtomConnectData(&ap->connect);
4350                 for (j = 0; j < ap->connect.count; j++) {
4351                         if (j % 4 == 0) {
4352                                 if (j > 0)
4353                                         fprintf(fp, "\n");
4354                                 fprintf(fp, "CONECT%5d", i + 1);
4355                         }
4356                         fprintf(fp, "%5d", cp[j] + 1);
4357                 }
4358                 if (j > 0)
4359                         fprintf(fp, "\n");
4360         }
4361         fprintf(fp, "END\n");
4362         fclose(fp);
4363         return 0;
4364 }
4365
4366 int
4367 MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char **errbuf)
4368 {
4369         DcdRecord dcd;
4370         SFloat32 *xp, *yp, *zp;
4371         int n;
4372         *errbuf = NULL;
4373         if (mp == NULL || mp->natoms == 0) {
4374                 s_append_asprintf(errbuf, "Molecule is empty");
4375                 return 1;
4376         }
4377         memset(&dcd, 0, sizeof(dcd));
4378         dcd.natoms = mp->natoms;
4379         dcd.nframes = MoleculeGetNumberOfFrames(mp);
4380         if (dcd.nframes == 0) {
4381                 s_append_asprintf(errbuf, "no frame is present");
4382                 return 1;
4383         }
4384         dcd.nstart = mp->startStep;
4385         dcd.ninterval = mp->stepsPerFrame;
4386         if (dcd.ninterval == 0)
4387                 dcd.ninterval = 1;
4388         dcd.nend = dcd.nstart + (dcd.nframes - 1) * dcd.ninterval;
4389         if (mp->cell != NULL)
4390                 dcd.nextra = 1;
4391         dcd.delta = mp->psPerStep;
4392         if (dcd.delta == 0.0)
4393                 dcd.delta = 1.0;
4394         dcd.ncharmver = 24;
4395         n = DcdCreate(fname, &dcd);
4396         if (n != 0) {
4397                 if (n < 0)
4398                         s_append_asprintf(errbuf, "Cannot create dcd file");
4399                 else
4400                         s_append_asprintf(errbuf, "Cannot write dcd header");
4401                 DcdClose(&dcd);
4402                 return 1;
4403         }
4404         
4405         xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4406         yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4407         zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4408         if (xp == NULL || yp == NULL || zp == NULL) {
4409                 s_append_asprintf(errbuf, "Cannot allocate memory");
4410                 if (xp) free(xp);
4411                 if (yp) free(yp);
4412                 if (zp) free(zp);
4413                 DcdClose(&dcd);
4414                 return 1;
4415         }
4416         for (n = 0; n < dcd.nframes; n++) {
4417                 int i;
4418                 Atom *ap;
4419                 for (i = 0, ap = mp->atoms; i < dcd.natoms && mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4420                         Vector r;
4421                         if (ap->frames == NULL || n >= ap->nframes)
4422                                 r = ap->r;
4423                         else
4424                                 r = ap->frames[n];
4425                         xp[i] = r.x;
4426                         yp[i] = r.y;
4427                         zp[i] = r.z;
4428                 }
4429                 if (i < dcd.natoms) {
4430                         size_t sz = (dcd.natoms - i) * sizeof(SFloat32);
4431                         memset(xp + i, 0, sz);
4432                         memset(yp + i, 0, sz);
4433                         memset(zp + i, 0, sz);
4434                 }
4435                 if (n < mp->nframe_cells && mp->frame_cells != NULL) {
4436                         Vector *cp = &(mp->frame_cells[n * 4]);
4437                         dcd.globalcell[0] = VecLength(cp[0]);
4438                         dcd.globalcell[2] = VecLength(cp[1]);
4439                         dcd.globalcell[5] = VecLength(cp[2]);
4440                         dcd.globalcell[1] = VecDot(cp[0], cp[1]) / (dcd.globalcell[0] * dcd.globalcell[2]);
4441                         dcd.globalcell[3] = VecDot(cp[0], cp[2]) / (dcd.globalcell[0] * dcd.globalcell[5]);
4442                         dcd.globalcell[4] = VecDot(cp[1], cp[2]) / (dcd.globalcell[2] * dcd.globalcell[5]);                     
4443                 }                       
4444                 if (DcdWriteFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
4445                         s_append_asprintf(errbuf, "Write error in dcd file");
4446                         goto exit;
4447                 }
4448         }
4449         
4450 exit:
4451         DcdClose(&dcd);
4452         free(xp);
4453         free(yp);
4454         free(zp);
4455         if (errbuf[0] == 0)
4456                 return 0;
4457         else return 1;
4458 }
4459
4460 int
4461 MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
4462 {
4463         FILE *fp;
4464         int i;
4465         Vector v;
4466         *errbuf = NULL;
4467         fp = fopen(fname, "wb");
4468         if (fp == NULL) {
4469                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4470                 return 1;
4471         }
4472         if (mp->cell != NULL) {
4473                 fprintf(fp, "Bounding box:\n");
4474                 for (i = 0; i < 3; i++) {
4475                         v = mp->cell->axes[i];
4476                         fprintf(fp, "%.3f %.3f %.3f %d\n", v.x, v.y, v.z, mp->cell->flags[i]);
4477                 }
4478                 fprintf(fp, "Bounding box origin:\n");
4479                 v = mp->cell->origin;
4480                 fprintf(fp, "%.3f %.3f %.3f\n", v.x, v.y, v.z);
4481         }
4482         fclose(fp);
4483         return 0;
4484 }
4485                 
4486  static int
4487 sCompareByElement(const void *ap, const void *bp)
4488 {
4489         return ((*(Atom **)bp)->atomicNumber - (*(Atom **)ap)->atomicNumber);
4490 }
4491
4492 static int
4493 sMakeAdc(int n, int base, Symop symop)
4494 {
4495         int an, sym;
4496         if (SYMOP_ALIVE(symop)) {
4497                 an = base;
4498                 sym = (symop.dx + 5) * 10000 + (symop.dy + 5) * 1000 + (symop.dz + 5) * 100 + symop.sym + 1;
4499         } else {
4500                 an = n;
4501                 sym = 55501;
4502         }
4503         return (an + 1) * 100000 + sym;
4504 }
4505
4506 static int
4507 sCompareAdc(const void *ap, const void *bp)
4508 {
4509         int n = *((Int *)ap) % 100000 - *((Int *)bp) % 100000;
4510         if (n == 0)
4511                 n = *((Int *)ap) / 100000 - *((Int *)bp) / 100000;
4512         return n;
4513 }
4514
4515 static void
4516 sOutputAtomListInstructions(FILE *fp, int natoms, Atom *atoms)
4517 {
4518         int i, j, k, an, sym;
4519         Atom *ap;
4520         Int *adc;
4521         adc = (Int *)malloc(sizeof(Int) * natoms);
4522         if (adc == NULL)
4523                 return;
4524         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4525                 if (ap->exflags & kAtomHiddenFlag)
4526                         continue;
4527                 adc[i] = sMakeAdc(i, ap->symbase, ap->symop);
4528         }
4529         mergesort(adc, natoms, sizeof(Int), sCompareAdc);
4530         
4531         /*  Create the atom list  */
4532         an = sym = -1;
4533         for (i = j = k = 0; i < natoms; i++) {
4534                 int an1 = adc[i] / 100000;
4535                 int sym1 = adc[i] % 100000;
4536                 if (sym == sym1 && an1 == an + 1) {
4537                         /*  Continuous  */
4538                         an = an1;
4539                         k++;
4540                         continue;
4541                 }
4542                 if (k > 0)
4543                         /*  Output the last atom with a minus sign  */
4544                         adc[j++] = -(an * 100000 + sym);
4545                 /*  Output this atom  */
4546                 adc[j++] = adc[i];
4547                 an = an1;
4548                 sym = sym1;
4549                 k = 0;
4550         }
4551         if (k > 0)
4552                 adc[j++] = -(an * 100000 + sym);
4553         
4554         /*  Create the instruction cards  */
4555         for (i = k = 0; i < j; i++) {
4556                 if (k == 0)
4557                         fprintf(fp, "      401");
4558                 fprintf(fp, "%9d", adc[i]);
4559                 k++;
4560                 if (i == j - 1 || k == 6 || (k == 5 && i < j - 2 && adc[i + 2] < 0)) {
4561                         fprintf(fp, "\n");
4562                         k = 0;
4563                 }
4564         }
4565         free(adc);
4566 }
4567
4568 static int
4569 sEllipsoidType(int an)
4570 {
4571         return (an >= 18 ? 3 : (an >= 2 && an != 6 ? 2 : (an > 0 ? 1 : 0)));
4572 }
4573
4574 static void
4575 sOutputAtomTypeInstructions(FILE *fp, int natoms, Atom *atoms)
4576 {
4577         int i;
4578         Atom *ap;
4579         int etype, elast, istart, ilast, n1, n2;
4580         elast = istart = ilast = -1;
4581         for (i = 0, ap = atoms; i <= natoms; i++, ap++) {
4582                 if (i < natoms) {
4583                         if (SYMOP_ALIVE(ap->symop))
4584                                 continue;
4585                         if (ap->exflags & kAtomHiddenFlag)
4586                                 continue;
4587                         etype = sEllipsoidType(ap->atomicNumber);
4588                         if (elast < 0) {
4589                                 istart = ilast = i;
4590                                 elast = etype;
4591                                 continue;
4592                         } else if (elast == etype && ilast == i - 1) {
4593                                 ilast++;
4594                                 continue;
4595                         }
4596                 }
4597                 /*  Output the instruction card for the 'last' block of atoms  */
4598                 switch (etype) {
4599                         case 2:
4600                                 n1 = 4; n2 = 0; break;
4601                         case 3:
4602                                 n1 = 4; n2 = 5; break;
4603                         default:
4604                                 n1 = 1; n2 = 0; break;
4605                 }
4606                 fprintf(fp, "  1   715 %8d        0 %8d        0    0.100    0.000    0.000\n", n1, n2);
4607                 fprintf(fp, "                           %9d%9d\n", istart + 1, ilast + 1);
4608                 elast = etype;
4609                 ilast = istart = i;
4610         }
4611 }
4612
4613 static int
4614 sCompareBondType(const void *ap, const void *bp)
4615 {
4616         /*  Descending order  */
4617         return *((int *)bp) - *((int *)ap);
4618 }
4619
4620 static void
4621 sOutputBondInstructions(FILE *fp, int natoms, Atom *atoms, int overlap_correction)
4622 {
4623         Atom *ap, *ap2;
4624         char buf[96];
4625         int i, j, n[5], an, count, n1, n2, k;
4626         Int *cp;
4627         Int nexbonds;
4628         Int *exbonds;
4629         static const float sBondRad[4] = {0.060, 0.060, 0.060, 0.040};
4630         static const int sBondShade[4] = {5, 3, 1, 1};
4631
4632         n[0] = n[1] = n[2] = n[3] = 0;  /*  Start index of 3rd row atoms (and higher), 2nd row, 1st row, and H */
4633         n[4] = natoms;
4634         for (i = natoms - 1, ap = atoms + i; i >= 0; i--, ap--) {
4635                 an = ap->atomicNumber;
4636                 if (an < 2)
4637                         n[3] = i;
4638                 if (an < 10)
4639                         n[2] = i;
4640                 if (an < 18)
4641                         n[1] = i;
4642         }
4643         nexbonds = 0;
4644         exbonds = NULL;
4645         count = 0;
4646
4647         if (overlap_correction)
4648                 strcpy(buf, "  2  1001    0.000\n");
4649         else
4650                 strcpy(buf, "  2   812\n");
4651         
4652         for (i = 0; i < 4; i++) {
4653                 for (j = i; j < 4; j++) {
4654                         /*  Examine bonds between "group i" and "group j"  */
4655                         Vector dr;
4656                         double d;
4657                         double min_bond = 10000.0;     /*  Minimum distance between bound atoms  */
4658                         double min_nonbond = 10000.0;  /*  Minimum distance between non-bound atoms  */
4659                         double max_bond = -10000.0;    /*  Maximum distance between bound atoms  */
4660                         int count_exbond = 0;          /*  Number of explicit bonds in this group  */
4661                         for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
4662                                 for (n2 = n[j], ap2 = atoms + n2; n2 < n[j + 1]; n2++, ap2++) {
4663                                         if (n1 == n2)
4664                                                 continue;
4665                                         VecSub(dr, ap->r, ap2->r);
4666                                         d = VecLength(dr);
4667                                         cp = AtomConnectData(&ap->connect);
4668                                         for (k = ap->connect.count - 1; k >= 0; k--) {
4669                                                 if (cp[k] == n2)
4670                                                         break;
4671                                         }
4672                                         if (k >= 0) {
4673                                                 /*  n1 and n2 are bound  */
4674                                                 if (d < min_bond)
4675                                                         min_bond = d;
4676                                                 if (d > max_bond)
4677                                                         max_bond = d;
4678                                         } else {
4679                                                 /*  n1 and n2 are not bound  */
4680                                                 if (d < min_nonbond)
4681                                                         min_nonbond = d;
4682                                         }
4683                                 }
4684                         }
4685                         if (min_bond == 10000.0)
4686                                 continue;  /*  No bonds between these groups  */
4687                         min_bond *= 0.9;
4688                         if (max_bond + 0.002 < min_nonbond)
4689                                 max_bond += 0.002;
4690                         else {
4691                                 max_bond = min_nonbond - 0.002;
4692                                 /*  Some bonds may be omitted, so scan all bonds again  */
4693                                 for (n1 = n[i], ap = ATOM_AT_INDEX(atoms, n1); n1 < n[i + 1]; n1++, ap = ATOM_NEXT(ap)) {
4694                                         cp = AtomConnectData(&ap->connect);
4695                                         for (k = ap->connect.count - 1; k >= 0; k--) {
4696                                                 n2 = cp[k];
4697                                                 if (n2 < n[j] || n2 >= n[j + 1])
4698                                                         continue;
4699                                                 ap2 = atoms + n2;
4700                                                 VecSub(dr, ap->r, ap2->r);
4701                                                 d = VecLength(dr);
4702                                                 if (d > max_bond) {
4703                                                         /*  This bond should be explicitly defined  */
4704                                                         Int adc1, adc2;
4705                                                         if (count_exbond == 0) {
4706                                                                 adc1 = -(i + 1);  /*  Bond type  */
4707                                                                 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4708                                                         }
4709                                                         adc1 = sMakeAdc(n1, ap->symbase, ap->symop);
4710                                                         adc2 = sMakeAdc(n2, ap2->symbase, ap2->symop);
4711                                                         AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4712                                                         AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc2);
4713                                                         count_exbond++;
4714                                                 }
4715                                         }
4716                                 }
4717                         }
4718                         /*  Output the last instruction card  */
4719                         fputs(buf, fp);
4720                         /*  Make a new trailer card  */
4721                         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]);
4722                         count++;
4723                 }
4724         }
4725         if (count > 0) {
4726                 /*  Output the last trailer card  */
4727                 buf[2] = ' ';
4728                 fputs(buf, fp);
4729         }
4730         if (nexbonds > 0) {
4731                 if (count == 0 && overlap_correction) {
4732                         /*  1001 card is not yet written, so write it  */
4733                         buf[2] = ' ';
4734                         fputs(buf, fp);
4735                 }
4736                 snprintf(buf, sizeof(buf), "  1   %3d", (overlap_correction ? 821 : 811));
4737                 k = -exbonds[0] - 1;  /*  Bond type for the first block  */
4738                 i = 1;  /*  Index for exbonds[]  */
4739                 j = 0;  /*  Count in this block  */
4740                 while (i <= nexbonds) {
4741                         if (j >= 29 || i == nexbonds || exbonds[i] < 0) {
4742                                 /*  End of block  */
4743                                 buf[2] = '2';
4744                                 fputs(buf, fp);
4745                                 /*  The trailer card  */
4746                                 fprintf(fp, "                     %3d            %6.3f\n", sBondShade[k], sBondRad[k]);
4747                                 if (i == nexbonds)
4748                                         break;
4749                                 if (exbonds[i] < 0)
4750                                         k = -exbonds[i++] - 1;  /*  The new bond type  */
4751                                 j = 0;
4752                         } else if (j > 0 && j % 3 == 0) {
4753                                 buf[2] = '1';
4754                                 fputs(buf, fp);
4755                         }
4756                         n1 = exbonds[i++];
4757                         n2 = exbonds[i++];
4758                         snprintf(buf + 9 + (j % 3) * 18, sizeof(buf) - 9 - (j % 3) * 18, "%9d%9d\n", n1, n2);
4759                         j++;
4760                 }
4761                 free(exbonds);
4762         }
4763 }
4764
4765 int
4766 MoleculeWriteToTepFile(Molecule *mp, const char *fname, char **errbuf)
4767 {
4768         FILE *fp;
4769         int i, j, natoms, *ip;
4770         Int *cp;
4771         Atom *ap, *atoms, **app;
4772         Double *dp;
4773         static Double sUnit[] = {1, 1, 1, 90, 90, 90};
4774         
4775         *errbuf = NULL;
4776
4777         /*  Create sorted array of atoms  */
4778         natoms = mp->natoms;
4779         atoms = (Atom *)calloc(sizeof(Atom), natoms);
4780         app = (Atom **)calloc(sizeof(Atom *), natoms);
4781         ip = (int *)calloc(sizeof(int), natoms);
4782         if (atoms == NULL || app == NULL || ip == NULL) {
4783                 s_append_asprintf(errbuf, "Cannot allocate memory");
4784                 return 1;
4785         }
4786         /*  Sort the atom pointer by atomic number  */
4787         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
4788                 app[i] = ap;
4789         mergesort(app, natoms, sizeof(Atom *), sCompareByElement);
4790         for (i = 0; i < natoms; i++) {
4791                 /*  ip[old_index] is new_index  */
4792                 ip[app[i] - mp->atoms] = i;
4793         }
4794         /*  Copy the atom record to atoms[]  */
4795         /*  The 'v' member contains crystallographic coordinates  */
4796         /*  The connection table and symbase are renumbered  */
4797         /*  Hidden flags are modified to reflect the visibility in the MainView  */
4798         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4799                 AtomDuplicateNoFrame(ap, app[i]);
4800         /*      memmove(ap, app[i], gSizeOfAtomRecord); */
4801                 MoleculeCartesianToXtal(mp, &(ap->v), &(ap->r));
4802                 cp = AtomConnectData(&ap->connect);
4803                 for (j = ap->connect.count - 1; j >= 0; j--) {
4804                         cp[j] = ip[cp[j]];
4805                 }
4806                 if (SYMOP_ALIVE(ap->symop))
4807                         ap->symbase = ip[ap->symbase];
4808                 if (MainView_isAtomHidden(mp->mview, i)) {
4809                         ap->exflags |= kAtomHiddenFlag;
4810                 } else {
4811                         ap->exflags &= ~kAtomHiddenFlag;
4812                 }
4813         }
4814         free(ip);
4815         free(app);
4816         
4817         fp = fopen(fname, "wb");
4818         if (fp == NULL) {
4819                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4820                 return 1;
4821         }
4822
4823         /*  Title line  */
4824         fprintf(fp, "Generated by Molby\n");
4825         
4826         /*  XtalCell  */
4827         if (mp->cell != NULL) {
4828                 dp = mp->cell->cell;
4829         } else {
4830                 dp = sUnit;
4831         }
4832         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]);
4833         
4834         /*  Symmetry operations  */
4835         if (mp->nsyms > 0) {
4836                 for (i = 0; i < mp->nsyms; i++) {
4837                         dp = mp->syms[i];
4838                         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]);
4839                 }
4840         } else {
4841                 fprintf(fp, "1             0  1  0  0              0  0  1  0              0  0  0  1\n");
4842         }
4843         
4844         /*  Atoms  */
4845         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4846                 /*  The 'v' field contains crystallographic coordinates  */
4847                 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);
4848                 if (ap->aniso != NULL) {
4849                         dp = ap->aniso->bij;
4850                         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);
4851                 } else {
4852                         Double temp = ap->tempFactor;
4853                         if (temp <= 0)
4854                                 temp = 1.2;
4855                         fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", temp, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4856                 }
4857         }
4858         /*  Special points  */
4859         {
4860                 Vector camera, lookat, up, xvec, yvec, zvec;
4861                 MainView_getCamera(mp->mview, &camera, &lookat, &up);
4862                 VecSub(zvec, lookat, camera);
4863                 VecCross(xvec, zvec, up);
4864                 NormalizeVec(&xvec, &xvec);
4865                 NormalizeVec(&yvec, &up);
4866                 VecInc(xvec, lookat);
4867                 VecInc(yvec, lookat);
4868                 MoleculeCartesianToXtal(mp, &lookat, &lookat);
4869                 MoleculeCartesianToXtal(mp, &xvec, &xvec);
4870                 MoleculeCartesianToXtal(mp, &yvec, &yvec);
4871                 fprintf(fp, " ORGN                      %9g%9g%9g        0\n", 0.0, 0.0, 0.0);
4872                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4873                 fprintf(fp, " CNTR                      %9g%9g%9g        0\n", lookat.x, lookat.y, lookat.z);
4874                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4875                 fprintf(fp, " X                         %9g%9g%9g        0\n", xvec.x, xvec.y, xvec.z);
4876                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4877                 fprintf(fp, " Y                         %9g%9g%9g        0\n", yvec.x, yvec.y, yvec.z);
4878                 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);
4879         }
4880         
4881         /*  Instructions  */
4882         fprintf(fp, "      201\n");
4883         fprintf(fp, "      205       12\n");
4884         fprintf(fp, "      301      6.6      6.6        0      0.8\n");
4885         sOutputAtomListInstructions(fp, natoms, atoms);
4886         fprintf(fp, "      501%4d55501%4d55501%4d55501%4d55501%4d55501                 1\n", natoms + 2, natoms + 2, natoms + 3, natoms + 2, natoms + 4);
4887         fprintf(fp, "      502        1      0.0        2      0.0        3      0.0\n");
4888         fprintf(fp, "      604                               1.538\n");
4889
4890         sOutputBondInstructions(fp, natoms, atoms, 1);
4891         sOutputAtomTypeInstructions(fp, natoms, atoms);
4892         sOutputBondInstructions(fp, natoms, atoms, 0);
4893
4894         for (i = 0; i < natoms; i++) {
4895                 AtomClean(atoms + i);
4896         }
4897         free(atoms);
4898
4899         fprintf(fp, "      202\n");
4900         fprintf(fp, "  0    -1\n");
4901         fclose(fp);
4902         return 0;
4903 }
4904
4905 void
4906 MoleculeDump(Molecule *mol)
4907 {
4908         int i, j;
4909         Int *cp;
4910         Atom *ap;
4911         for (i = 0; i < mol->natoms; i++) {
4912                 char buf1[8];
4913                 ap = ATOM_AT_INDEX(mol->atoms, i);
4914                 snprintf(buf1, sizeof buf1, "%3.4s.%d", ap->resName, ap->resSeq);
4915                 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);
4916                 cp = AtomConnectData(&ap->connect);
4917                 for (j = 0; j < ap->connect.count; j++) {
4918                         fprintf(stderr, "%s%d", (j > 0 ? "," : ""), cp[j]);
4919                 }
4920                 fprintf(stderr, "]\n");
4921         }
4922 }
4923
4924 #pragma mark ====== MD support (including modification of Molecule) ======
4925
4926 /*  Call md_prepare for the MDArena. If MDArena has not been created, a new arena is created.
4927         If something goes wrong, returns 1 (for missing parameters) or -1 (more serious error).
4928     If retmsg is not NULL, a message describing the problem is returned there. This message
4929     must be free'd by the caller.  */
4930 int
4931 MoleculePrepareMDArena(Molecule *mol, int check_only, char **retmsg)
4932 {
4933         const char *msg;
4934         Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
4935         Int missing = 0;
4936         IntGroup *ig1, *ig2, *ig3;
4937         MDArena *arena = mol->arena;
4938
4939         if (arena == NULL) {
4940                 md_arena_new(mol);
4941                 arena = mol->arena;
4942         } else if (arena->xmol != mol)
4943                 md_arena_set_molecule(arena, mol);
4944
4945         arena->is_initialized = 0;
4946         
4947         /*  Rebuild the tables  */
4948         ig1 = ig2 = ig3 = NULL;
4949         nangles = MoleculeFindMissingAngles(mol, &angles);
4950         ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
4951         nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
4952         if (nangles > 0) {
4953                 ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
4954                 MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
4955                 free(angles);
4956                 IntGroupRelease(ig1);
4957         }
4958         if (ndihedrals > 0) {
4959                 ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
4960                 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
4961                 free(dihedrals);
4962                 IntGroupRelease(ig2);
4963         }
4964         if (nimpropers > 0) {
4965                 ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
4966                 MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
4967                 free(impropers);
4968                 IntGroupRelease(ig3);
4969         }
4970         
4971         {
4972                 /*  Update the path information of the molecule before MD setup  */
4973                 char *buf = (char *)malloc(4096);
4974                 MoleculeCallback_pathName(mol, buf, sizeof buf);
4975                 MoleculeSetPath(mol, buf);
4976                 free(buf);
4977         }
4978                 
4979         /*  Prepare parameters and internal information  */
4980         msg = md_prepare(arena, check_only);
4981         
4982         /*  Some parameters are missing?  */
4983         if (msg != NULL) {
4984                 if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
4985                         missing = 1;
4986                 else {
4987                         if (retmsg != NULL)
4988                                 asprintf(retmsg, "cannot initialize for MD: %s", msg);
4989                         return -1;
4990                 }
4991         }
4992         
4993         /*  The local parameter list is updated  */
4994         {
4995                 Int parType, idx;
4996                 if (mol->par == NULL)
4997                         mol->par = ParameterNew();
4998                 for (parType = kFirstParType; parType <= kLastParType; parType++) {
4999                         /*  Delete global and undefined parameters  */
5000                         UnionPar *up, *upbuf;
5001                         Int nparams, count;
5002                         ig1 = IntGroupNew();
5003                         for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
5004                                 if (up->bond.src != 0)
5005                                         IntGroupAdd(ig1, idx, 1);
5006                         }
5007                         if (IntGroupGetCount(ig1) > 0)
5008                                 MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
5009                         IntGroupRelease(ig1);
5010                         /*  Copy global and undefined parameters from arena and insert to mol->par  */
5011                         nparams = ParameterGetCountForType(arena->par, parType);
5012                         if (nparams == 0)
5013                                 continue;
5014                         upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
5015                         ig1 = IntGroupNew();
5016                         ig2 = IntGroupNew();
5017                         for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
5018                                 if (up->bond.src > 0)
5019                                         IntGroupAdd(ig1, idx, 1); /* Global parameter */
5020                                 else if (up->bond.src < 0)
5021                                         IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
5022                         }
5023                         if ((count = IntGroupGetCount(ig1)) > 0) {
5024                                 /*  Insert global parameters (at the top)  */
5025                                 ParameterCopy(arena->par, parType, upbuf, ig1);
5026                                 ig3 = IntGroupNewWithPoints(0, count, -1);
5027                                 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5028                                 IntGroupRelease(ig3);
5029                         }
5030                         if ((count = IntGroupGetCount(ig2)) > 0) {
5031                                 /*  Insert undefined parameters (at the bottom)  */
5032                                 ParameterCopy(arena->par, parType, upbuf, ig2);
5033                                 idx = ParameterGetCountForType(mol->par, parType);
5034                                 ig3 = IntGroupNewWithPoints(idx, count, -1);
5035                                 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5036                                 IntGroupRelease(ig3);
5037                         }
5038                         IntGroupRelease(ig2);
5039                         IntGroupRelease(ig1);
5040                         free(upbuf);
5041                 }
5042                 mol->needsMDRebuild = 0;  /*  We know the "modified" parameters are consistent with the MDArena  */
5043         }
5044         
5045         if (missing) {
5046                 if (retmsg != NULL)
5047                         *retmsg = strdup(msg);
5048                 return 1;
5049         } else return 0;
5050 }
5051
5052 #pragma mark ====== Serialize ======
5053
5054 Molecule *
5055 MoleculeDeserialize(const char *data, Int length, Int *timep)
5056 {
5057         Molecule *mp;
5058         Parameter *par;
5059         Atom *ap;
5060 /*      int result; */
5061
5062         mp = MoleculeNew();
5063         if (mp == NULL)
5064                 goto out_of_memory;
5065         par = ParameterNew();
5066         if (par == NULL)
5067                 goto out_of_memory;
5068
5069         while (length >= 12) {
5070                 const char *ptr = data + 8 + sizeof(Int);
5071                 int len = *((const Int *)(data + 8));
5072                 int i, j, n;
5073                 if (strcmp(data, "ATOM") == 0) {
5074                         n = len / gSizeOfAtomRecord;
5075                         NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, n);
5076                         memmove(mp->atoms, ptr, len);
5077                 } else if (strcmp(data, "ANISO") == 0) {
5078                         n = len / (sizeof(Int) + sizeof(Aniso));
5079                         for (i = 0; i < n; i++) {
5080                                 j = *((const Int *)ptr);
5081                                 if (j < 0 || j >= mp->natoms)
5082                                         goto bad_format;
5083                                 ap = ATOM_AT_INDEX(mp->atoms, j);
5084                                 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
5085                                 if (ap->aniso == NULL)
5086                                         goto out_of_memory;
5087                                 *(ap->aniso) = *((Aniso *)(ptr + sizeof(Int)));
5088                                 ptr += sizeof(Int) + sizeof(Aniso);
5089                         }
5090                 } else if (strcmp(data, "FRAME") == 0) {
5091                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5092                                 if (ap->nframes == 0)
5093                                         continue;
5094                                 ap->frames = (Vector *)malloc(sizeof(Vector) * ap->nframes);
5095                                 if (ap->frames == NULL)
5096                                         goto out_of_memory;
5097                                 memmove(ap->frames, ptr, sizeof(Vector) * ap->nframes);
5098                                 ptr += sizeof(Vector) * ap->nframes;
5099                         }
5100                 } else if (strcmp(data, "EXTCON") == 0) {
5101                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5102                                 if (ap->connect.count <= ATOM_CONNECT_LIMIT)
5103                                         continue;
5104                                 n = ap->connect.count;
5105                                 ap->connect.count = 0;
5106                                 ap->connect.u.ptr = NULL;
5107                                 NewArray(&(ap->connect.u.ptr), &(ap->connect.count), sizeof(Int), n);
5108                                 memmove(ap->connect.u.ptr, ptr, sizeof(Int) * n);
5109                                 ptr += sizeof(Int) * n;
5110                         }
5111                 } else if (strcmp(data, "BOND") == 0) {
5112                         n = len / (sizeof(Int) * 2);
5113                         NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, n);
5114                         memmove(mp->bonds, ptr, len);
5115                 } else if (strcmp(data, "ANGLE") == 0) {
5116                         n = len / (sizeof(Int) * 3);
5117                         NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, n);
5118                         memmove(mp->angles, ptr, len);
5119                 } else if (strcmp(data, "DIHED") == 0) {
5120                         n = len / (sizeof(Int) * 4);
5121                         NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, n);
5122                         memmove(mp->dihedrals, ptr, len);
5123                 } else if (strcmp(data, "IMPROP") == 0) {
5124                         n = len / (sizeof(Int) * 4);
5125                         NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, n);
5126                         memmove(mp->impropers, ptr, len);
5127                 } else if (strcmp(data, "RESIDUE") == 0) {
5128                         n = len / 4;
5129                         NewArray(&mp->residues, &mp->nresidues, 4, n);
5130                         memmove(mp->residues, ptr, len);
5131                 } else if (strcmp(data, "CELL") == 0) {
5132                         mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
5133                         if (mp->cell == NULL)
5134                                 goto out_of_memory;
5135                         memmove(mp->cell, ptr, sizeof(XtalCell));
5136                 } else if (strcmp(data, "SYMOP") == 0) {
5137                         n = len / sizeof(Transform);
5138                         NewArray(&mp->syms, &mp->nsyms, sizeof(Transform), n);
5139                         memmove(mp->syms, ptr, len);
5140                 } else if (strcmp(data, "ANCHOR") == 0) {
5141                         const char *ptr2 = ptr + len;
5142                         while (ptr < ptr2) {
5143                                 PiAnchor an;
5144                                 memset(&an, 0, sizeof(an));
5145                                 i = *((Int *)ptr);
5146                                 if (i >= 0 && i < mp->natoms) {
5147                                         n = *((Int *)(ptr + sizeof(Int)));
5148                                         AtomConnectResize(&(an.connect), n);
5149                                         memmove(AtomConnectData(&(an.connect)), ptr + sizeof(Int) * 2, sizeof(Int) * n);
5150                                         NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), n);
5151                                         memmove(an.coeffs, ptr + sizeof(Int) * (2 + n), sizeof(Double) * n);
5152                                         ap = ATOM_AT_INDEX(mp->atoms, i);
5153                                         ap->anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
5154                                         memmove(ap->anchor, &an, sizeof(PiAnchor));
5155                                 }
5156                                 ptr += sizeof(Int) * (2 + n) + sizeof(Double) * n;
5157                         }
5158                 } else if (strcmp(data, "TIME") == 0) {
5159                         if (timep != NULL)
5160                                 *timep = *((Int *)ptr);
5161                 } else if (strcmp(data, "BONDPAR") == 0) {
5162                         mp->par = par;
5163                         n = len / sizeof(BondPar);
5164                         NewArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), n);
5165                         memmove(par->bondPars, ptr, len);
5166                 } else if (strcmp(data, "ANGPAR") == 0) {
5167                         mp->par = par;
5168                         n = len / sizeof(AnglePar);
5169                         NewArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), n);
5170                         memmove(par->anglePars, ptr, len);
5171                 } else if (strcmp(data, "DIHEPAR") == 0) {
5172                         mp->par = par;
5173                         n = len / sizeof(TorsionPar);
5174                         NewArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), n);
5175                         memmove(par->dihedralPars, ptr, len);
5176                 } else if (strcmp(data, "IMPRPAR") == 0) {
5177                         mp->par = par;
5178                         n = len / sizeof(TorsionPar);
5179                         NewArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), n);
5180                         memmove(par->improperPars, ptr, len);
5181                 } else if (strcmp(data, "VDWPAR") == 0) {
5182                         mp->par = par;
5183                         n = len / sizeof(VdwPar);
5184                         NewArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), n);
5185                         memmove(par->vdwPars, ptr, len);
5186                 } else if (strcmp(data, "VDWPPAR") == 0) {
5187                         mp->par = par;
5188                         n = len / sizeof(VdwPairPar);
5189                         NewArray(&par->vdwpPars, &par->nvdwpPars, sizeof(VdwPairPar), n);
5190                         memmove(par->vdwpPars, ptr, len);
5191                 } else if (strcmp(data, "VCUTPAR") == 0) {
5192                         mp->par = par;
5193                         n = len / sizeof(VdwCutoffPar);
5194                         NewArray(&par->vdwCutoffPars, &par->nvdwCutoffPars, sizeof(VdwCutoffPar), n);
5195                         memmove(par->vdwCutoffPars, ptr, len);
5196                 }
5197                 len += 8 + sizeof(Int);
5198                 data += len;
5199                 length -= len;
5200         }
5201         if (mp->par == NULL)
5202                 ParameterRelease(par);
5203 /*      result = MoleculeRebuildTablesFromConnects(mp);
5204         if (result != 0)
5205                 goto bad_format; */
5206         return mp;
5207         
5208   out_of_memory:
5209         Panic("Low memory while deserializing molecule data");
5210         return NULL; /* Not reached */
5211
5212   bad_format:
5213         Panic("internal error: bad format during deserializing molecule data");
5214         return NULL; /* Not reached */
5215 }
5216
5217 char *
5218 MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep)
5219 {
5220         char *ptr, *p;
5221         int len, len_all, i, naniso, nframes, nconnects, nanchors;
5222         Atom *ap;
5223
5224         /*  Array of atoms  */
5225         len = 8 + sizeof(Int) + gSizeOfAtomRecord * mp->natoms;
5226         ptr = (char *)malloc(len);
5227         if (ptr == NULL)
5228                 goto out_of_memory;
5229         memmove(ptr, "ATOM\0\0\0\0", 8);
5230         *((Int *)(ptr + 8)) = gSizeOfAtomRecord * mp->natoms;
5231         p = ptr + 8 + sizeof(Int);
5232         memmove(p, mp->atoms, gSizeOfAtomRecord * mp->natoms);
5233         naniso = nframes = nconnects = nanchors = 0;
5234         for (i = 0; i < mp->natoms; i++) {
5235                 ap = ATOM_AT_INDEX(p, i);
5236                 if (ap->aniso != NULL) {
5237                         naniso++;
5238                         ap->aniso = NULL;
5239                 }
5240                 if (ap->frames != NULL) {
5241                         nframes += ap->nframes;
5242                         ap->frames = NULL;
5243                 }
5244                 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5245                         nconnects += ap->connect.count;
5246                         ap->connect.u.ptr = NULL;
5247                 }
5248                 if (ap->anchor != NULL) {
5249                         nanchors++;
5250                         ap->anchor = NULL;
5251                 }
5252         }
5253         len_all = len;
5254
5255         /*  Array of aniso  */
5256         if (naniso > 0) {
5257                 len = 8 + sizeof(Int) + (sizeof(Int) + sizeof(Aniso)) * naniso;
5258                 ptr = (char *)realloc(ptr, len_all + len);
5259                 if (ptr == NULL)
5260                         goto out_of_memory;
5261                 p = ptr + len_all;
5262                 memmove(p, "ANISO\0\0\0", 8);
5263                 *((Int *)(p + 8)) = (sizeof(Int) + sizeof(Aniso)) * naniso;
5264                 p += 8 + sizeof(Int);
5265                 for (i = 0; i < mp->natoms; i++) {
5266                         ap = ATOM_AT_INDEX(mp->atoms, i);
5267                         if (ap->aniso != NULL) {
5268                                 *((Int *)p) = i;
5269                                 *((Aniso *)(p + sizeof(Int))) = *(ap->aniso);
5270                                 p += sizeof(Int) + sizeof(Aniso);
5271                         }
5272                 }
5273                 len_all += len;
5274         }
5275         
5276         /*  Array of frames  */
5277         if (nframes > 0) {
5278                 len = 8 + sizeof(Int) + sizeof(Vector) * nframes;
5279                 ptr = (char *)realloc(ptr, len_all + len);
5280                 if (ptr == NULL)
5281                         goto out_of_memory;
5282                 p = ptr + len_all;
5283                 memmove(p, "FRAME\0\0\0", 8);
5284                 *((Int *)(p + 8)) = sizeof(Vector) * nframes;
5285                 p += 8 + sizeof(Int);
5286                 for (i = 0; i < mp->natoms; i++) {
5287                         ap = ATOM_AT_INDEX(mp->atoms, i);
5288                         if (ap->frames != NULL) {
5289                                 memmove(p, ap->frames, sizeof(Vector) * ap->nframes);
5290                                 p += sizeof(Vector) * ap->nframes;
5291                         }
5292                 }
5293                 len_all += len;
5294         }
5295         
5296         /*  Array of connects  */
5297         if (nconnects > 0) {
5298                 len = 8 + sizeof(Int) + sizeof(Int) * nconnects;
5299                 ptr = (char *)realloc(ptr, len_all + len);
5300                 if (ptr == NULL)
5301                         goto out_of_memory;
5302                 p = ptr + len_all;
5303                 memmove(p, "EXTCON\0\0", 8);
5304                 *((Int *)(p + 8)) = sizeof(Int) * nconnects;
5305                 p += 8 + sizeof(Int);
5306                 for (i = 0; i < mp->natoms; i++) {
5307                         ap = ATOM_AT_INDEX(mp->atoms, i);
5308                         if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5309                                 memmove(p, ap->connect.u.ptr, sizeof(Int) * ap->connect.count);
5310                                 p += sizeof(Int) * ap->connect.count;
5311                         }
5312                 }
5313                 len_all += len;
5314         }
5315         
5316         /*  Bonds, angles, dihedrals, impropers  */
5317         if (mp->nbonds > 0) {
5318                 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nbonds;
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, "BOND\0\0\0\0", 8);
5324                 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nbonds;
5325                 p += 8 + sizeof(Int);
5326                 memmove(p, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
5327                 len_all += len;
5328         }
5329         if (mp->nangles > 0) {
5330                 len = 8 + sizeof(Int) + sizeof(Int) * 3 * mp->nangles;
5331                 ptr = (char *)realloc(ptr, len_all + len);
5332                 if (ptr == NULL)
5333                         goto out_of_memory;
5334                 p = ptr + len_all;
5335                 memmove(p, "ANGLE\0\0\0", 8);
5336                 *((Int *)(p + 8)) = sizeof(Int) * 3 * mp->nangles;
5337                 p += 8 + sizeof(Int);
5338                 memmove(p, mp->angles, sizeof(Int) * 3 * mp->nangles);
5339                 len_all += len;
5340         }
5341         if (mp->ndihedrals > 0) {
5342                 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->ndihedrals;
5343                 ptr = (char *)realloc(ptr, len_all + len);
5344                 if (ptr == NULL)
5345                         goto out_of_memory;
5346                 p = ptr + len_all;
5347                 memmove(p, "DIHED\0\0\0", 8);
5348                 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->ndihedrals;
5349                 p += 8 + sizeof(Int);
5350                 memmove(p, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
5351                 len_all += len;
5352         }
5353         if (mp->nimpropers > 0) {
5354                 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->nimpropers;
5355                 ptr = (char *)realloc(ptr, len_all + len);
5356                 if (ptr == NULL)
5357                         goto out_of_memory;
5358                 p = ptr + len_all;
5359                 memmove(p, "IMPROP\0\0", 8);
5360                 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->nimpropers;
5361                 p += 8 + sizeof(Int);
5362                 memmove(p, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
5363                 len_all += len;
5364         }
5365         
5366         /*  Array of residues  */
5367         if (mp->nresidues > 0) {
5368                 len = 8 + sizeof(Int) + 4 * mp->nresidues;
5369                 ptr = (char *)realloc(ptr, len_all + len);
5370                 if (ptr == NULL)
5371                         goto out_of_memory;
5372                 p = ptr + len_all;
5373                 memmove(p, "RESIDUE\0", 8);
5374                 *((Int *)(p + 8)) = 4 * mp->nresidues;
5375                 p += 8 + sizeof(Int);
5376                 memmove(p, mp->residues, 4 * mp->nresidues);
5377                 len_all += len;
5378         }
5379
5380         /*  Unit cell  */
5381         if (mp->cell != NULL) {
5382                 len = 8 + sizeof(Int) + sizeof(XtalCell);
5383                 ptr = (char *)realloc(ptr, len_all + len);
5384                 if (ptr == NULL)
5385                         goto out_of_memory;
5386                 p = ptr + len_all;
5387                 memmove(p, "CELL\0\0\0\0", 8);
5388                 *((Int *)(p + 8)) = sizeof(XtalCell);
5389                 p += 8 + sizeof(Int);
5390                 memmove(p, mp->cell, sizeof(XtalCell));
5391                 len_all += len;
5392         }
5393         
5394         /*  Symmetry operations  */
5395         if (mp->nsyms > 0) {
5396                 len = 8 + sizeof(Int) + sizeof(Transform) * mp->nsyms;
5397                 ptr = (char *)realloc(ptr, len_all + len);
5398                 if (ptr == NULL)
5399                         goto out_of_memory;
5400                 p = ptr + len_all;
5401                 memmove(p, "SYMOP\0\0\0", 8);
5402                 *((Int *)(p + 8)) = sizeof(Transform) * mp->nsyms;
5403                 p += 8 + sizeof(Int);
5404                 memmove(p, mp->syms, sizeof(Transform) * mp->nsyms);
5405                 len_all += len;
5406         }
5407         
5408         /*  Pi-anchors  */
5409         if (nanchors > 0) {
5410                 /*  Estimate the necessary storage first  */
5411                 /*  One entry consists of { atom_index (Int), number_of_connects (Int), connects (Int's), weights (Double's) }  */
5412                 len = 8 + sizeof(Int);
5413                 for (i = 0; i < mp->natoms; i++) {
5414                         ap = ATOM_AT_INDEX(mp->atoms, i);
5415                         if (ap->anchor != NULL)
5416                                 len += sizeof(Int) * 2 + (sizeof(Int) + sizeof(Double)) * ap->anchor->connect.count;
5417                 }
5418                 ptr = (char *)realloc(ptr, len_all + len);
5419                 if (ptr == NULL)
5420                         goto out_of_memory;
5421                 p = ptr + len_all;
5422                 memmove(p, "ANCHOR\0\0", 8);
5423                 *((Int *)(p + 8)) = len - (8 + sizeof(Int));
5424                 p += 8 + sizeof(Int);
5425                 for (i = 0; i < mp->natoms; i++) {
5426                         Int count, *ip;
5427                         ap = ATOM_AT_INDEX(mp->atoms, i);
5428                         if (ap->anchor != NULL) {
5429                                 count = ap->anchor->connect.count;
5430                                 *((Int *)p) = i;
5431                                 *((Int *)(p + sizeof(Int))) = count;
5432                                 p += sizeof(Int) * 2;
5433                                 ip = AtomConnectData(&(ap->anchor->connect));
5434                                 memmove(p, ip, sizeof(Int) * count);
5435                                 p += sizeof(Int) * count;
5436                                 memmove(p, ap->anchor->coeffs, sizeof(Double) * count);
5437                                 p += sizeof(Double) * count;
5438                         }
5439                 }
5440                 len_all += len;
5441         }
5442         
5443         /*  Parameters  */
5444         if (mp->par != NULL) {
5445                 int type;
5446                 for (type = kFirstParType; type <= kLastParType; type++) {
5447                         const char *parname;
5448                         Int parsize, parcount;
5449                         void *parptr;
5450                         switch (type) {
5451                                 case kBondParType:
5452                                         parname = "BONDPAR\0";
5453                                         parsize = sizeof(BondPar);
5454                                         parcount = mp->par->nbondPars;
5455                                         parptr = mp->par->bondPars;
5456                                         break;
5457                                 case kAngleParType:
5458                                         parname = "ANGPAR\0\0";
5459                                         parsize = sizeof(AnglePar);
5460                                         parcount = mp->par->nanglePars;
5461                                         parptr = mp->par->anglePars;
5462                                         break;
5463                                 case kDihedralParType:
5464                                         parname = "DIHEPAR\0";
5465                                         parsize = sizeof(TorsionPar);
5466                                         parcount = mp->par->ndihedralPars;
5467                                         parptr = mp->par->dihedralPars;
5468                                         break;
5469                                 case kImproperParType:
5470                                         parname = "IMPRPAR\0";
5471                                         parsize = sizeof(TorsionPar);
5472                                         parcount = mp->par->nimproperPars;
5473                                         parptr = mp->par->improperPars;
5474                                         break;
5475                                 case kVdwParType:
5476                                         parname = "VDWPAR\0\0";
5477                                         parsize = sizeof(VdwPar);
5478                                         parcount = mp->par->nvdwPars;
5479                                         parptr = mp->par->vdwPars;
5480                                         break;
5481                                 case kVdwPairParType:
5482                                         parname = "VDWPPAR\0";
5483                                         parsize = sizeof(VdwPairPar);
5484                                         parcount = mp->par->nvdwpPars;
5485                                         parptr = mp->par->vdwpPars;
5486                                         break;
5487                                 case kVdwCutoffParType:
5488                                         parname = "VCUTPAR\0";
5489                                         parsize = sizeof(VdwCutoffPar);
5490                                         parcount = mp->par->nvdwCutoffPars;
5491                                         parptr = mp->par->vdwCutoffPars;
5492                                         break;
5493                                 default:
5494                                         continue;
5495                         }
5496                         if (parcount > 0) {
5497                                 len = 8 + sizeof(Int) + parsize * parcount;
5498                                 ptr = (char *)realloc(ptr, len_all + len);
5499                                 if (ptr == NULL)
5500                                         goto out_of_memory;
5501                                 p = ptr + len_all;
5502                                 memmove(p, parname, 8);
5503                                 *((Int *)(p + 8)) = parsize * parcount;
5504                                 p += 8 + sizeof(Int);
5505                                 memmove(p, parptr, parsize * parcount);
5506                                 len_all += len;
5507                         }
5508                 }
5509         }
5510         
5511         /*  Time stamp  */
5512         {
5513                 time_t tm = time(NULL);
5514                 len = 8 + sizeof(Int) + sizeof(Int);
5515                 ptr = (char *)realloc(ptr, len_all + len);
5516                 if (ptr == NULL)
5517                         goto out_of_memory;
5518                 p = ptr + len_all;
5519                 memmove(p, "TIME\0\0\0\0", 8);
5520                 *((Int *)(p + 8)) = sizeof(Int);
5521                 p += 8 + sizeof(Int);
5522                 *((Int *)p) = (Int)tm;
5523                 len_all += len;
5524                 if (timep != NULL)
5525                         *timep = (Int)tm;
5526         }
5527         
5528         if (outLength != NULL)
5529                 *outLength = len_all;
5530         return ptr;
5531
5532   out_of_memory:
5533     Panic("Low memory while serializing a molecule data");
5534         return NULL; /* Not reached */  
5535 }
5536
5537 #pragma mark ====== Search for bonds, angles, dihedrals, impropers ======
5538
5539 static IntGroup *
5540 sMoleculeSearchIncludingAtoms(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5541 {
5542         int i, j;
5543         Int *ip;
5544         IntGroup *gp = NULL;
5545         if (atomgroup == NULL)
5546                 return NULL;
5547         for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5548                 for (j = 0; j < nsize; j++) {
5549                         if (IntGroupLookup(atomgroup, ip[j], NULL) != 0) {
5550                                 if (gp == NULL)
5551                                         gp = IntGroupNew();
5552                                 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5553                                         Panic("Low memory while searching %s", msg);
5554                                 break;
5555                         }
5556                 }
5557         }
5558         return gp;
5559 }
5560
5561 IntGroup *
5562 MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5563 {
5564         if (mp == NULL)
5565                 return NULL;
5566         return sMoleculeSearchIncludingAtoms(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5567 }
5568
5569 IntGroup *
5570 MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5571 {
5572         if (mp == NULL)
5573                 return NULL;
5574         return sMoleculeSearchIncludingAtoms(mp->nangles, mp->angles, 3, atomgroup, "angles");
5575 }
5576
5577 IntGroup *
5578 MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5579 {
5580         if (mp == NULL)
5581                 return NULL;
5582         return sMoleculeSearchIncludingAtoms(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5583 }
5584
5585 IntGroup *
5586 MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5587 {
5588         if (mp == NULL)
5589                 return NULL;
5590         return sMoleculeSearchIncludingAtoms(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5591 }
5592
5593 static IntGroup *
5594 sMoleculeSearchAcrossAtomGroup(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5595 {
5596         int i, j;
5597         Int *ip;
5598         IntGroup *gp = NULL;
5599         if (atomgroup == NULL)
5600                 return NULL;
5601         for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5602                 int k = -1;
5603                 for (j = 0; j < nsize; j++) {
5604                         int kk;
5605                         kk = (IntGroupLookup(atomgroup, ip[j], NULL) != 0);
5606                         if (k < 0)
5607                                 k = kk;
5608                         else if (k != kk) {
5609                                 /*  This bond etc. crosses the atom group border  */
5610                                 if (gp == NULL)
5611                                         gp = IntGroupNew();
5612                                 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5613                                         Panic("Low memory while searching %s", msg);
5614                                 break;
5615                         }
5616                 }
5617         }
5618         return gp;
5619 }
5620
5621 IntGroup *
5622 MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5623 {
5624         if (mp == NULL)
5625                 return NULL;
5626         return sMoleculeSearchAcrossAtomGroup(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5627 }
5628
5629 IntGroup *
5630 MoleculeSearchAnglesAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5631 {
5632         if (mp == NULL)
5633                 return NULL;
5634         return sMoleculeSearchAcrossAtomGroup(mp->nangles, mp->angles, 3, atomgroup, "angles");
5635 }
5636
5637 IntGroup *
5638 MoleculeSearchDihedralsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5639 {
5640         if (mp == NULL)
5641                 return NULL;
5642         return sMoleculeSearchAcrossAtomGroup(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5643 }
5644
5645 IntGroup *
5646 MoleculeSearchImpropersAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5647 {
5648         if (mp == NULL)
5649                 return NULL;
5650         return sMoleculeSearchAcrossAtomGroup(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5651 }
5652
5653 /*  Subroutine for MoleculeGuessBonds. It can be also used independently, but make sure that *outNbonds/*outBonds 
5654     _correctly_ represents an array of two integers (as in mp->nbonds/mp->bonds).  */
5655 /*  Find atoms within the given "distance" from the given position.  */
5656 /*  If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5657  the threshold distance is given by the sum of van der Waals radii times limit, and radius is
5658  the van der Waals radius of the atom at the given position. */
5659 /*  Index is the atom index of the given atom; it is only used in returning the "bond" array
5660  to the caller. If index is negative, then (-index) is the real atom index, and
5661  only atoms with lower indices than (-index) are looked for.  */
5662 int
5663 MoleculeFindCloseAtoms(Molecule *mp, const Vector *vp, Double radius, Double limit, Int *outNbonds, Int **outBonds, Int index)
5664 {
5665         Int n2, j, nlim, newbond[2];
5666         Double a2, alim;
5667         Vector dr, r2;
5668         if (index < 0) {
5669                 nlim = index = -index;
5670         } else {
5671                 nlim = mp->natoms;
5672         }
5673         for (j = 0; j < nlim; j++) {
5674                 Atom *bp = ATOM_AT_INDEX(mp->atoms, j);
5675                 if (index == j)
5676                         continue;
5677                 n2 = bp->atomicNumber;
5678                 if (n2 >= 0 && n2 < gCountElementParameters)
5679                         a2 = gElementParameters[n2].radius;
5680                 else a2 = gElementParameters[6].radius;
5681                 r2 = bp->r;
5682                 VecSub(dr, *vp, r2);
5683                 if (limit < 0)
5684                         alim = -limit;
5685                 else
5686                         alim = limit * (radius + a2);
5687                 if (VecLength2(dr) < alim * alim) {
5688                         newbond[0] = index;
5689                         newbond[1] = j;
5690                         /*      MoleculeAddBonds(mp, 1, newbonds); */
5691                         AssignArray(outBonds, outNbonds, sizeof(Int) * 2, *outNbonds, newbond);
5692                 }
5693         }
5694         return 0;
5695 }
5696
5697 /*  Guess the bonds from the coordinates  */
5698 /*  If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5699     the threshold distance is given by the sum of van der Waals radii times limit.  */
5700 int
5701 MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds)
5702 {
5703         Int nbonds, *bonds, i, newbond[2];
5704         Atom *ap;
5705         nbonds = 0;
5706         bonds = NULL;
5707         if (limit == 0.0)
5708                 limit = 1.2;
5709         for (i = 1, ap = ATOM_NEXT(mp->atoms); i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5710                 Vector r = ap->r;
5711                 Int an = ap->atomicNumber;
5712                 Double rad;
5713                 if (an >= 0 && an < gCountElementParameters)
5714                         rad = gElementParameters[an].radius;
5715                 else rad = gElementParameters[6].radius;
5716                 MoleculeFindCloseAtoms(mp, &r, rad, limit, &nbonds, &bonds, -i);
5717         }
5718         if (nbonds > 0) {
5719                 newbond[0] = kInvalidIndex;
5720                 newbond[1] = 0;
5721                 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5722                 nbonds--;
5723         }
5724         if (outNbonds != NULL)
5725                 *outNbonds = nbonds;
5726         if (outBonds != NULL)
5727                 *outBonds = bonds;
5728         return 0;
5729 }
5730
5731 /*  Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information  */
5732 int
5733 MoleculeRebuildTablesFromConnects(Molecule *mp)
5734 {
5735         int i, j, k, retval;
5736         Atom *ap;
5737         Int ibuf[6], *cp;
5738         
5739         __MoleculeLock(mp);
5740
5741         /*  Find bonds   */
5742         if (mp->nbonds == 0) {
5743                 for (i = 0; i < mp->natoms; i++) {
5744                         ap = ATOM_AT_INDEX(mp->atoms, i);
5745                         cp = AtomConnectData(&ap->connect);
5746                         for (j = 0; j < ap->connect.count; j++) {
5747                                 k = cp[j];
5748                                 if (i >= k)
5749                                         continue;
5750                                 ibuf[0] = i;
5751                                 ibuf[1] = k;
5752                                 /*  MoleculeAddBonds() should not be used, because it assumes connects[] and
5753                                     bonds are already in sync  */
5754                                 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
5755                         /*      retval = MoleculeAddBonds(mp, 1, ibuf);
5756                                 if (retval != 0)
5757                                         goto abort; */
5758                         }
5759                 }
5760         }
5761         
5762         /*  Find angles  */
5763         if (mp->nangles == 0) {
5764                 for (i = 0; i < mp->natoms; i++) {
5765                         ap = ATOM_AT_INDEX(mp->atoms, i);
5766                         cp = AtomConnectData(&ap->connect);
5767                         for (j = 0; j < ap->connect.count; j++) {
5768                                 for (k = j + 1; k < ap->connect.count; k++) {
5769                                         ibuf[0] = cp[j];
5770                                         ibuf[1] = i;
5771                                         ibuf[2] = cp[k];
5772                                         ibuf[3] = -1;
5773                                         retval = MoleculeAddAngles(mp, ibuf, NULL);
5774                                         if (retval < 0)
5775                                                 goto abort;
5776                                 }
5777                         }
5778                 }
5779         }
5780         
5781         /*  Find dihedrals  */
5782         if (mp->ndihedrals == 0) {
5783                 for (i = 0; i < mp->natoms; i++) {
5784                         ap = ATOM_AT_INDEX(mp->atoms, i);
5785                         cp = AtomConnectData(&ap->connect);
5786                         for (j = 0; j < ap->connect.count; j++) {
5787                                 int jj, kk, mm, m;
5788                                 Atom *apjj;
5789                                 Int *cpjj;
5790                                 jj = cp[j];
5791                                 if (i >= jj)
5792                                         continue;
5793                                 apjj = ATOM_AT_INDEX(mp->atoms, jj);
5794                                 cpjj = AtomConnectData(&apjj->connect);
5795                                 for (k = 0; k < ap->connect.count; k++) {
5796                                         if (k == j)
5797                                                 continue;
5798                                         kk = cp[k];
5799                                         for (m = 0; m < apjj->connect.count; m++) {
5800                                                 mm = cpjj[m];
5801                                                 if (mm == i || mm == kk)
5802                                                         continue;
5803                                                 ibuf[0] = kk;
5804                                                 ibuf[1] = i;
5805                                                 ibuf[2] = jj;
5806                                                 ibuf[3] = mm;
5807                                                 ibuf[4] = -1;
5808                                                 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
5809                                                 if (retval < 0)
5810                                                         goto abort;
5811                                         }
5812                                 }
5813                         }
5814                 }
5815         }
5816         
5817         /*  Find impropers  */
5818         if (mp->nimpropers == 0) {
5819                 for (i = 0; i < mp->natoms; i++) {
5820                         int i1, i2, i4, n1, n2, n4;
5821                         ap = ATOM_AT_INDEX(mp->atoms, i);
5822                         cp = AtomConnectData(&ap->connect);
5823                         for (i1 = 0; i1 < ap->connect.count; i1++) {
5824                                 n1 = cp[i1];
5825                                 for (i2 = i1 + 1; i2 < ap->connect.count; i2++) {
5826                                         n2 = cp[i2];
5827                                         for (i4 = i2 + 1; i4 < ap->connect.count; i4++) {
5828                                                 n4 = cp[i4];
5829                                                 ibuf[0] = n1;
5830                                                 ibuf[1] = n2;
5831                                                 ibuf[2] = i;
5832                                                 ibuf[3] = n4;
5833                                                 ibuf[4] = -1;
5834                                                 retval = MoleculeAddImpropers(mp, ibuf, NULL);
5835                                                 if (retval < 0)
5836                                                         goto abort;
5837                                         }
5838                                 }
5839                         }
5840                 }
5841         }
5842
5843         mp->needsMDRebuild = 1;
5844         __MoleculeUnlock(mp);
5845         return 0;
5846
5847   abort:
5848         __MoleculeUnlock(mp);
5849         return retval;
5850 }
5851
5852 int
5853 MoleculeAreAtomsConnected(Molecule *mol, int idx1, int idx2)
5854 {
5855         Atom *ap1 = ATOM_AT_INDEX(mol->atoms, idx1);
5856         if (AtomConnectHasEntry(&ap1->connect, idx2))
5857                 return 1;
5858         else if (ap1->anchor != NULL && AtomConnectHasEntry(&(ap1->anchor->connect), idx2))
5859                 return 2;
5860         else return 0;
5861 }
5862
5863 #pragma mark ====== Atom names ======
5864
5865 /*  Look for the n1-th atom in resno-th residue (n1 is 0-based)  */
5866 int
5867 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
5868 {
5869         int i, j, lasti;
5870         Atom *ap;
5871         if (mp == NULL || mp->natoms == 0)
5872                 return -1;
5873         lasti = -1;
5874         for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5875                 if (ap->resSeq == resno) {
5876                         lasti = i;
5877                         if (j++ == n1)
5878                                 return i;
5879                 }
5880         }
5881         if (n1 == -1)
5882                 return lasti; /* max */
5883         return -1;
5884 }
5885
5886 int
5887 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
5888 {
5889     int n;
5890     char *p;
5891         n = strtol(s, &p, 0);
5892         if (p > s) {
5893                 while (isspace(*p))
5894                         p++;
5895                 if (*p == 0) {
5896                   resName[0] = 0;
5897                   *resSeq = -1;
5898                   atomName[0] = 0;
5899                   return n;
5900                 }
5901         }
5902
5903         if ((p = strchr(s, ':')) != NULL) {
5904                 /*  Residue is specified  */
5905                 char *pp;
5906                 if ((pp = strchr(s, '.')) != NULL && pp < p) {
5907                         /*  Residue number is also specified  */
5908                         char *ppp;
5909                         n = pp - s;
5910                         *resSeq = strtol(pp + 1, &ppp, 0);
5911                         if (ppp == pp + 1)
5912                                 return -2;  /*  Bad format  */
5913                         while (isspace(*ppp))
5914                                 ppp++;
5915                         if (ppp != p)
5916                                 return -2;  /*  Bad format  */
5917                 } else {
5918                         *resSeq = -1;
5919                         /*  Check whether the "residue name" is an integer  */
5920                         n = strtol(s, &pp, 0);
5921                         if (pp > s) {
5922                                 while (isspace(*pp))
5923                                         pp++;
5924                                 if (*pp == 0 || *pp == ':') {
5925                                         *resSeq = n;
5926                                         if (*resSeq < 0)
5927                                                 return -2;  /*  Bad format  */
5928                                 }
5929                         }
5930                         if (*resSeq >= 0)
5931                                 n = 0;
5932                         else
5933                                 n = p - s;
5934                 }
5935                 if (n >= sizeof(resName))
5936                         n = sizeof(resName) - 1;
5937                 strncpy(resName, s, n);
5938                 resName[n] = 0;
5939                 p++;
5940         } else {
5941                 resName[0] = 0;
5942                 *resSeq = -1;
5943                 p = (char *)s;
5944         }
5945         strncpy(atomName, p, 4);
5946         atomName[4] = 0;
5947         return 0;
5948 }
5949
5950 /*  Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer  */
5951 int
5952 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
5953 {
5954         char resName[6];
5955         int resSeq, n;
5956         char atomName[6];
5957         /*      char *p; */
5958
5959         n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
5960         if (atomName[0] == 0) {
5961           if (n >= mp->natoms)
5962             n = -1;  /* Out of range */
5963           return n;
5964         }
5965         for (n = 0; n < mp->natoms; n++) {
5966                 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
5967                 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
5968                         && (resSeq < 0 || ap->resSeq == resSeq)
5969                         && strncmp(atomName, ap->aname, 4) == 0) {
5970                         return n;
5971                 }
5972         }
5973         return -1;  /*  Not found  */
5974 }
5975
5976 void
5977 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
5978 {
5979         Atom *ap;
5980         int n;
5981         if (mp == NULL || index < 0 || index >= mp->natoms) {
5982                 buf[0] = 0;
5983                 return;
5984         }
5985         ap = mp->atoms + index;
5986         if (ap->resSeq != 0) {
5987                 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
5988                 buf += n;
5989                 bufsize -= n;
5990         }
5991         snprintf(buf, bufsize, "%.4s", ap->aname);
5992 }
5993
5994 #pragma mark ====== Selection ======
5995
5996 static void
5997 sMoleculeNotifyChangeSelection(Molecule *mp)
5998 {
5999         /*  TODO: Finer control of notification types may be necessary  */
6000         MoleculeCallback_notifyModification(mp, 0);
6001 }
6002
6003 void
6004 MoleculeSetSelection(Molecule *mp, IntGroup *select)
6005 {
6006         if (mp == NULL)
6007                 return;
6008         if (select != NULL)
6009                 IntGroupRetain(select);
6010         if (mp->selection != NULL)
6011                 IntGroupRelease(mp->selection);
6012         mp->selection = select;
6013         sMoleculeNotifyChangeSelection(mp);
6014 }
6015
6016 IntGroup *
6017 MoleculeGetSelection(Molecule *mp)
6018 {
6019         if (mp == NULL)
6020                 return NULL;
6021         else return mp->selection;
6022 }
6023
6024 void
6025 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
6026 {
6027         if (mp->selection == NULL)
6028                 mp->selection = IntGroupNew();
6029         if (!extending)
6030                 IntGroupClear(mp->selection);
6031         IntGroupAdd(mp->selection, n1, 1);
6032         sMoleculeNotifyChangeSelection(mp);
6033 }
6034
6035 void
6036 MoleculeUnselectAtom(Molecule *mp, int n1)
6037 {
6038         if (mp->selection != NULL)
6039                 IntGroupRemove(mp->selection, n1, 1);
6040         sMoleculeNotifyChangeSelection(mp);
6041 }
6042
6043 void
6044 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
6045 {
6046         if (mp->selection == NULL)
6047                 mp->selection = IntGroupNew();
6048         IntGroupReverse(mp->selection, n1, 1);
6049         sMoleculeNotifyChangeSelection(mp);
6050 }
6051
6052 int
6053 MoleculeIsAtomSelected(Molecule *mp, int n1)
6054 {
6055         if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
6056                 return 1;
6057         else return 0;
6058 }
6059
6060 int
6061 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
6062 {
6063         if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
6064                 return 1;
6065         else return 0;
6066 }
6067
6068 IntGroup *
6069 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
6070 {
6071         int status;
6072         IntGroup *remain, *ig1, *ig2;
6073         ig1 = ig2 = NULL;
6074         remain = IntGroupNewFromIntGroup(remove);
6075         if (remain == NULL)
6076                 status = -1;
6077         else
6078                 status = IntGroupReverse(remain, 0, mp->natoms);
6079         if (status == 0) {
6080                 ig1 = IntGroupNew();
6081                 if (ig1 == NULL)
6082                         status = -1;
6083                 else
6084                         status = IntGroupDifference(selection, remove, ig1);
6085         }
6086         if (status == 0) {
6087                 ig2 = IntGroupNew();
6088                 if (ig2 == NULL)
6089                         status = -1;
6090                 else
6091                         status = IntGroupDeconvolute(ig1, remain, ig2);
6092         }
6093         if (remain != NULL)
6094                 IntGroupRelease(remain);
6095         if (ig1 != NULL)
6096                 IntGroupRelease(ig1);
6097         if (status == 0)
6098                 return ig2;
6099         else {
6100                 if (ig2 != NULL)
6101                         IntGroupRelease(ig2);
6102                 return NULL;
6103         }
6104 }
6105
6106 #pragma mark ====== Atom Equivalence ======
6107
6108 struct sEqList {
6109         int i[2];
6110         struct sEqList *next;
6111         struct sEqList *link;
6112 };
6113
6114 static struct sEqList *sListBase = NULL;
6115 static struct sEqList *sListFree = NULL;
6116
6117 static struct sEqList *
6118 sAllocEqList(void)
6119 {
6120         struct sEqList *lp;
6121         if (sListFree != NULL) {
6122                 lp = sListFree;
6123                 sListFree = lp->next;
6124                 lp->i[0] = lp->i[1] = 0;
6125                 lp->next = NULL;
6126                 return lp;
6127         }
6128         lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
6129         lp->link = sListBase;
6130         sListBase = lp;
6131         return lp;
6132 }
6133
6134 static void
6135 sFreeEqList(struct sEqList *list)
6136 {
6137         list->next = sListFree;
6138         sListFree = list;
6139 }
6140
6141 static void
6142 sDeallocateEqLists(void)
6143 {
6144         struct sEqList *lp, *lp_link;
6145         for (lp = sListBase; lp != NULL; lp = lp_link) {
6146                 lp_link = lp->link;
6147                 free(lp);
6148         }
6149         sListBase = NULL;
6150         sListFree = NULL;
6151 }
6152
6153 static int
6154 sExistInEqList(int i, int idx, struct sEqList *list)
6155 {
6156         while (list != NULL) {
6157                 if (list->i[idx] == i)
6158                         return 1;
6159                 list = list->next;
6160         }
6161         return 0;
6162 }
6163
6164 static struct sEqList *
6165 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
6166 {
6167         Atom *api, *apj;
6168         struct sEqList *list1, *list2;
6169         Int ii, jj, ni, nj, *cpi, *cpj;
6170         api = ATOM_AT_INDEX(mol->atoms, i);
6171         apj = ATOM_AT_INDEX(mol->atoms, j);
6172         if (api->atomicNumber != apj->atomicNumber)
6173                 return NULL;
6174         list1 = sAllocEqList();
6175         if (list1 == NULL)
6176                 return NULL;
6177         list1->i[0] = i;
6178         list1->i[1] = j;
6179         list1->next = list;
6180         if (i == j || (db[i] != NULL && db[i] == db[j]))
6181                 return list1;
6182         cpi = AtomConnectData(&api->connect);
6183         cpj = AtomConnectData(&apj->connect);
6184         for (ni = 0; ni < api->connect.count; ni++) {
6185                 ii = cpi[ni];
6186                 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
6187                         continue;
6188                 if (sExistInEqList(ii, 0, list1))
6189                         continue;
6190                 list2 = NULL;
6191                 for (nj = 0; nj < apj->connect.count; nj++) {
6192                         jj = cpj[nj];
6193                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6194                                 continue;
6195                         if (sExistInEqList(jj, 1, list1))
6196                                 continue;
6197                         list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
6198                         if (list2 != NULL)
6199                                 break;
6200                 }
6201                 if (list2 == NULL) {
6202                         sFreeEqList(list1);
6203                         return NULL;    /*  No equivalent to ii  */
6204                 }
6205                 list1 = list2;      /*  ii is OK, try next  */
6206         }
6207         return list1;
6208 }
6209
6210 int
6211 sDBInclude(Int *ip, int i)
6212 {
6213         int j;
6214         if (ip == NULL)
6215                 return -1;
6216         for (j = ip[0] - 1; j >= 0; j--) {
6217                 if (ip[j] == i)
6218                         return j;
6219         }
6220         return -1;
6221 }
6222
6223 Int *
6224 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
6225 {
6226         Int **db;  /*  List of equivalents for each atom  */
6227         Int *ip, *result;
6228         Atom *api, *apj, *apk;
6229         Int *cpi, *cpj, *ibuf, nibuf;
6230         int i, j, k, ii, jj, kk;
6231         if (mol == NULL || mol->natoms == 0)
6232                 return NULL;
6233         db = (Int **)calloc(sizeof(Int *), mol->natoms);
6234         ibuf = NULL;
6235         nibuf = 0;
6236
6237         /*  Find the equivalent univalent atoms  */
6238         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6239                 if (api->connect.count < 2)
6240                         continue;
6241                 cpi = AtomConnectData(&api->connect);
6242                 for (j = 0; j < api->connect.count; j++) {
6243                         Int n;
6244                         n = 0;
6245                         jj = cpi[j];
6246                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6247                                 continue;
6248                         AssignArray(&ibuf, &nibuf, sizeof(Int), n, &jj);
6249                         n++;
6250                         apj = ATOM_AT_INDEX(mol->atoms, jj);
6251                         if (apj->connect.count != 1 || db[jj] != NULL)
6252                                 continue;
6253                         cpj = AtomConnectData(&apj->connect);
6254                         for (k = j + 1; k < api->connect.count; k++) {
6255                                 kk = cpj[k];
6256                                 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
6257                                         continue;
6258                                 apk = ATOM_AT_INDEX(mol->atoms, kk);
6259                                 if (apk->connect.count != 1 || db[kk] != NULL)
6260                                         continue;
6261                                 if (apj->atomicNumber == apk->atomicNumber) {
6262                                         AssignArray(&ibuf, &nibuf, sizeof(Int), n, &kk);
6263                                         n++;
6264                                 }
6265                         }
6266                         if (n > 1) {
6267                                 ip = (Int *)calloc(sizeof(Int), n + 1);
6268                                 if (ip == NULL)
6269                                         return NULL;
6270                                 ip[0] = n;
6271                                 memmove(ip + 1, ibuf, sizeof(Int) * n);
6272                                 for (k = 0; k < n; k++)
6273                                         db[ip[k + 1]] = ip;
6274                         }
6275                 }
6276         }
6277         if (ibuf != NULL) {
6278                 free(ibuf);
6279                 ibuf = NULL;
6280         }
6281         
6282         /*  Try matching (i,j) pair  */
6283         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6284                 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
6285                         continue;
6286                 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
6287                         struct sEqList *list;
6288                         if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
6289                                 continue;
6290                         if (api->atomicNumber != apj->atomicNumber)
6291                                 continue;  /*  Different elements do not match  */
6292                         if (db[i] != NULL && db[i] == db[j])
6293                                 continue;  /*  Already equivalent  */
6294                         list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
6295                         if (list == NULL)
6296                                 continue;  /*  (i,j) do not match  */
6297                         while (list != NULL) {
6298                                 ii = list->i[0];
6299                                 jj = list->i[1];
6300                                 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
6301                                         /*  Merge db[ii] and db[jj]  */
6302                                         k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
6303                                         ip = (Int *)calloc(sizeof(Int), k + 1);
6304                                         if (ip == NULL)
6305                                                 return NULL;  /*  Out of memory  */
6306                                         if (db[ii] == NULL) {
6307                                                 ip[1] = ii;
6308                                                 k = 2;
6309                                         } else {
6310                                                 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
6311                                                 k = db[ii][0] + 1;
6312                                         }
6313                                         if (db[jj] == NULL) {
6314                                                 ip[k++] = jj;
6315                                         } else {
6316                                                 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
6317                                                 k += db[jj][0];
6318                                         }
6319                                         ip[0] = k - 1;
6320                                         /*  Free old ones  */
6321                                         if (db[ii] != NULL)
6322                                                 free(db[ii]);
6323                                         if (db[jj] != NULL)
6324                                                 free(db[jj]);
6325                                         for (k = 0; k < ip[0]; k++)
6326                                                 db[ip[k + 1]] = ip;
6327                                         if (0) {
6328                                                 /*  For debug  */
6329                                                 printf("(%d,%d) matched: ", ii, jj);
6330                                                 for (k = 0; k < ip[0]; k++) {
6331                                                         printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
6332                                                 }
6333                                                 printf("]\n");
6334                                         }
6335                                 }
6336                                 list = list->next;
6337                         }
6338                 }
6339         }
6340         
6341         /*  Record the equivalent atoms with the lowest index for each atom  */
6342         result = (Int *)calloc(sizeof(Int), mol->natoms);
6343         for (i = 0; i < mol->natoms; i++)
6344                 result[i] = -1;
6345         for (i = 0; i < mol->natoms; i++) {
6346                 if (result[i] >= 0 || (ip = db[i]) == NULL)
6347                         continue;
6348                 k = mol->natoms;
6349                 for (j = 0; j < ip[0]; j++) {
6350                         kk = ip[j + 1];
6351                         if (kk < k)
6352                                 k = kk;
6353                 }
6354                 for (j = 0; j < ip[0]; j++) {
6355                         result[ip[j + 1]] = k;
6356                         db[ip[j + 1]] = NULL;
6357                 }
6358                 free(ip);
6359         }
6360         sDeallocateEqLists();
6361         return result;
6362 }
6363
6364 #pragma mark ====== Symmetry expansion ======
6365
6366 int
6367 MoleculeGetTransformForSymop(Molecule *mp, Symop symop, Transform *tf, int is_cartesian)
6368 {
6369         Transform t;
6370         if (mp == NULL || mp->cell == NULL)
6371                 return -1;
6372         if (symop.sym >= mp->nsyms && symop.sym != 0)
6373                 return -2;
6374         memmove(*tf, SYMMETRY_AT_INDEX(mp->syms, symop.sym), sizeof(Transform));
6375         (*tf)[9] += symop.dx;
6376         (*tf)[10] += symop.dy;
6377         (*tf)[11] += symop.dz;
6378         if (is_cartesian) {
6379                 TransformMul(t, *tf, mp->cell->rtr);
6380                 TransformMul(*tf, mp->cell->tr, t);
6381         }
6382         return 0;
6383 }
6384
6385 int
6386 MoleculeGetSymopForTransform(Molecule *mp, const Transform tf, Symop *symop, int is_cartesian)
6387 {
6388         Transform t;
6389         int i, j, n[3];
6390         if (mp == NULL || mp->cell == NULL)
6391                 return -1;
6392         if (is_cartesian) {
6393                 TransformMul(t, tf, mp->cell->tr);
6394                 TransformMul(t, mp->cell->rtr, t);
6395         } else {
6396                 memmove(t, tf, sizeof(Transform));
6397         }
6398         for (i = 0; i < mp->nsyms || i == 0; i++) {
6399                 Transform *tp = &(SYMMETRY_AT_INDEX(mp->syms, i));
6400                 for (j = 0; j < 9; j++) {
6401                         if (fabs((*tp)[j] - t[j]) > 1e-4)
6402                                 break;
6403                 }
6404                 if (j == 9) {
6405                         for (j = 9; j < 12; j++) {
6406                                 double f1 = t[j] - (*tp)[j];
6407                                 double f2 = floor(f1 + 0.5);
6408                                 if (fabs(f1 - f2) > 1e-4)
6409                                         break;
6410                                 n[j - 9] = f2;
6411                         }
6412                         if (j == 12) {
6413                                 /*  Found  */
6414                                 symop->sym = i;
6415                                 symop->dx = n[0];
6416                                 symop->dy = n[1];
6417                                 symop->dz = n[2];
6418                                 symop->alive = (SYMOP_ALIVE((*symop)) != 0);
6419                                 return 0;
6420                         }
6421                 }
6422         }
6423         return -3;  /*  Not found  */
6424 }
6425
6426 int
6427 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
6428 {
6429         if (mp == NULL)
6430                 return 1;
6431         if (symop.sym >= mp->nsyms && symop.sym != 0)
6432                 return 2;
6433         if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
6434                 TransformVec(vpout, mp->cell->rtr, vpin);
6435                 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpout);
6436                 vpout->x += symop.dx;
6437                 vpout->y += symop.dy;
6438                 vpout->z += symop.dz;
6439                 TransformVec(vpout, mp->cell->tr, vpout);
6440         } else {
6441                 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpin);
6442                 vpout->x += symop.dx;
6443                 vpout->y += symop.dy;
6444                 vpout->z += symop.dz;
6445         }
6446         return 0;
6447 }
6448
6449 /*  Add expanded atoms. Returns the number of newly created atoms.
6450         If indices is non-NULL, it should be an array of Int with at least 
6451         IntGroupGetCount(group) entries, and on return it contains the
6452     indices of the expanded atoms (may be existing atoms if the expanded
6453     atoms are already present)
6454     If allowOverlap is non-zero, then the new atom is created even when the
6455     coordinates coincide with the some other atom (special position) of the
6456     same element; otherwise, such atom will not be created and the existing
6457     atom is returned in indices[].  */
6458 int
6459 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group, Int *indices, Int allowOverlap)
6460 {
6461         int i, n, n0, n1, n2, base, count, *table;
6462         Atom *ap;
6463         IntGroupIterator iter;
6464         Transform tr, t1;
6465         Symop symop1;
6466         Atom *ap2;
6467         Vector nr, dr;
6468         
6469         if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
6470                 return -1;
6471         if (symop.sym != 0 && symop.sym >= mp->nsyms)
6472                 return -2;
6473
6474         /*  Create atoms, with avoiding duplicates  */
6475         n0 = n1 = mp->natoms;
6476         table = (int *)malloc(sizeof(int) * n0);
6477         if (table == NULL)
6478                 return -3;
6479         for (i = 0; i < n0; i++)
6480                 table[i] = -1;
6481         IntGroupIteratorInit(group, &iter);
6482         MoleculeGetTransformForSymop(mp, symop, &tr, 0);
6483         __MoleculeLock(mp);
6484         for (i = 0; i < count; i++) {
6485                 n = IntGroupIteratorNext(&iter);
6486                 ap = ATOM_AT_INDEX(mp->atoms, n);
6487                 if (SYMOP_ALIVE(ap->symop)) {
6488                         /*  Calculate the cumulative symop  */
6489                         Transform tr2;
6490                         MoleculeGetTransformForSymop(mp, ap->symop, &t1, 0);
6491                         TransformMul(tr2, tr, t1);
6492                         if (MoleculeGetSymopForTransform(mp, tr2, &symop1, 0) != 0) {
6493                                 if (indices != NULL)
6494                                         indices[i] = -1;
6495                                 continue;  /*  Skip this atom  */
6496                         }
6497                         base = ap->symbase;
6498                 } else {
6499                         symop1 = symop;
6500                         base = n;
6501                 }
6502
6503                 /*  Calculate the expande position  */
6504                 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
6505                 
6506                 /*  Is this expansion already present?  */
6507                 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
6508                         /*  Symmetry operation and the base atom are the same  */
6509                         if (ap2->symbase == base && SYMOP_EQUAL(symop1, ap2->symop))
6510                                 break;
6511                         /*  Atomic number and the position are the same  */
6512                         if (ap2->atomicNumber == ap->atomicNumber && allowOverlap == 0) {
6513                                 VecSub(dr, ap2->r, nr);
6514                                 if (VecLength2(dr) < 1e-6)
6515                                         break;
6516                         }
6517                 }
6518                 if (n2 < n0) {
6519                         /*  If yes, then skip it  */
6520                         if (indices != NULL)
6521                                 indices[i] = n2;
6522                         continue;
6523                 } else {
6524                         /*  Create a new atom  */
6525                         Atom newAtom;
6526                         AtomDuplicate(&newAtom, ap);
6527                         MoleculeCreateAnAtom(mp, &newAtom, -1);
6528                         AtomClean(&newAtom);
6529                         ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
6530                         ap2->r = nr;
6531                         ap2->symbase = base;
6532                         ap2->symop = symop1;
6533                         ap2->symop.alive = (symop1.dx != 0 || symop1.dy != 0 || symop1.dz != 0 || symop1.sym != 0);
6534                         table[n] = n1;  /*  The index of the new atom  */
6535                         MoleculeSetAnisoBySymop(mp, n1);  /*  Recalculate anisotropic parameters according to symop  */
6536                         if (indices != NULL)
6537                                 indices[i] = n1;
6538                         n1++;
6539                 }
6540         }
6541         IntGroupIteratorRelease(&iter);
6542
6543         /*  Create bonds  */
6544         for (i = n0; i < n1; i++) {
6545                 Int b[2], j;
6546                 ap = ATOM_AT_INDEX(mp->atoms, i);
6547                 if (SYMOP_ALIVE(ap->symop) && MoleculeGetTransformForSymop(mp, ap->symop, &tr, 1) == 0) {
6548                         /*  For each connected atom, look for the transformed atom  */
6549                         Int *cp;
6550                         ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6551                         cp = AtomConnectData(&ap2->connect);
6552                         n2 = ap2->connect.count;
6553                         for (n = 0; n < n2; n++) {
6554                                 Atom *apn = ATOM_AT_INDEX(mp->atoms, cp[n]);
6555                                 nr = apn->r;
6556                                 TransformVec(&nr, tr, &nr);
6557                                 /*  Look for the bonded atom transformed by ap->symop  */
6558                                 for (j = 0, ap2 = mp->atoms; j < mp->natoms; j++, ap2 = ATOM_NEXT(ap2)) {
6559                                         if (ap2->symbase == cp[n] && SYMOP_EQUAL(ap->symop, ap2->symop))
6560                                                 break;
6561                                         VecSub(dr, nr, ap2->r);
6562                                         if (ap2->atomicNumber == apn->atomicNumber && VecLength2(dr) < 1e-6)
6563                                                 break;
6564                                 }
6565                                 if (j < mp->natoms) {
6566                                         /*  Bond i-j is created  */
6567                                         b[0] = i;
6568                                         b[1] = j;
6569                                         if (MoleculeLookupBond(mp, b[0], b[1]) < 0)
6570                                                 MoleculeAddBonds(mp, 1, b, NULL, 1);
6571                                 }
6572                         }
6573                 }
6574         }
6575         mp->needsMDRebuild = 1;
6576         __MoleculeUnlock(mp);
6577         free(table);
6578         return n1 - n0;  /*  The number of added atoms  */
6579 }
6580
6581 /*  Recalculate the coordinates of symmetry expanded atoms.
6582     (Also recalculate the positions of pi-anchor atoms)
6583         Returns the number of affected atoms.
6584     If group is non-NULL, only the expanded atoms whose base atoms are in the
6585     given group are considered.
6586         If groupout and vpout are non-NULL, the indices of the affected atoms
6587         and the original positions are returned (for undo operation).
6588         The pointers returned in *groupout and *vpout must be released and 
6589         free()'ed by the caller  */
6590 int
6591 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
6592 {
6593         int i, count;
6594         Atom *ap, *bp;
6595         Vector nr, dr;
6596         IntGroup *ig = NULL;
6597         Vector *vp = NULL;
6598         
6599         if (mp == NULL || mp->natoms == 0)
6600                 return 0;
6601
6602         __MoleculeLock(mp);
6603         count = 0;
6604         if (mp->nsyms != 0) {
6605                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6606                         if (!SYMOP_ALIVE(ap->symop))
6607                                 continue;
6608                         if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
6609                                 continue;
6610                         bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6611                         MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
6612                         VecSub(dr, nr, ap->r);
6613                         if (VecLength2(dr) < 1e-20)
6614                                 continue;
6615                         if (groupout != NULL) {
6616                                 if (ig == NULL) {
6617                                         ig = IntGroupNew();
6618                                         vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
6619                                 }
6620                                 vp[count] = ap->r;
6621                                 IntGroupAdd(ig, i, 1);
6622                         }
6623                         ap->r = nr;
6624                         count++;
6625                 }
6626         }
6627         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6628                 Int *ip, j, n;
6629                 if (ap->anchor == NULL)
6630                         continue;
6631                 if (group != NULL) {
6632                         if (IntGroupLookup(group, i, NULL) == 0) {
6633                                 n = ap->anchor->connect.count;
6634                                 ip = AtomConnectData(&(ap->anchor->connect));
6635                                 for (j = 0; j < n; j++) {
6636                                         if (IntGroupLookup(group, ip[j], NULL) != 0)
6637                                                 break;
6638                                 }
6639                                 if (j == n)
6640                                         continue;  /*  This pi-anchor should not be modified  */
6641                         }
6642                 }
6643                 nr = ap->r;
6644                 MoleculeCalculatePiAnchorPosition(mp, i);
6645                 VecSub(dr, nr, ap->r);
6646                 if (VecLength2(dr) < 1e-20) {
6647                         ap->r = nr;  /*  No change  */
6648                         continue;
6649                 }
6650                 if (groupout != NULL) {
6651                         if (ig == NULL) {
6652                                 ig = IntGroupNew();
6653                                 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
6654                         }
6655                         vp[count] = nr;
6656                         IntGroupAdd(ig, i, 1);
6657                 }
6658                 count++;
6659         }
6660         mp->needsMDCopyCoordinates = 1;
6661         __MoleculeUnlock(mp);
6662
6663         if (count > 0) {
6664                 if (groupout != NULL && vpout != NULL) {
6665                         *groupout = ig;
6666                         *vpout = (Vector *)realloc(vp, sizeof(Vector) * count);
6667                 } else {
6668                         IntGroupRelease(ig);
6669                         free(vp);
6670                 }
6671         } else {
6672                 if (groupout != NULL && vpout != NULL) {
6673                         *groupout = NULL;
6674                         *vpout = NULL;
6675                 }
6676         }
6677         return count;
6678 }
6679
6680 #pragma mark ====== Show/hide atoms ======
6681
6682 static void
6683 sMoleculeNotifyChangeAppearance(Molecule *mp)
6684 {
6685         /*  TODO: Finer control of notification types may be necessary  */
6686         MoleculeCallback_notifyModification(mp, 0);
6687 }
6688
6689
6690 static void
6691 sMoleculeUnselectHiddenAtoms(Molecule *mp)
6692 {
6693         int i;
6694         if (mp == NULL || mp->selection == NULL)
6695                 return;
6696         for (i = 0; i < mp->natoms; i++) {
6697                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6698                 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
6699                         IntGroupRemove(mp->selection, i, 1);
6700         }
6701         sMoleculeNotifyChangeAppearance(mp);
6702 }
6703
6704 int
6705 MoleculeShowAllAtoms(Molecule *mp)
6706 {
6707         int i;
6708         if (mp == NULL)
6709                 return 0;
6710         for (i = 0; i < mp->natoms; i++) {
6711                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6712                 ap->exflags &= ~kAtomHiddenFlag;
6713         }
6714         sMoleculeNotifyChangeAppearance(mp);
6715         return 1;
6716 }
6717
6718 int
6719 MoleculeShowReverse(Molecule *mp)
6720 {
6721         int i;
6722         if (mp == NULL)
6723                 return 0;
6724         for (i = 0; i < mp->natoms; i++) {
6725                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6726                 ap->exflags ^= kAtomHiddenFlag;
6727         }
6728         sMoleculeUnselectHiddenAtoms(mp);
6729         sMoleculeNotifyChangeAppearance(mp);
6730         return 1;
6731 }
6732
6733 int
6734 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
6735 {
6736         int i;
6737         if (mp == NULL || ig == NULL)
6738                 return 0;
6739         for (i = 0; i < mp->natoms; i++) {
6740                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6741                 if (ap->exflags & kAtomHiddenFlag)
6742                         continue;  /*  Already hidden  */
6743                 if (IntGroupLookupPoint(ig, i) >= 0)
6744                         ap->exflags |= kAtomHiddenFlag;
6745         }
6746         sMoleculeUnselectHiddenAtoms(mp);
6747         sMoleculeNotifyChangeAppearance(mp);
6748         return 1;
6749 }
6750
6751 #pragma mark ====== Reversible Editing ======
6752
6753 /*
6754 static void
6755 sMoleculeNotifyModification(Molecule *mp)
6756 {
6757         **  TODO: Finer control of notification types may be necessary  **
6758         MoleculeCallback_notifyModification(mp, 0);
6759 }
6760 */
6761
6762 /*  Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup  */
6763 int
6764 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
6765 {
6766         int n1, n2, n3, i;
6767         if (where == NULL) {
6768                 /*  Append the new objects at the end  */
6769                 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
6770                 return 0;
6771         }
6772         n1 = IntGroupGetCount(where);  /*  Position to get new object  */
6773         n2 = nobjs;                    /*  Position to get old object  */
6774         n3 = n1 + n2;                  /*  Position to place new/old object  */
6775         for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
6776                 int start = IntGroupGetStartPoint(where, i);
6777                 int end = IntGroupGetEndPoint(where, i);
6778                 if (end < n3) {
6779                         /*  old[end-(n3-n2)..n2-1] is moved to old[end..n3-1]  */
6780                         memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
6781                         n2 = end - (n3 - n2);
6782                         n3 = end;
6783                 }
6784                 /*  new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1]  */
6785                 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
6786                 n3 -= end - start;
6787                 n1 -= end - start;
6788         }
6789         return 0;
6790 }
6791
6792 /*  Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
6793 int
6794 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6795 {
6796         int n1, n2, n3, start, end, i;
6797         if (where == NULL || IntGroupGetCount(where) == 0)
6798                 return 0;  /*  No operation  */
6799         if (objs == NULL || nobjs == 0)
6800                 return 1;  /*  Bad argument  */
6801         n1 = 0;  /*  Position to move remaining elements to */
6802         n2 = 0;  /*  Position to move remaining elements from  */
6803         n3 = 0;  /*  Position to move removed elements to  */
6804         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6805                 end = IntGroupGetEndPoint(where, i);
6806                 if (n2 < start) {
6807                         /*  Move (start - n2) elements from objs[n2] to objs[n1]  */
6808                         if (n1 < n2)
6809                                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
6810                         n1 += start - n2;
6811                         n2 = start;
6812                 }
6813                 /*  Move (end - start) elements from objs[n2] to clip[n3]  */
6814                 if (clip != NULL)
6815                         memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
6816                 n3 += (end - start);
6817                 n2 += (end - start);
6818         }
6819         /*  Move (nobjs - n2) elements from objs[n2] to objs[n1]  */
6820         if (nobjs > n2)
6821                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
6822         return 0;
6823 }
6824
6825 /*  Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
6826 int
6827 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6828 {
6829         int n1, start, end, i;
6830         if (objs == NULL || where == NULL)
6831                 return 1;  /*  Bad argument  */
6832         n1 = 0;  /*  Position to move removed elements to  */
6833         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6834                 end = IntGroupGetEndPoint(where, i);
6835                 /*  Copy (end - start) elements from objs[start] to clip[n1]  */
6836                 if (clip != NULL)
6837                         memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
6838                 n1 += (end - start);
6839         }
6840         return 0;
6841 }
6842
6843 /*  Create a new atom with no bonding information. ap must _not_ be inside the given molecule
6844    (Use AtomDuplicate() first) */
6845 int
6846 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
6847 {
6848     Atom *ap1, *api;
6849         int i;
6850         if (mp == NULL || ap == NULL || mp->noModifyTopology)
6851                 return -1;
6852         __MoleculeLock(mp);
6853         if (pos < 0 || pos >= mp->natoms)
6854                 pos = mp->natoms;
6855         ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
6856         if (ap1 == NULL)
6857                 goto error;  /*  Out of memory  */
6858         ap1 = ATOM_AT_INDEX(mp->atoms, pos);
6859         if (pos < mp->natoms - 1) {
6860                 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6861         }
6862         if (AtomDuplicate(ap1, ap) == NULL) {
6863                 /*  Cannot duplicate: restore the original state  */
6864                 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6865                 mp->natoms--;
6866                 goto error;
6867         }
6868         ap1->connect.count = 0;
6869         if (ap1->resSeq >= mp->nresidues)
6870                 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
6871         if (ap1->resName[0] == 0)
6872           strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
6873         if (ap1->segName[0] == 0)
6874           strncpy(ap1->segName, "MAIN", 4);
6875         if (pos < mp->natoms - 1) {
6876                 /*  Renumber the connect table, bonds, angles, etc. */
6877                 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
6878                         int j;
6879                         Int *cp;
6880                         cp = AtomConnectData(&api->connect);
6881                         for (j = 0; j < api->connect.count; j++) {
6882                                 if (cp[j] >= pos)
6883                                         cp[j]++;
6884                         }
6885                         if (api->anchor != NULL) {
6886                                 cp = AtomConnectData(&api->anchor->connect);
6887                                 for (j = 0; j < api->anchor->connect.count; j++) {
6888                                         if (cp[j] >= pos)
6889                                                 cp[j]++;
6890                                 }
6891                         }
6892                 }
6893                 for (i = 0; i < mp->nbonds * 2; i++) {
6894                         if (mp->bonds[i] >= pos)
6895                                 mp->bonds[i]++;
6896                 }
6897                 for (i = 0; i < mp->nangles * 3; i++) {
6898                         if (mp->angles[i] >= pos)
6899                                 mp->angles[i]++;
6900                 }
6901                 for (i = 0; i < mp->ndihedrals * 4; i++) {
6902                         if (mp->dihedrals[i] >= pos)
6903                                 mp->dihedrals[i]++;
6904                 }
6905                 for (i = 0; i < mp->nimpropers * 4; i++) {
6906                         if (mp->impropers[i] >= pos)
6907                                 mp->impropers[i]++;
6908                 }
6909         }
6910         mp->nframes = -1;  /*  Should be recalculated later  */
6911         MoleculeIncrementModifyCount(mp);
6912         mp->needsMDRebuild = 1;
6913         __MoleculeUnlock(mp);
6914         return pos;
6915 error:
6916         __MoleculeUnlock(mp);
6917         return -1;
6918 }
6919
6920 #if defined(DEBUG)
6921
6922 static int s_error_count;
6923
6924 static int
6925 s_fprintf(FILE *fp, const char *fmt, ...)
6926 {
6927         va_list va;
6928         va_start(va, fmt);
6929         s_error_count++;
6930         return vfprintf(fp, fmt, va);
6931 }
6932
6933 int
6934 MoleculeCheckSanity(Molecule *mol)
6935 {
6936         const char *fail = "Sanity check failure";
6937         Int i, j, *ip, c[4];
6938         Atom *ap;
6939         s_error_count = 0;
6940         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
6941                 if (ap->resSeq >= mol->nresidues)
6942                         s_fprintf(stderr, "%s: atom %d residue %d but nresidues %d\n", fail, i, ap->resSeq, mol->nresidues);
6943                 if (ap->type != 0 && ap->type < kAtomTypeMinimum)
6944                         s_fprintf(stderr, "%s: atom %d atom type %d less than minimum\n", fail, i, ap->type);
6945                 if (ap->atomicNumber < 0 || ap->atomicNumber > 113)
6946                         s_fprintf(stderr, "%s: atom %d atomic number %d\n", fail, i, ap->atomicNumber);
6947                 ip = AtomConnectData(&ap->connect);
6948                 for (j = 0; j < ap->connect.count; j++) {
6949                         if (ip[j] < 0 || ip[j] >= mol->natoms)
6950                                 s_fprintf(stderr, "%s: atom %d connect[%d] = %d out of range\n", fail, i, j, ip[j]);
6951                         if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[j])->connect), i) == 0)
6952                                 s_fprintf(stderr, "%s: atom %d has connect %d but atom %d has no connect %d\n", fail, i, ip[j], ip[j], i);
6953                 }
6954         }
6955         for (i = 0, ip = mol->bonds; i < mol->nbonds; i++, ip += 2) {
6956                 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms)
6957                         s_fprintf(stderr, "%s: bond %d %d-%d out of range\n", fail, i, ip[0], ip[1]);
6958                 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[0])->connect), ip[1]) == 0)
6959                         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]);
6960         }
6961         for (i = 0, ip = mol->angles; i < mol->nangles; i++, ip += 3) {
6962                 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms)
6963                         s_fprintf(stderr, "%s: angle %d %d-%d-%d out of range\n", fail, i, ip[0], ip[1], ip[2]);
6964                 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
6965                 if (c[0] == 0)
6966                         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]);
6967                 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
6968                 if (c[1] == 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[2]);
6970                 if (c[0] == 2 && c[1] == 2)
6971                         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]);
6972         }
6973         for (i = 0, ip = mol->dihedrals; i < mol->ndihedrals; i++, ip += 4) {
6974                 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)
6975                         s_fprintf(stderr, "%s: dihedral %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
6976                 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
6977                 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
6978                 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
6979                 if (c[0] == 0)
6980                         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]);
6981                 if (c[1] == 0)
6982                         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]);
6983                 if (c[2] == 0)
6984                         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]);
6985         }
6986         for (i = 0, ip = mol->impropers; i < mol->nimpropers; i++, ip += 4) {
6987                 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)
6988                         s_fprintf(stderr, "%s: improper %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
6989                 c[0] = MoleculeAreAtomsConnected(mol, ip[2], ip[0]);
6990                 c[1] = MoleculeAreAtomsConnected(mol, ip[2], ip[1]);
6991                 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
6992                 if (c[0] == 0)
6993                         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]);
6994                 if (c[1] == 0)
6995                         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]);
6996                 if (c[2] == 0)
6997                         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]);
6998         }
6999         return s_error_count;
7000 }
7001 #endif
7002
7003 /*  Merge two molecules. We use this procedure for all add-atom operations.  */
7004 /*  resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
7005 /*  If nactions and actions are non-NULL, then the corresponding undo actions are created and returned. */
7006 /*  If forUndo is non-zero, then only the atoms are inserted; other information should be inserted
7007     separately by other undo actions.  */
7008 int
7009 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, Int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7010 {
7011         Int nsrc, ndst;
7012         Int i, j, n1, n2, n3, n4, *cp;
7013         Int *new2old, *old2new;
7014         IntGroup *ig;
7015         Atom *ap;
7016         MolAction *act;
7017         
7018         if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
7019                 return 0;  /*  Do nothing  */
7020
7021         if (dst->noModifyTopology)
7022                 return 1;  /*  Prohibited operation  */
7023
7024         if (where != NULL && IntGroupGetCount(where) != src->natoms)
7025                 return 1;  /*  Bad parameter  */
7026
7027         if (nactions != NULL)
7028                 *nactions = 0;
7029         if (actions != NULL)
7030                 *actions = NULL;
7031         act = NULL;
7032
7033         __MoleculeLock(dst);
7034
7035         nsrc = src->natoms;
7036         ndst = dst->natoms;
7037         if (resSeqOffset < 0)
7038                 resSeqOffset = 0;
7039
7040         /*  Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
7041             and ndst..ndst+nsrc-1 are for atoms in src.  */ 
7042         new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
7043         if (new2old == NULL)
7044                 goto panic;
7045         old2new = new2old + ndst + nsrc;
7046         n1 = 0;  /*  dst index  */
7047         n2 = 0;  /*  src index  */
7048         n3 = 0;  /*  "merged" index  */
7049         i = 0;
7050         while (n1 < ndst || n2 < nsrc) {
7051                 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
7052                         n4 = ndst - n1;
7053                 else n4 -= n3;
7054                 /*  n4 elements from dst[n1] will go to merged[n3]  */
7055                 for (j = 0; j < n4; j++) {
7056                         old2new[n1 + j] = n3 + j;
7057                         new2old[n3 + j] = n1 + j;
7058                 }
7059                 n3 += n4;
7060                 n1 += n4;
7061                 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
7062                         n4 = nsrc - n2;
7063                 /*  n4 elements from src[n2] will go to merged[n3]  */
7064                 for (j = 0; j < n4; j++) {
7065                         old2new[ndst + n2 + j] = n3 + j;
7066                         new2old[n3 + j] = ndst + n2 + j;
7067                 }
7068                 n3 += n4;
7069                 n2 += n4;
7070                 i++;
7071         }
7072
7073         /*  Expand the destination array  */
7074         if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
7075                 goto panic;
7076
7077         /*  Move the atoms  */
7078         if (where == NULL) {
7079                 /*  Duplicate atoms to the end of the destination array  */
7080                 for (i = 0; i < nsrc; i++) {
7081                         ap = ATOM_AT_INDEX(dst->atoms, ndst + i);
7082                         if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7083                                 goto panic;
7084                         if (forUndo)  /*  For undo action, all bonds come from another undo action, so connection info are cleared */
7085                                 AtomConnectResize(&ap->connect, 0);
7086                 }
7087         } else {
7088                 /*  Duplicate to a temporary storage and then insert  */
7089                 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
7090                 if (tempatoms == NULL)
7091                         goto panic;
7092                 for (i = 0; i < nsrc; i++) {
7093                         ap = ATOM_AT_INDEX(tempatoms, i);
7094                         if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7095                                 goto panic;
7096                         if (forUndo)  /*  See above  */
7097                                 AtomConnectResize(&ap->connect, 0);                             
7098                 }
7099                 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
7100                         goto panic;
7101                 free(tempatoms);
7102         }
7103         dst->natoms = ndst + nsrc;
7104
7105         /*  Renumber the atom indices in connect[] and symbase, and modify the residue numbers  */
7106         for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7107                 if (new2old[i] < ndst) {
7108                         /*  This atom is from dst  */
7109                         n1 = 0;
7110                 } else {
7111                         /*  This atom is from src  */
7112                         n1 = ndst;  /*  Offset to the internal number  */
7113                         if (ap->resSeq != 0)
7114                                 ap->resSeq += resSeqOffset;  /*  Modify residue number  */
7115                 }
7116                 cp = AtomConnectData(&ap->connect);
7117                 for (j = 0; j < ap->connect.count; j++)
7118                         cp[j] = old2new[cp[j] + n1];
7119                 if (SYMOP_ALIVE(ap->symop))
7120                         ap->symbase = old2new[ap->symbase + n1];
7121                 if (ap->anchor != NULL) {
7122                         cp = AtomConnectData(&ap->anchor->connect);
7123                         for (j = 0; j < ap->anchor->connect.count; j++)
7124                                 cp[j] = old2new[cp[j] + n1];
7125                 }
7126         }
7127         
7128         /*  Move the bonds, angles, dihedrals, impropers  */
7129         for (i = 0; i < 4; i++) {
7130                 Int *nitems, *nitems_src;
7131                 Int **items, **items_src;
7132                 Int nsize;  /*  Number of Ints in one element  */
7133                 switch (i) {
7134                         case 0:
7135                                 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
7136                         case 1:
7137                                 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
7138                         case 2:
7139                                 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
7140                         case 3:
7141                                 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
7142                 }
7143                 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
7144                 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
7145                 if (forUndo) {
7146                         /*  During undo, no bonds etc. are copied from src; they will be taken care later
7147                             by undo actions  */
7148                         n1 = *nitems;
7149                         n2 = 0;
7150                 } else {
7151                         /*  Keep the old number of entries in dst, because it is updated by AssignArray()  */
7152                         n1 = *nitems;
7153                         /*  Also keep the old number of entries in src, in case src and dst point the same molecule  */
7154                         n2 = *nitems_src;
7155                         /*  Expand the array  */
7156                         if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
7157                                 goto panic;
7158                         /*  Copy the items  */
7159                         memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
7160                         if (i == 0) {
7161                                 /*  Copy the bond order info if present */
7162                                 Int nn1 = dst->nbondOrders;
7163                                 if (dst->bondOrders != NULL || src->bondOrders != NULL) {
7164                                         if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), dst->nbonds - 1, NULL) == NULL)
7165                                                 goto panic;
7166                                         memset(dst->bondOrders + nn1, 0, sizeof(Double) * (dst->nbonds - nn1));
7167                                         if (src->bondOrders != NULL)
7168                                                 memmove(dst->bondOrders + n1, src->bondOrders, sizeof(Double) * n2);
7169                                 }
7170                         }
7171                 }
7172                 /*  Renumber  */
7173                 for (j = 0; j < n1 * nsize; j++)
7174                         (*items)[j] = old2new[(*items)[j]];
7175                 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
7176                         (*items)[j] = old2new[(*items)[j] + ndst];
7177                 if (forUndo == 0 && actions != NULL) {
7178                         ig = IntGroupNewWithPoints(n1, n2, -1);
7179                         switch (i) {
7180                                 case 0: act = MolActionNew(gMolActionDeleteBonds, ig); break;
7181                                 case 1: act = MolActionNew(gMolActionDeleteAngles, ig); break;
7182                                 case 2: act = MolActionNew(gMolActionDeleteDihedrals, ig); break;
7183                                 case 3: act = MolActionNew(gMolActionDeleteImpropers, ig); break;
7184                         }
7185                         IntGroupRelease(ig);
7186                         AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7187                         act = NULL;
7188                 }
7189         }
7190         
7191         /*  Renumber existing parameters  */
7192         if (dst->par != NULL) {
7193                 int type;
7194                 for (type = kFirstParType; type <= kLastParType; type++) {
7195                         UnionPar *up1;
7196                         n1 = ParameterGetCountForType(dst->par, type);
7197                         for (i = 0; i < n1; i++) {
7198                                 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7199                                 ParameterRenumberAtoms(type, up1, ndst, old2new);
7200                         }
7201                 }
7202         }
7203
7204         /*  Merge parameters from src  */
7205         if (src->par != NULL && forUndo == 0) {
7206                 UnionPar *up1, *up2;
7207                 int type;
7208                 if (dst->par == NULL)
7209                         dst->par = ParameterNew();
7210                 else {
7211                         /*  Renumber existing parameters  */
7212                         for (type = kFirstParType; type <= kLastParType; type++) {
7213                                 n1 = ParameterGetCountForType(dst->par, type);
7214                                 for (i = 0; i < n1; i++) {
7215                                         up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7216                                         ParameterRenumberAtoms(type, up1, ndst, old2new);
7217                                 }
7218                         }
7219                 }
7220                 ig = IntGroupNew();
7221                 for (type = kFirstParType; type <= kLastParType; type++) {
7222                         n1 = ParameterGetCountForType(src->par, type);
7223                         n2 = ParameterGetCountForType(dst->par, type);
7224                         if (n1 == 0)
7225                                 continue;
7226                         /*  Determine which parameter should be copied from src to dst  */
7227                         for (i = 0; i < n1; i++) {
7228                                 UInt types[4];
7229                                 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
7230                                 n3 = ParameterGetAtomTypes(type, up1, types);
7231                                 for (j = 0; j < n3; j++) {
7232                                         /*  If it includes explicit atom index, then it should be copied  */
7233                                         if (types[j] < kAtomTypeMinimum) {
7234                                                 IntGroupAdd(ig, i, 1);
7235                                                 break;
7236                                         }
7237                                 }
7238                                 if (j == n3) {
7239                                         for (j = 0; j < n2; j++) {
7240                                                 up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
7241                                                 if (ParameterCompare(up1, up2, type))
7242                                                         break;
7243                                         }
7244                                         if (j >= n2)
7245                                                 /*  This is an unknown parameter; should be copied  */
7246                                                 IntGroupAdd(ig, i, 1);
7247                                 }
7248                         }
7249                         n1 = IntGroupGetCount(ig);
7250                         if (n1 == 0)
7251                                 continue;
7252                         up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
7253                         if (up1 == NULL)
7254                                 goto panic;
7255                         /*  Copy parameters and renumber indices if necessary  */
7256                         for (i = j = 0; i < n1; i++) {
7257                                 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
7258                                 if (up2 == NULL)
7259                                         continue;
7260                                 up1[j] = *up2;
7261                                 ParameterRenumberAtoms(type, up1 + j, nsrc, old2new + ndst);
7262                                 j++;
7263                         }
7264                         /*  Merge parameters  */
7265                         IntGroupClear(ig);
7266                         IntGroupAdd(ig, n2, j);
7267                         if (ParameterInsert(dst->par, type, up1, ig) < j)
7268                                 goto panic;
7269                         if (actions != NULL) {
7270                                 act = MolActionNew(gMolActionDeleteParameters, type, ig);
7271                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7272                                 act = NULL;
7273                         }
7274                         IntGroupClear(ig);
7275                         free(up1);
7276                 }
7277                 IntGroupRelease(ig);
7278         }
7279         
7280         /*  Copy the residues if necessary  */
7281         /*  src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
7282             However, 1+resSeqOffset should not overwrite the existing residue in dst;
7283                 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1].  */
7284         if (forUndo == 0) {
7285                 n1 = dst->nresidues;
7286                 if (1 + resSeqOffset < n1) {
7287                         n2 = n1;
7288                 } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
7289                 if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
7290                         if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
7291                                 goto panic;
7292                         memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
7293                         if (nactions != NULL) {
7294                                 act = MolActionNew(gMolActionChangeNumberOfResidues, n1);
7295                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7296                                 act = NULL;
7297                         }
7298                 }
7299         }
7300
7301         MoleculeCleanUpResidueTable(dst);
7302         
7303         free(new2old);
7304         dst->nframes = -1;  /*  Should be recalculated later  */
7305
7306         MoleculeIncrementModifyCount(dst);
7307         dst->needsMDRebuild = 1;
7308         __MoleculeUnlock(dst);
7309         return 0;
7310
7311   panic:
7312         __MoleculeUnlock(dst);
7313     Panic("Low memory while adding atoms");
7314         return 1;  /*  Not reached  */
7315 }
7316
7317 /*  Unmerge the molecule. If necessary, the undo actions are stored in nactions/actions array.
7318     (The nactions/actions array must be initialized by the caller)  */
7319 static int
7320 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag, Int *nactions, MolAction ***actions, Int forUndo)
7321 {
7322         Int nsrc, ndst, nsrcnew;
7323         Int i, j, n1, n2, n3, n4, *cp;
7324         Int *new2old, *old2new;
7325         IntGroup *move_g, *del_g, *remain_g, *dst_par_g, *remove_par_g;
7326         Molecule *dst;
7327         Atom *ap, *dst_ap;
7328         UnionPar *up;
7329         MolAction *act;
7330
7331         if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
7332                 /*  Do nothing  */
7333                 if (dstp != NULL)
7334                         *dstp = NULL;
7335                 return 0;
7336         }
7337         
7338         if (src->noModifyTopology && moveFlag)
7339                 return 1;  /*  Prohibit editing  */
7340
7341         if ((ndst = IntGroupGetCount(where)) > src->natoms)
7342                 return 1;  /*  Bad parameter  */
7343
7344         __MoleculeLock(src);
7345         
7346         act = NULL;
7347         
7348         nsrc = src->natoms;
7349         nsrcnew = nsrc - ndst;
7350         if (resSeqOffset < 0)
7351                 resSeqOffset = 0;
7352
7353         /*  Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
7354             and nsrcnew..nsrc-1 are for atoms moved into dst.  */ 
7355         new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
7356         if (new2old == NULL)
7357                 goto panic;
7358         old2new = new2old + nsrc;
7359         n1 = 0;  /*  src index  */
7360         n2 = 0;  /*  dst index  */
7361         n3 = 0;  /*  src index after "unmerge"  */
7362         i = 0;
7363         while (n1 < nsrc || n2 < ndst) {
7364                 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
7365                         n4 = nsrc - n1;
7366                 else n4 -= n1;
7367                 /*  n4 elements from src[n1] will go to unmerged[n3]  */
7368                 for (j = 0; j < n4; j++) {
7369                         old2new[n1 + j] = n3 + j;
7370                         new2old[n3 + j] = n1 + j;
7371                 }
7372                 n3 += n4;
7373                 n1 += n4;
7374                 if ((n4 = IntGroupGetInterval(where, i)) < 0)
7375                         n4 = nsrc - n1;
7376                 /*  n4 elements from src[n1] will go to dst[n2]  */
7377                 for (j = 0; j < n4; j++) {
7378                         old2new[n1 + j] = nsrcnew + n2 + j;
7379                         new2old[nsrcnew + n2 + j] = n1 + j;
7380                 }
7381                 n1 += n4;
7382                 n2 += n4;
7383                 i++;
7384         }
7385
7386         /*  Atoms to remain in the source group  */
7387         if (moveFlag) {
7388                 remain_g = IntGroupNewWithPoints(0, nsrc, -1);
7389                 IntGroupRemoveIntGroup(remain_g, where);
7390         } else remain_g = NULL;
7391         
7392         /*  Find parameters to be moved to the dst (dst_par_g), and to be removed from the src (remove_par_g) */
7393         if (src->par != NULL) {
7394                 dst_par_g = IntGroupNew();
7395                 if (moveFlag)
7396                         remove_par_g = IntGroupNew();
7397                 else remove_par_g = NULL;
7398                 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7399                         n2 = ParameterGetCountForType(src->par, n1);
7400                         if (n2 == 0)
7401                                 continue;
7402                         for (i = 0; i < n2; i++) {
7403                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7404                                 if (ParameterIsRelevantToAtomGroup(n1, up, src->atoms, where)) {
7405                                         /*  This parameter is to be copied to dst  */
7406                                         IntGroupAdd(dst_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7407                                 }
7408                                 if (moveFlag && !ParameterIsRelevantToAtomGroup(n1, up, src->atoms, remain_g)) {
7409                                         /*  This parameter is to be removed  */
7410                                         IntGroupAdd(remove_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7411                                 }
7412                         }
7413                 }
7414         } else dst_par_g = remove_par_g = NULL;
7415         
7416         /*  Pi anchors should be modified if the anchor and its component atoms become separated between
7417             src anc dst  */
7418         if (moveFlag) {
7419                 Int ibufsize, *ibuf, flag_i, flag_j;
7420                 ibufsize = 8;
7421                 ibuf = (Int *)malloc(sizeof(Int) * ibufsize);
7422                 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7423                         if (ap->anchor == NULL)
7424                                 continue;
7425                         flag_i = (old2new[i] < nsrcnew);
7426                         cp = AtomConnectData(&ap->anchor->connect);
7427                         for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7428                                 flag_j = (old2new[cp[j]] < nsrcnew);
7429                                 if (flag_i == flag_j) {
7430                                         if (n1 >= ibufsize) {
7431                                                 ibufsize += 8;
7432                                                 ibuf = (Int *)realloc(ibuf, sizeof(Int) * ibufsize);
7433                                         }
7434                                         ibuf[n1++] = cp[j];
7435                                 }
7436                         }
7437                         if (n1 < j) {
7438                                 /*  Need to modify the pi anchor list  */
7439                                 if (n1 <= 1)
7440                                         n1 = 0;
7441                                 MolActionCreateAndPerform(src, SCRIPT_ACTION("isI"), "set_atom_attr", i, "anchor_list", n1, ibuf);
7442                         }
7443                 }
7444         }
7445         
7446         /*  Make a new molecule  */
7447         if (dstp != NULL) {
7448                 dst = MoleculeNew();
7449                 if (dst == NULL)
7450                         goto panic;
7451                 /*  Expand the destination array  */
7452                 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
7453                         goto panic;
7454                 dst_ap = dst->atoms;
7455         } else {
7456                 dst = NULL;
7457                 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
7458                 if (dst_ap == NULL)
7459                         goto panic;
7460         }
7461         
7462         /*  Move the atoms  */
7463         if (moveFlag) {
7464                 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
7465                         goto panic;
7466                 src->natoms = nsrcnew;
7467                 if (dst == NULL) {
7468                         /*  The atom record must be deallocated correctly  */
7469                         for (i = 0; i < ndst; i++)
7470                                 AtomClean(ATOM_AT_INDEX(dst_ap, i));
7471                 }
7472         } else {
7473                 if (dst != NULL) {
7474                         for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
7475                                 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
7476                 }
7477         }
7478         
7479         if (dst == NULL) {
7480                 /*  The dummy destination array is no longer needed  */
7481                 free(dst_ap);
7482                 dst_ap = NULL;
7483         }
7484         
7485         /*  Renumber the atom indices in connect[] (src) */
7486         if (moveFlag) {
7487                 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7488                         cp = AtomConnectData(&ap->connect);
7489                         for (j = n1 = 0; j < ap->connect.count; j++) {
7490                                 n2 = old2new[cp[j]];
7491                                 if (n2 < nsrcnew)
7492                                         cp[n1++] = n2;
7493                         }
7494                         AtomConnectResize(&ap->connect, n1);
7495                         if (ap->anchor != NULL) {
7496                                 cp = AtomConnectData(&ap->anchor->connect);
7497                                 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7498                                         n2 = old2new[cp[j]];
7499                                         if (n2 < nsrcnew)
7500                                                 cp[n1++] = n2;
7501                                 }
7502                                 if (n1 != ap->anchor->connect.count) {
7503                                         /*  This should not happen!!  */
7504                                         AtomConnectResize(&ap->anchor->connect, n1);
7505                                         fprintf(stderr, "Internal error in sMoleculeUnmergeSub (line %d)\n", __LINE__);
7506                                         if (n1 == 0) {
7507                                                 free(ap->anchor->coeffs);
7508                                                 free(ap->anchor);
7509                                                 ap->anchor = NULL;
7510                                         }
7511                                 }
7512                         }
7513                 }
7514         }
7515         
7516         /*  Renumber the atom indices in connect[] (dst)  */
7517         if (dst != NULL) {
7518                 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7519                         if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
7520                                 ap->resSeq -= resSeqOffset;
7521                         else ap->resSeq = 0;
7522                         cp = AtomConnectData(&ap->connect);
7523                         for (j = n1 = 0; j < ap->connect.count; j++) {
7524                                 n2 = old2new[cp[j]] - nsrcnew;
7525                                 if (n2 >= 0)
7526                                         cp[n1++] = n2;
7527                         }
7528                         AtomConnectResize(&ap->connect, n1);
7529                         if (ap->anchor != NULL) {
7530                                 cp = AtomConnectData(&ap->anchor->connect);
7531                                 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7532                                         n2 = old2new[cp[j]] - nsrcnew;
7533                                         if (n2 >= 0)
7534                                                 cp[n1++] = n2;
7535                                 }
7536                                 if (n1 != ap->anchor->connect.count) {
7537                                         /*  This can happen, and the anchor info is silently modified  */
7538                                         if (n1 <= 1) {
7539                                                 AtomConnectResize(&ap->anchor->connect, 0);
7540                                                 free(ap->anchor->coeffs);
7541                                                 free(ap->anchor);
7542                                                 ap->anchor = NULL;
7543                                         } else {
7544                                                 Double d;
7545                                                 AtomConnectResize(&ap->anchor->connect, n1);
7546                                                 d = 0.0;
7547                                                 for (j = 0; j < n1; j++)
7548                                                         d += ap->anchor->coeffs[j];
7549                                                 for (j = 0; j < n1; j++)
7550                                                         ap->anchor->coeffs[j] /= d;
7551                                                 MoleculeCalculatePiAnchorPosition(dst, i);
7552                                         }
7553                                 }
7554                         }
7555                 }
7556         }
7557
7558         /*  Separate the bonds, angles, dihedrals, impropers  */
7559         /*  TODO: Improper torsions should also be copied!  */
7560         move_g = IntGroupNew();
7561         if (move_g == NULL)
7562                 goto panic;
7563         for (i = 3; i >= 0; i--) {
7564                 Int *nitems, *nitems_dst;
7565                 Int **items, **items_dst;
7566                 Int nsize;  /*  Number of Ints in one element  */
7567                 unsigned char *counts;
7568                 del_g = IntGroupNew();
7569                 switch (i) {
7570                         case 0:
7571                                 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
7572                         case 1:
7573                                 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
7574                         case 2:
7575                                 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
7576                         case 3:
7577                                 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
7578                         default:
7579                                 nitems = NULL; items = NULL; nsize = 0; break;  /*  Not reached  */
7580                 }
7581                 if (dst != NULL) {
7582                         nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
7583                         items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
7584                 } else {
7585                         nitems_dst = NULL;
7586                         items_dst = NULL;
7587                 }
7588                 counts = (unsigned char *)calloc(1, *nitems);
7589                 /*  Find the entries that should be moved to dst  */
7590                 n2 = 0;
7591                 for (j = 0; j < *nitems * nsize; j++) {
7592                         n1 = old2new[(*items)[j]];
7593                         if (n1 >= nsrcnew)
7594                                 counts[j / nsize]++; /* Count the atom belonging to dst */ 
7595                 }
7596                 for (j = n2 = n3 = 0; j < *nitems; j++) {
7597                         if (counts[j] > 0) {
7598                                 /*  Remove from src  */
7599                                 n2++;
7600                                 if (IntGroupAdd(del_g, j, 1) != 0)
7601                                         goto panic;
7602                                 if (counts[j] == nsize) {
7603                                         /*  Move to dst  */
7604                                         n3++;
7605                                         if (IntGroupAdd(move_g, j, 1) != 0)
7606                                                 goto panic;
7607                                 }
7608                         }
7609                 }
7610                 if (n2 > 0) {
7611                         /*  Expand the destination array  */
7612                         if (items_dst != NULL && n3 > 0) {
7613                                 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
7614                                         goto panic;
7615                                 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
7616                                         goto panic;
7617                                 if (i == 0 && src->bondOrders != NULL) {
7618                                         if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), n3 - 1, NULL) == NULL)
7619                                                 goto panic;
7620                                         if (sCopyElementsFromArrayAtPositions(src->bondOrders, src->nbondOrders, dst->bondOrders, sizeof(Double), move_g) != 0)
7621                                                 goto panic;
7622                                 }
7623                         }
7624                         /*  Remove from src  */
7625                         if (moveFlag && forUndo == 0) {
7626                                 if (nactions != NULL) {
7627                                         Int k, *ip;
7628                                         Double *dp;
7629                                         ip = (Int *)malloc(sizeof(Int) * nsize * n2);
7630                                         for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
7631                                                 memmove(ip + j * nsize, *items + k * nsize, sizeof(Int) * nsize);
7632                                         if (i == 0 && src->bondOrders != NULL) {
7633                                                 dp = (Double *)malloc(sizeof(Double) * n2);
7634                                                 for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
7635                                                         dp[j] = src->bondOrders[k];
7636                                         } else dp = NULL;
7637                                         switch (i) {
7638                                                 case 0:
7639                                                         act = MolActionNew(gMolActionAddBondsForUndo, n2 * nsize, ip, del_g); break;
7640                                                 case 1:
7641                                                         act = MolActionNew(gMolActionAddAngles, n2 * nsize, ip, del_g); break;
7642                                                 case 2:
7643                                                         act = MolActionNew(gMolActionAddDihedrals, n2 * nsize, ip, del_g); break;
7644                                                 case 3:
7645                                                         act = MolActionNew(gMolActionAddImpropers, n2 * nsize, ip, del_g); break;
7646                                         }
7647                                         if (act != NULL) {
7648                                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7649                                                 act = NULL;
7650                                         }
7651                                         free(ip);
7652                                         if (dp != NULL) {
7653                                                 act = MolActionNew(gMolActionAssignBondOrders, n2, dp, del_g);
7654                                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7655                                                 act = NULL;
7656                                                 free(dp);
7657                                         }
7658                                 }
7659                                 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
7660                                         goto panic;
7661                                 (*nitems) -= n2;
7662                         }
7663                 }
7664                 /*  Renumber the entries  */
7665                 if (moveFlag) {
7666                         for (j = 0; j < *nitems * nsize; j++) {
7667                                 (*items)[j] = old2new[(*items)[j]];
7668                         }
7669                 }
7670                 if (items_dst != NULL) {
7671                         for (j = 0; j < *nitems_dst * nsize; j++) {
7672                                 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
7673                         }
7674                 }
7675                 free(counts);
7676                 IntGroupClear(move_g);
7677                 IntGroupRelease(del_g);
7678         }
7679         IntGroupRelease(move_g);
7680         
7681         /*  Copy the residues  */
7682         if (dst != NULL) {
7683                 /*  src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset)  */
7684                 n1 = src->nresidues - resSeqOffset;  /*  This will be dst->nresidues (if >0)  */
7685                 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
7686                         goto panic;
7687                 if (n1 > 1) {
7688                         memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
7689                 }
7690         }
7691
7692         /*  Copy the parameters to dst */
7693         if (dst != NULL && dst_par_g != NULL && (n2 = IntGroupGetCount(dst_par_g)) > 0) {
7694                 IntGroup *dst_new_g = IntGroupNew();
7695                 Int dst_par_count[kLastParType - kFirstParType + 1];
7696                 if (dst_new_g == NULL)
7697                         goto panic;
7698                 for (i = 0; i <= kLastParType - kFirstParType; i++)
7699                         dst_par_count[i] = 0;
7700                 up = (UnionPar *)calloc(sizeof(UnionPar), n2);
7701                 if (up == NULL)
7702                         goto panic;
7703                 if (ParameterCopy(src->par, kFirstParType, up, dst_par_g) < n2)
7704                         goto panic;
7705                 /*  Renumber the explicit atom indices  */
7706                 for (i = 0; i < nsrc; i++)
7707                         old2new[i] -= nsrcnew;  /*  new indices for atoms in dst; otherwise negative numbers  */
7708                 for (i = 0; i < n2; i++) {
7709                         /*  Renumber the indices, and count the number of parameters for each type  */
7710                         n1 = kFirstParType + IntGroupGetNthPoint(dst_par_g, i) / kParameterIndexOffset;
7711                         dst_par_count[n1 - kFirstParType]++;
7712                         ParameterRenumberAtoms(n1, up + i, nsrc, old2new);
7713                 }
7714                 for (i = 0; i < nsrc; i++)
7715                         old2new[i] += nsrcnew;
7716                 if (dst->par == NULL)
7717                         dst->par = ParameterNew();
7718                 for (i = 0; i <= kLastParType - kFirstParType; i++) {
7719                         if (dst_par_count[i] > 0)
7720                                 IntGroupAdd(dst_new_g, i * kParameterIndexOffset, dst_par_count[i]);
7721                 }
7722                 if (ParameterInsert(dst->par, kFirstParType, up, dst_new_g) < n2)
7723                         goto panic;
7724                 free(up);
7725                 IntGroupRelease(dst_new_g);
7726         }
7727         IntGroupRelease(dst_par_g);
7728
7729         /*  Remove the unused parameter. Note: the parameters that are in remove_par_g and not in 
7730             dst_par_g will disappear. To support undo, these parameters should be taken care separately.  */
7731         if (forUndo == 0 && remove_par_g != NULL && (n2 = IntGroupGetCount(remove_par_g)) > 0) {
7732                 UnionPar *up = (UnionPar *)malloc(sizeof(UnionPar) * n2);
7733                 ParameterDelete(src->par, kFirstParType, up, remove_par_g);
7734                 if (nactions != NULL) {
7735                         act = MolActionNew(gMolActionAddParameters, kFirstParType, remove_par_g, n2, up);
7736                         AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7737                         act = NULL;
7738                 }
7739                 free(up);
7740         }
7741         IntGroupRelease(remove_par_g);
7742         
7743         /*  Renumber the parameter records remaining in the src  */
7744         if (moveFlag) {
7745                 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7746                         n2 = ParameterGetCountForType(src->par, n1);
7747                         for (i = 0; i < n2; i++) {
7748                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7749                                 ParameterRenumberAtoms(n1, up, nsrc, old2new);
7750                         }
7751                 }
7752         }
7753
7754         /*  Clean up  */
7755         IntGroupRelease(remain_g);
7756         MoleculeCleanUpResidueTable(src);
7757         if (dst != NULL)
7758                 MoleculeCleanUpResidueTable(dst);
7759         free(new2old);
7760
7761         src->nframes = -1;  /*  Should be recalculated later  */
7762         if (dst != NULL)
7763                 dst->nframes = -1;  /*  Should be recalculated later  */
7764
7765         
7766         if (dstp != NULL)
7767                 *dstp = dst;
7768
7769         MoleculeIncrementModifyCount(src);
7770         src->needsMDRebuild = 1;
7771         __MoleculeUnlock(src);
7772         
7773         return 0;
7774
7775   panic:
7776         __MoleculeUnlock(src);
7777 /*    Panic("Low memory while removing atoms"); */
7778         return -1;
7779 }
7780
7781 /*  Separate molecule into two parts. The atoms specified by 'where' are moved
7782     from src to a new molecule, which is returned as *dstp. Dstp can be NULL, 
7783         in which case the moved atoms are discarded.  */
7784 int
7785 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7786 {
7787         return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1, nactions, actions, forUndo);
7788 }
7789
7790 /*  Extract atoms from a given molecule into two parts. The atoms specified by 
7791         'where' are copied from src to a new molecule, which is returned as *dstp.
7792     If dummyFlag is non-zero, then the atoms that are not included in the group 
7793         but are connected to any atoms in the group are converted to "dummy" atoms 
7794         (i.e. with element "Du" and names beginning with an underscore) and included 
7795         in the new molecule object.  */
7796 int
7797 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
7798 {
7799         int retval;
7800
7801         /*  Extract the fragment  */
7802         retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0, NULL, NULL, 0);
7803         if (retval != 0)
7804                 return retval;
7805
7806         if (dummyFlag) {
7807
7808                 /*  Search bonds crossing the molecule border  */
7809                 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
7810                 if (ig != NULL) {
7811                         IntGroupIterator iter;
7812                         Int i, idx;
7813                         idx = 1;
7814                         IntGroupIteratorInit(ig, &iter);
7815                         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7816                                 /*  The atoms at the border  */
7817                                 Int n1, n2, nn[3];
7818                                 Atom a, *ap;
7819                                 n1 = src->bonds[i*2];
7820                                 n2 = src->bonds[i*2+1];
7821                                 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
7822                                         int w = n1;
7823                                         n1 = n2;
7824                                         n2 = w;
7825                                         if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
7826                                                 continue;  /*  Actually this is an internal error  */
7827                                 }
7828                                 /*  n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule  */
7829                                 /*  Create a new dummy atom with the same segment/residue info with n1
7830                                     and the same position as n2  */
7831                                 ap = ATOM_AT_INDEX(src->atoms, n1);
7832                                 memset(&a, 0, gSizeOfAtomRecord);
7833                                 a.segSeq = ap->segSeq;
7834                                 memmove(a.segName, ap->segName, 4);
7835                                 a.resSeq = ap->resSeq;
7836                                 memmove(a.resName, ap->resName, 4);
7837                                 ElementToString(0, a.element);  /*  "Du"  */
7838                                 snprintf(a.aname, 4, "_%d", idx++);
7839                                 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
7840                                 /*  Add the dummy atom to the new molecule; nn[1] is the index
7841                                     of the new dummy atom in the new molecule  */
7842                                 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
7843                                 /*  Connect nn1 and nn2  */
7844                                 nn[2] = kInvalidIndex;
7845                                 MoleculeAddBonds(*dstp, 1, nn, NULL, 1);
7846                         }
7847                         IntGroupIteratorRelease(&iter);
7848                         IntGroupRelease(ig);
7849                 }
7850         }
7851         
7852         return 0;
7853 }
7854
7855 int
7856 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds, IntGroup *where, Int autoGenerate)
7857 {
7858         Int nangles, ndihedrals;
7859         Int *angles, *dihedrals;
7860         Int i, j, k, kk, n1, n2, cn1, cn2;
7861         Int *cp1, *cp2;
7862         Int temp[4];
7863         Atom *ap1, *ap2, *ap3;
7864         
7865         if (mp == NULL || bonds == NULL || nbonds <= 0)
7866                 return 0;
7867         if (mp->noModifyTopology)
7868                 return -4;  /*  Prohibited operation  */
7869
7870         /*  Note: Duplicates and validity are not checked (the caller must do that)  */
7871
7872         __MoleculeLock(mp);
7873
7874         n1 = mp->nbonds;
7875         if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, n1 + nbonds - 1, NULL) == NULL
7876                 || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nbonds, sizeof(Int) * 2, where) != 0) {
7877                 __MoleculeUnlock(mp);
7878                 return -4;  /*  Out of memory  */
7879         }
7880         if (mp->bondOrders != NULL) {
7881                 /*  Expand the bond order info (all new entries are zero)  */
7882                 Double *dp = (Double *)calloc(sizeof(Double), nbonds);
7883                 if (dp == NULL)
7884                         return -4;
7885                 if (AssignArray(&(mp->bondOrders), &(mp->nbondOrders), sizeof(Double), n1 + nbonds - 1, NULL) == NULL
7886                         || sInsertElementsToArrayAtPositions(mp->bondOrders, n1, dp, nbonds, sizeof(Double), where) != 0) {
7887                         __MoleculeUnlock(mp);
7888                         free(dp);
7889                         return -4;
7890                 }
7891                 free(dp);
7892         }
7893         
7894         angles = dihedrals = NULL;
7895         nangles = ndihedrals = 0;
7896         
7897         /*  Add connects[], and angles/dihedrals (if autoGenerate is true)  */
7898         for (i = 0; i < nbonds; i++) {
7899                 
7900                 /*  One entry at time  */
7901                 /*  (Otherwise, duplicate entries of angles and dihedrals result)  */
7902                 n1 = bonds[i * 2];
7903                 n2 = bonds[i * 2 + 1];
7904                 
7905                 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
7906                 AtomConnectInsertEntry(&ap1->connect, -1, n2);
7907                 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
7908                 AtomConnectInsertEntry(&ap2->connect, -1, n1);
7909         
7910                 /*  Add angles and dihedrals  */
7911                 if (autoGenerate) {
7912                         AtomConnect *ac1, *ac2;
7913                         if (ap1->anchor == NULL || ap2->anchor == NULL) {
7914                                 /*  N1-N2-{XY} or N2-N1-{XY} angles (X: connected atom, Y: constitute atom of pi-anchor)  */
7915                                 for (j = 0; j < 4; j++) {
7916                                         switch (j) {
7917                                                 case 0: temp[0] = n1; temp[1] = n2; ac1 = &ap2->connect; break;  /* N1-N2-X */
7918                                                 case 1: if (ap2->anchor == NULL) continue; else ac1 = &ap2->anchor->connect; break; /* N1-N2-Y */
7919                                                 case 2: temp[0] = n2; temp[1] = n1; ac1 = &ap1->connect; break;  /* N2-N1-X */
7920                                                 case 3: if (ap1->anchor == NULL) continue; else ac1 = &ap1->anchor->connect; break; /* N2-N1-Y */
7921                                         }
7922                                         cp1 = AtomConnectData(ac1);
7923                                         cn1 = ac1->count;
7924                                         for (k = 0; k < cn1; k++) {
7925                                                 temp[2] = cp1[k];
7926                                                 if (temp[2] == temp[0])
7927                                                         continue;
7928                                                 ap3 = ATOM_AT_INDEX(mp->atoms, temp[2]);
7929                                                 if (ap3->anchor != NULL) {
7930                                                         /*  Avoid X-anchor-anchor angle (anchor-X-anchor is allowed)  */
7931                                                         if ((j < 2 && ap2->anchor != NULL) || (j >= 2 && ap1->anchor != NULL))
7932                                                                 continue;
7933                                                 }
7934                                                 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7935                                                         goto panic;
7936                                                 /*  Dihedrals N1-N2-X-{XY} or N2-N1-X-{XY}  */
7937                                                 if (j == 1 || j == 3)
7938                                                         continue;
7939                                                 cp2 = AtomConnectData(&ap3->connect);
7940                                                 for (kk = 0; kk < ap3->connect.count; kk++) {
7941                                                         temp[3] = cp2[kk];
7942                                                         if (temp[3] == temp[0] || temp[3] == temp[1])
7943                                                                 continue;
7944                                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7945                                                                 goto panic;
7946                                                 }
7947                                                 if (ap3->anchor != NULL) {
7948                                                         /*  N1-N2-X-Y or N2-N1-X-Y  */
7949                                                         /*  for Y, only the first constitute atom is considered  */
7950                                                         cp2 = AtomConnectData(&ap3->anchor->connect);
7951                                                         temp[3] = cp2[0];
7952                                                         if (temp[3] == temp[0] || temp[3] == temp[1])
7953                                                                 continue;
7954                                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7955                                                                 goto panic;
7956                                                 }
7957                                         }
7958                                 }
7959                         }
7960                         /*  X-N1-N2-X dihedrals  */
7961                         /*  Y-N1-N2-anchor is allowed, but the force may be zero if the angle N1-N2-anchor is */
7962                         /*  close to 180 deg (e.g. in ferrocene, C-anchor-Fe-anchor dihedral should be k=0)  */
7963                         if (ap1->anchor == NULL) {
7964                                 ac1 = &ap1->connect;
7965                                 cn1 = ac1->count;
7966                         } else {
7967                                 ac1 = &ap1->anchor->connect;
7968                                 cn1 = 1;  /*  Only the first constitute atom of pi-anchor is considered  */
7969                         }
7970                         if (ap2->anchor == NULL) {
7971                                 ac2 = &ap2->connect;
7972                                 cn2 = ac2->count;
7973                         } else {
7974                                 ac2 = &ap2->anchor->connect;
7975                                 cn2 = 1;  /*  Only the first constitute atom of pi-anchor is considered  */
7976                         }
7977                         temp[1] = n1;
7978                         temp[2] = n2;
7979                         cp1 = AtomConnectData(ac1);
7980                         cp2 = AtomConnectData(ac2);
7981                         for (j = 0; j < cn1; j++) {
7982                                 temp[0] = cp1[j];
7983                                 if (temp[0] == temp[2])
7984                                         continue;
7985                                 for (k = 0; k < cn2; k++) {
7986                                         temp[3] = cp2[k];
7987                                         if (temp[3] == temp[0] || temp[3] == temp[1])
7988                                                 continue;
7989                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7990                                                 goto panic;
7991                                 }
7992                         }
7993                 }
7994         }
7995         
7996         if (angles != NULL) {
7997                 temp[0] = kInvalidIndex;
7998                 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7999                         goto panic;
8000                 MoleculeAddAngles(mp, angles, NULL);
8001                 free(angles);
8002         }
8003         if (dihedrals != NULL) {
8004                 temp[0] = kInvalidIndex;
8005                 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8006                         goto panic;
8007                 MoleculeAddDihedrals(mp, dihedrals, NULL);
8008                 free(dihedrals);
8009         }
8010
8011         MoleculeIncrementModifyCount(mp);
8012         mp->needsMDRebuild = 1;
8013         __MoleculeUnlock(mp);
8014
8015         return nbonds;
8016
8017   panic:
8018         __MoleculeUnlock(mp);
8019         Panic("Low memory while adding bonds");
8020         return -1;  /*  Not reached  */
8021 }
8022
8023 /*  Delete bonds  */
8024 /*  The deleted angles and dihedrals are stored in outRemoval.  */
8025 /*  (*outRemoval) is an array of integers, containing:
8026       [0..na*3-1]: the angle indices
8027       [na*3..na*3+nd*4-1]: the dihedral indices
8028           [na*3+nd*4..na*3+nd*4+ni*4-1]: the improper indices
8029     *outRemovedPos is an intgroup denoting the positions of the removed angles/dihedrals/impropers.
8030           the angle indices are included as they are,
8031       the dihedral indices are offset by ATOMS_MAX_NUMBER,
8032       the improper indices are offset by ATOMS_MAX_NUMBER*2.
8033     Note: the removed bond indices are not returned, because the caller should already know them.  */
8034 int
8035 MoleculeDeleteBonds(Molecule *mp, Int *bonds, IntGroup *where, Int **outRemoved, IntGroup **outRemovedPos)
8036 {
8037         Int i, j, n1, n2, nw;
8038         Int *ip, *jp, na, nd, ni;
8039         IntGroup *ag, *dg, *ig;
8040         Atom *ap;
8041         IntGroupIterator iter;
8042
8043         if (mp == NULL)
8044                 return 0;
8045         if (mp->noModifyTopology)
8046                 return -4;  /*  Prohibited operation  */
8047
8048         __MoleculeLock(mp);
8049
8050         /*  Update connects[]  */
8051         IntGroupIteratorInit(where, &iter);
8052         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8053                 n1 = mp->bonds[i * 2];
8054                 n2 = mp->bonds[i * 2 + 1];
8055                 ap = ATOM_AT_INDEX(mp->atoms, n1);
8056                 ip = AtomConnectData(&ap->connect);
8057                 for (j = 0; j < ap->connect.count; j++) {
8058                         if (ip[j] == n2) {
8059                                 AtomConnectDeleteEntry(&ap->connect, j);
8060                                 break;
8061                         }
8062                 }
8063                 ap = ATOM_AT_INDEX(mp->atoms, n2);
8064                 ip = AtomConnectData(&ap->connect);
8065                 for (j = 0; j < ap->connect.count; j++) {
8066                         if (ip[j] == n1) {
8067                                 AtomConnectDeleteEntry(&ap->connect, j);
8068                                 break;
8069                         }
8070                 }
8071         }
8072         
8073         /*  Remove bonds, angles, dihedrals, impropers  */
8074         ag = IntGroupNew();
8075         dg = ig = NULL;
8076         na = nd = ni = 0;
8077         
8078         nw = IntGroupGetCount(where);
8079         jp = (Int *)malloc(sizeof(Int) * nw * 2);
8080         j = 0;
8081         IntGroupIteratorReset(&iter);
8082         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8083                 jp[j++] = mp->bonds[i * 2];
8084                 jp[j++] = mp->bonds[i * 2 + 1];
8085         }
8086         IntGroupIteratorRelease(&iter);
8087
8088         for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8089                 for (j = 0; j < nw; j++) {
8090                         n1 = jp[j * 2];
8091                         n2 = jp[j * 2 + 1];
8092                         if ((ip[0] == n1 && ip[1] == n2)
8093                                 || (ip[1] == n1 && ip[0] == n2)
8094                                 || (ip[1] == n1 && ip[2] == n2)
8095                                 || (ip[2] == n1 && ip[1] == n2)) {
8096                                 if (IntGroupAdd(ag, i, 1) != 0)
8097                                         goto panic;
8098                                 na++;
8099                                 break;
8100                         }
8101                 }
8102         }
8103         for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8104                 for (j = 0; j < nw; j++) {
8105                         n1 = jp[j * 2];
8106                         n2 = jp[j * 2 + 1];
8107                         if ((ip[0] == n1 && ip[1] == n2)
8108                          || (ip[1] == n1 && ip[0] == n2)
8109                          || (ip[1] == n1 && ip[2] == n2)
8110                          || (ip[2] == n1 && ip[1] == n2)
8111                          || (ip[2] == n1 && ip[3] == n2)
8112                          || (ip[3] == n1 && ip[2] == n2)) {
8113                                 if (dg == NULL)
8114                                         dg = IntGroupNew();
8115                                 if (IntGroupAdd(dg, i, 1) != 0)
8116                                         goto panic;
8117                                 nd++;
8118                                 break;
8119                         }
8120                 }
8121         }
8122         for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8123                 for (j = 0; j < nw; j++) {
8124                         n1 = jp[j * 2];
8125                         n2 = jp[j * 2 + 1];
8126                         if ((ip[0] == n1 && ip[2] == n2)
8127                          || (ip[1] == n1 && ip[2] == n2)
8128                          || (ip[3] == n1 && ip[2] == n2)
8129                          || (ip[0] == n2 && ip[2] == n1)
8130                          || (ip[1] == n2 && ip[2] == n1)
8131                          || (ip[3] == n2 && ip[2] == n1)) {
8132                                 if (ig == NULL)
8133                                         ig = IntGroupNew();
8134                                 if (IntGroupAdd(ig, i, 1) != 0)
8135                                         goto panic;
8136                                 ni++;
8137                                 break;
8138                         }
8139                 }
8140         }
8141         free(jp);
8142         
8143         if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, bonds, sizeof(Int) * 2, where) != 0)
8144                 goto panic;
8145         mp->nbonds -= IntGroupGetCount(where);
8146         if (mp->nbonds == 0) {
8147                 free(mp->bonds);
8148                 mp->bonds = NULL;
8149         }
8150         if (mp->bondOrders != NULL) {
8151                 if (sRemoveElementsFromArrayAtPositions(mp->bondOrders, mp->nbondOrders, NULL, sizeof(Double), where) != 0)
8152                         goto panic;
8153                 mp->nbondOrders -= IntGroupGetCount(where);
8154                 if (mp->nbondOrders == 0) {
8155                         free(mp->bondOrders);
8156                         mp->bondOrders = NULL;
8157                 }
8158         }
8159         if (na == 0 && nd == 0 && ni == 0)
8160                 ip = NULL;
8161         else
8162                 ip = (Int *)malloc(sizeof(Int) * (na * 3 + nd * 4 + ni * 4));
8163         if (na > 0)
8164                 MoleculeDeleteAngles(mp, ip, ag);
8165         if (nd > 0)
8166                 MoleculeDeleteDihedrals(mp, ip + na * 3, dg);
8167         if (ni > 0)
8168                 MoleculeDeleteImpropers(mp, ip + na * 3 + nd * 4, ig);
8169         if (ip != NULL) {
8170                 IntGroupOffset(dg, ATOMS_MAX_NUMBER);
8171                 IntGroupOffset(ig, ATOMS_MAX_NUMBER * 2);
8172                 IntGroupAddIntGroup(ag, dg);
8173                 IntGroupAddIntGroup(ag, ig);
8174                 IntGroupRelease(dg);
8175                 IntGroupRelease(ig);
8176         }
8177
8178         if (IntGroupGetCount(ag) == 0) {
8179                 IntGroupRelease(ag);
8180                 ag = NULL;
8181         }
8182         
8183         *outRemoved = ip;
8184         *outRemovedPos = ag;
8185
8186         MoleculeIncrementModifyCount(mp);
8187         mp->needsMDRebuild = 1;
8188         __MoleculeUnlock(mp);
8189
8190         return na * 3 + nd * 4 + ni * 4;
8191
8192   panic:
8193         __MoleculeUnlock(mp);
8194         Panic("Low memory while removing bonds");
8195         return -1;  /*  Not reached  */
8196 }
8197
8198 int
8199 MoleculeAssignBondOrders(Molecule *mp, const Double *orders, IntGroup *where)
8200 {
8201         Int i, j;
8202         IntGroupIterator iter;
8203         if (mp == NULL || orders == NULL || mp->nbonds == 0)
8204                 return 0;
8205         if (mp->noModifyTopology)
8206                 return -4;  /*  Prohibited operation  */
8207         if (mp->bondOrders == NULL) {
8208                 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbonds - 1, NULL);
8209                 memset(mp->bondOrders, 0, sizeof(Double) * mp->nbondOrders);
8210         }
8211         IntGroupIteratorInit(where, &iter);
8212         j = 0;
8213         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8214                 if (i >= mp->nbondOrders)
8215                         break;
8216                 mp->bondOrders[i] = orders[j++];
8217         }
8218         IntGroupIteratorRelease(&iter);
8219         return 0;
8220 }
8221
8222 int
8223 MoleculeGetBondOrders(Molecule *mp, Double *outOrders, IntGroup *where)
8224 {
8225         Int i, j;
8226         IntGroupIterator iter;
8227         if (mp == NULL || mp->nbonds == 0)
8228                 return 0;
8229         if (mp->bondOrders == NULL) {
8230                 /*  Returns all zero  */
8231                 i = IntGroupGetCount(where);
8232                 for (j = 0; j < i; j++)
8233                         outOrders[j] = 0.0;
8234         } else {
8235                 IntGroupIteratorInit(where, &iter);
8236                 j = 0;
8237                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8238                         if (i < mp->nbondOrders)
8239                                 outOrders[j] = mp->bondOrders[i];
8240                         else outOrders[j] = 0.0;
8241                         j++;
8242                 }
8243         }
8244         return 0;
8245 }
8246
8247 int
8248 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
8249 {
8250         int n1, nc;
8251         if (mp == NULL || angles == NULL)
8252                 return 0;
8253         if (mp->noModifyTopology)
8254                 return -4;  /*  Prohibited operation  */
8255
8256         __MoleculeLock(mp);
8257         if (where != NULL)
8258                 nc = IntGroupGetCount(where);
8259         else {
8260                 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
8261                         ;
8262                 nc = n1;
8263         }
8264         if (nc > 0) {
8265                 n1 = mp->nangles;
8266                 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
8267                         || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
8268                         __MoleculeUnlock(mp);
8269                         Panic("Low memory while adding angles");
8270                 }
8271         }
8272         mp->needsMDRebuild = 1;
8273         __MoleculeUnlock(mp);
8274         return nc;
8275 }
8276
8277 int
8278 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
8279 {
8280         int nc;
8281         if (mp == NULL || where == NULL)
8282                 return 0;
8283         if (mp->noModifyTopology)
8284                 return -4;  /*  Prohibited operation  */
8285         __MoleculeLock(mp);
8286         if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
8287                 __MoleculeUnlock(mp);
8288                 Panic("Bad argument while deleting angles");
8289         }
8290         mp->nangles -= (nc = IntGroupGetCount(where));
8291         if (mp->nangles == 0) {
8292                 free(mp->angles);
8293                 mp->angles = NULL;
8294         }
8295         mp->needsMDRebuild = 1;
8296         __MoleculeUnlock(mp);
8297         return nc;
8298 }
8299
8300 int
8301 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
8302 {
8303         int n1, nc;
8304         if (mp == NULL || dihedrals == NULL)
8305                 return 0;
8306         if (mp->noModifyTopology)
8307                 return -4;  /*  Prohibited operation  */
8308         if (where != NULL)
8309                 nc = IntGroupGetCount(where);
8310         else {
8311                 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
8312                         ;
8313                 nc = n1;
8314         }
8315         if (nc <= 0)
8316                 return 0;
8317         n1 = mp->ndihedrals;
8318         __MoleculeLock(mp);
8319         if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8320         || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
8321                 __MoleculeUnlock(mp);
8322                 Panic("Low memory while adding dihedrals");
8323         }
8324         mp->needsMDRebuild = 1;
8325         __MoleculeUnlock(mp);
8326         return nc;
8327 }
8328
8329 int
8330 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
8331 {       
8332         int nc;
8333         if (mp == NULL || where == NULL)
8334                 return 0;
8335         if (mp->noModifyTopology)
8336                 return -4;  /*  Prohibited operation  */
8337         __MoleculeLock(mp);
8338         if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
8339                 __MoleculeUnlock(mp);
8340                 Panic("Internal error: bad argument while deleting dihedrals");
8341         }
8342         mp->ndihedrals -= (nc = IntGroupGetCount(where));
8343         if (mp->ndihedrals == 0) {
8344                 free(mp->dihedrals);
8345                 mp->dihedrals = NULL;
8346         }
8347         mp->needsMDRebuild = 1;
8348         __MoleculeUnlock(mp);
8349         return nc;
8350 }
8351
8352 int
8353 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
8354 {
8355         int n1, nc;
8356         if (mp == NULL || impropers == NULL)
8357                 return 0;
8358         if (mp->noModifyTopology)
8359                 return -4;  /*  Prohibited operation  */
8360         if (where != NULL)
8361                 nc = IntGroupGetCount(where);
8362         else {
8363                 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
8364                         ;
8365                 nc = n1;
8366         }
8367         if (nc <= 0)
8368                 return 0;
8369         n1 = mp->nimpropers;
8370         __MoleculeLock(mp);
8371         if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8372         || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
8373                 __MoleculeUnlock(mp);
8374                 Panic("Low memory while adding impropers");
8375         }
8376         mp->needsMDRebuild = 1;
8377         __MoleculeUnlock(mp);
8378         return nc;
8379 }
8380
8381 int
8382 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
8383 {
8384         int nc;
8385         if (mp == NULL || where == NULL)
8386                 return 0;
8387         if (mp->noModifyTopology)
8388                 return -4;  /*  Prohibited operation  */
8389         __MoleculeLock(mp);
8390         if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
8391                 __MoleculeUnlock(mp);
8392                 Panic("Internal error: bad argument while deleting impropers");
8393         }
8394         mp->nimpropers -= (nc = IntGroupGetCount(where));
8395         if (mp->impropers == NULL) {
8396                 free(mp->impropers);
8397                 mp->impropers = NULL;
8398         }
8399         __MoleculeUnlock(mp);
8400         return nc;
8401 }
8402
8403 int
8404 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
8405 {
8406         Int i, *ip;
8407         if (mp == NULL || mp->bonds == NULL)
8408                 return -1;
8409         for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
8410                 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
8411                         return i;
8412         }
8413         return -1;
8414 }
8415
8416 int
8417 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
8418 {
8419         Int i, *ip;
8420         if (mp == NULL || mp->angles == NULL)
8421                 return -1;
8422         for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8423                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
8424                         (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
8425                         return i;
8426         }
8427         return -1;
8428 }
8429
8430 int
8431 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8432 {
8433         Int i, *ip;
8434         if (mp == NULL || mp->dihedrals == NULL)
8435                 return -1;
8436         for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8437                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
8438                         (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
8439                         return i;
8440         }
8441         return -1;
8442 }
8443
8444 int
8445 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8446 {
8447         Int i, *ip;
8448         if (mp == NULL || mp->impropers == NULL)
8449                 return -1;
8450         for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8451                 if (n3 != ip[2])
8452                         continue;
8453                 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
8454                         (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
8455                         (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
8456                         return i;
8457         }
8458         return -1;
8459 }
8460
8461 /*  Remove the bond at bondIndex and create two dummy atoms instead.
8462     The dummy atoms are placed at the end of atoms[], and the residue
8463         numbers are the same as the root atoms (i.e. the atoms to which
8464         the dummy atoms are connected). The indices are returned in
8465         dummyIndices[0,1].  */
8466 int
8467 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
8468 {
8469         Int roots[3], newBonds[5];
8470         Vector dr;
8471         Atom *rootp[2];
8472         Atom na[2], *nap;
8473         int i, natoms;
8474         IntGroup *ig;
8475         if (mp == NULL || mp->noModifyTopology)
8476                 return 0;
8477         if (bondIndex < 0 || bondIndex >= mp->nbonds)
8478                 return -1;
8479         roots[0] = mp->bonds[bondIndex * 2];
8480         roots[1] = mp->bonds[bondIndex * 2 + 1];
8481         roots[2] = kInvalidIndex;
8482         rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
8483         rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
8484         VecSub(dr, rootp[0]->r, rootp[1]->r);
8485         for (i = 0; i < 2; i++) {
8486                 float w;
8487                 nap = &na[i];
8488                 memmove(nap, rootp[i], sizeof(na));
8489                 nap->aname[0] = '*';
8490                 strcpy(nap->element, "Du");
8491                 nap->type = 0;
8492                 nap->charge = nap->weight = 0.0;
8493                 nap->atomicNumber = 0;
8494                 nap->connect.count = 0;
8495                 w = (i == 0 ? 0.4 : -0.4);
8496                 VecScaleInc(nap->r, dr, w);
8497                 VecZero(nap->v);
8498                 VecZero(nap->f);
8499                 nap->intCharge = 0;
8500                 nap->exflags = 0;
8501         }
8502
8503         /*  Expand atoms array and append the dummy atoms at the end  */
8504         __MoleculeLock(mp);
8505         natoms = mp->natoms;
8506         if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
8507                 goto panic;
8508         memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
8509         dummyIndices[0] = natoms;
8510         dummyIndices[1] = natoms + 1;
8511
8512         /*  Remove the old bond and create new bonds  */
8513         ig = IntGroupNewWithPoints(bondIndex, 1, -1);
8514         if (ig == NULL)
8515                 goto panic;
8516         MoleculeDeleteBonds(mp, NULL, ig, NULL, NULL);
8517         IntGroupRelease(ig);
8518         newBonds[0] = roots[0];
8519         newBonds[1] = dummyIndices[0];
8520         newBonds[2] = roots[1];
8521         newBonds[3] = dummyIndices[1];
8522         newBonds[4] = kInvalidIndex;
8523         
8524         i = (MoleculeAddBonds(mp, 2, newBonds, NULL, 1) < 0 ? -1 : 0);
8525         mp->needsMDRebuild = 1;
8526         __MoleculeUnlock(mp);
8527         return i;
8528
8529 panic:
8530         __MoleculeUnlock(mp);
8531         Panic("Low memory during creating dummy atoms");
8532         return 1;
8533 }
8534
8535 /*  Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
8536     a bond between the two root atoms. The value bondIndex is used as a
8537         hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
8538         the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
8539         is ignored and the new bond is stored at the end of bonds[].  */
8540 int
8541 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
8542 {
8543         return 0;
8544 }
8545
8546 /*
8547 Int
8548 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
8549 {
8550         Int n1, *np1;
8551         if (mol == NULL || mol->noModifyTopology)
8552                 return -1;
8553         n1 = mol->nangles;
8554         np1 = mol->angles;
8555         mol->nangles = 0;
8556         mol->angles = NULL;
8557         if (nangles > 0) {
8558                 __MoleculeLock(mol);
8559                 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
8560                 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
8561                 mol->needsMDRebuild = 1;
8562                 __MoleculeUnlock(mol);
8563         }
8564         *outAngles = np1;
8565         return n1;
8566 }
8567                                                 
8568 Int
8569 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
8570 {
8571         Int n1, *np1;
8572         if (mol == NULL || mol->noModifyTopology)
8573                 return -1;
8574         n1 = mol->ndihedrals;
8575         np1 = mol->dihedrals;
8576         mol->ndihedrals = 0;
8577         mol->dihedrals = NULL;
8578         if (ndihedrals > 0) {
8579                 __MoleculeLock(mol);
8580                 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
8581                 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
8582                 mol->needsMDRebuild = 1;
8583                 __MoleculeUnlock(mol);
8584         }
8585         *outDihedrals = np1;
8586         return n1;
8587 }
8588
8589 Int
8590 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
8591 {
8592         Int n1, *np1;
8593         if (mol == NULL || mol->noModifyTopology)
8594                 return -1;
8595         n1 = mol->nimpropers;
8596         np1 = mol->impropers;
8597         mol->nimpropers = 0;
8598         mol->impropers = NULL;
8599         if (nimpropers > 0) {
8600                 __MoleculeLock(mol);
8601                 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
8602                 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
8603                 mol->needsMDRebuild = 1;
8604                 __MoleculeUnlock(mol);
8605         }
8606         *outImpropers = np1;
8607         return n1;
8608 }
8609 */
8610
8611 Int
8612 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
8613 {
8614         Int i, j, k, *ip;
8615         Atom *ap;
8616         Int nangles;
8617         Int *angles;
8618         
8619         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8620                 return 0;  /*  molecule is empty  */
8621         if (mol->noModifyTopology)
8622                 return -1;
8623         nangles = 0;
8624         angles = NULL;
8625         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
8626                 Int *cp = AtomConnectData(&ap->connect);
8627                 if (ap->anchor != NULL)
8628                         continue;
8629                 for (j = 0; j < ap->connect.count; j++) {
8630                         Int j0 = cp[j];
8631                         if (ATOM_AT_INDEX(mol->atoms, j0)->anchor != NULL)
8632                                 continue;
8633                         for (k = j + 1; k < ap->connect.count; k++) {
8634                                 Int k0 = cp[k];
8635                                 if (ATOM_AT_INDEX(mol->atoms, k0)->anchor != NULL)
8636                                         continue;
8637                                 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
8638                                         ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
8639                                         ip[0] = j0;
8640                                         ip[1] = i;
8641                                         ip[2] = k0;
8642                                 }
8643                         }
8644                 }
8645         }
8646         if (nangles > 0) {
8647                 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
8648                 ip[0] = -1;
8649                 nangles--;
8650         }
8651         if (outAngles != NULL)
8652                 *outAngles = angles;
8653         return nangles;
8654 }
8655
8656 Int
8657 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
8658 {
8659         Int n1, n2, n3, n4, *ip, *cp2, *cp3;
8660         Atom *ap2, *ap3;
8661         Int ndihedrals;
8662         Int *dihedrals;
8663         
8664         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8665                 return 0;  /*  molecule is empty  */
8666         ndihedrals = 0;
8667         dihedrals = NULL;
8668         for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
8669                 Int i1, i3, i4, *ip;
8670                 if (ap2->anchor != NULL)
8671                         continue;
8672                 cp2 = AtomConnectData(&ap2->connect);
8673                 for (i3 = 0; i3 < ap2->connect.count; i3++) {
8674                         n3 = cp2[i3];
8675                         if (n2 > n3)
8676                                 continue;
8677                         ap3 = ATOM_AT_INDEX(mol->atoms, n3);
8678                         if (ap3->anchor != NULL)
8679                                 continue;
8680                         cp3 = AtomConnectData(&ap3->connect);
8681                         for (i1 = 0; i1 < ap2->connect.count; i1++) {
8682                                 n1 = cp2[i1];
8683                                 if (n1 == n3)
8684                                         continue;
8685                                 if (ATOM_AT_INDEX(mol->atoms, n1)->anchor != NULL)
8686                                         continue;
8687                                 for (i4 = 0; i4 < ap3->connect.count; i4++) {
8688                                         n4 = cp3[i4];
8689                                         if (n2 == n4 || n1 == n4)
8690                                                 continue;
8691                                         if (ATOM_AT_INDEX(mol->atoms, n4)->anchor != NULL)
8692                                                 continue;
8693                                         if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
8694                                                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
8695                                                 ip[0] = n1;
8696                                                 ip[1] = n2;
8697                                                 ip[2] = n3;
8698                                                 ip[3] = n4;
8699                                         }
8700                                 }
8701                         }
8702                 }
8703         }
8704         if (ndihedrals > 0) {
8705                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
8706                 ip[0] = -1;
8707                 ndihedrals--;
8708         }
8709         if (outDihedrals != NULL)
8710                 *outDihedrals = dihedrals;
8711         return ndihedrals;
8712 }
8713
8714 Int
8715 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
8716 {
8717         Int n1, n2, n3, n4, t1, t2, t3, t4, *ip, *cp;
8718         Parameter *par = mol->par;
8719         Atom *ap, *ap3;
8720         Int nimpropers;
8721         Int *impropers;
8722         
8723         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8724                 return 0;  /*  molecule is empty  */
8725         if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
8726                 return 0;  /*  No improper parameters are defined  */
8727         nimpropers = 0;
8728         impropers = NULL;
8729         ap = mol->atoms;
8730         for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
8731                 Int i1, i2, i4, found, *ip;
8732                 t3 = ap3->type;
8733                 cp = AtomConnectData(&ap3->connect);
8734                 for (i1 = 0; i1 < ap3->connect.count; i1++) {
8735                         n1 = cp[i1];
8736                         t1 = ATOM_AT_INDEX(ap, n1)->type;
8737                         for (i2 = i1 + 1; i2 < ap3->connect.count; i2++) {
8738                                 n2 = cp[i2];
8739                                 t2 = ATOM_AT_INDEX(ap, n2)->type;
8740                                 for (i4 = i2 + 1; i4 < ap3->connect.count; i4++) {
8741                                         n4 = cp[i4];
8742                                         t4 = ATOM_AT_INDEX(ap, n4)->type;
8743                                         found = 0;
8744                                         if (ParameterLookupImproperPar(par, t1, t2, t3, t4, n1, n2, n3, n4, 0) != NULL)
8745                                                 found = 1;
8746                                         else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, -1, -1, -1, -1, 0) != NULL)
8747                                                 found = 1;
8748                                         if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
8749                                                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8750                                                 ip[0] = n1;
8751                                                 ip[1] = n2;
8752                                                 ip[2] = n3;
8753                                                 ip[3] = n4;
8754                                         }
8755                                 }
8756                         }
8757                 }
8758         }
8759         if (nimpropers > 0) {
8760                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8761                 ip[0] = -1;
8762                 nimpropers--;
8763         }
8764         if (outImpropers != NULL)
8765                 *outImpropers = impropers;
8766         return nimpropers;
8767 }
8768
8769 #pragma mark ====== Residues ======
8770
8771 void
8772 MoleculeCleanUpResidueTable(Molecule *mp)
8773 {
8774         int i, maxres;
8775         Atom *ap;
8776         if (mp == NULL || mp->natoms == 0)
8777                 return;
8778         maxres = 0;
8779         __MoleculeLock(mp);
8780         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8781                 if (ap->resSeq >= maxres)
8782                         maxres = ap->resSeq + 1;
8783                 if (ap->resSeq < mp->nresidues) {
8784                         if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
8785                                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8786                 } else {
8787                         AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
8788                 }
8789         }
8790         if (maxres < mp->nresidues)
8791                 mp->nresidues = maxres;
8792         __MoleculeUnlock(mp);
8793 }
8794
8795 /*  Change the number of residues. If nresidues is greater than the current value,
8796     then the array mp->residues is expanded with null names. If nresidues is smaller
8797         than the current value, mp->nresidues is set to the smallest possible value
8798         that is no smaller than nresidues and larger than any of the resSeq values.  */
8799 int
8800 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
8801 {
8802         int n;
8803         if (mp == NULL)
8804                 return 0;
8805         if (mp->nresidues == nresidues)
8806                 return nresidues;
8807         else if (mp->nresidues < nresidues) {
8808                 __MoleculeLock(mp);
8809                 n = mp->nresidues;
8810                 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
8811                 while (n < nresidues)
8812                         mp->residues[n++][0] = 0;
8813                 __MoleculeUnlock(mp);
8814                 return nresidues;
8815         } else {
8816                 int i;
8817                 Atom *ap;
8818                 n = nresidues;
8819                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8820                         if (ap->resSeq >= n)
8821                                 n = ap->resSeq + 1;
8822                 }
8823                 mp->nresidues = n;
8824                 return n;
8825         }
8826 }
8827
8828 int
8829 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
8830 {
8831         IntGroupIterator iter;
8832         int withArray, resSeq, maxSeq;
8833         int i, j;
8834         Atom *ap;
8835         
8836         /*  If LSB of resSeqs is 1, then a constant value is used for all specified atoms  */
8837         if (((int)resSeqs & 1) == 0) {
8838                 withArray = 1;
8839                 resSeq = 0;
8840         } else {
8841                 withArray = 0;
8842                 resSeq = ((int)resSeqs - 1) / 2;
8843         }
8844         
8845         IntGroupIteratorInit(group, &iter);
8846
8847         /*  Change resSeqs  */
8848         maxSeq = 0;
8849         j = 0;
8850         __MoleculeLock(mp);
8851         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8852                 ap = ATOM_AT_INDEX(mp->atoms, i);
8853                 if (withArray)
8854                         resSeq = resSeqs[j++];
8855                 if (resSeq > maxSeq)
8856                         maxSeq = resSeq;
8857                 ap->resSeq = resSeq;
8858         }
8859         __MoleculeUnlock(mp);
8860
8861         /*  Expand array if necessary  */
8862         if (maxSeq >= mp->nresidues)
8863                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8864
8865         /*  Synchronize resName and residues[]  */
8866         __MoleculeLock(mp);
8867         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8868                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8869                         continue;
8870                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8871         }
8872         IntGroupIteratorRelease(&iter);
8873         __MoleculeUnlock(mp);
8874         
8875         MoleculeIncrementModifyCount(mp);
8876         
8877         return 0;
8878 }
8879
8880 int
8881 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
8882 {
8883         return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(resSeq * 2 + 1));
8884 }
8885
8886 /*  Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
8887     specifies the mp->nresidues after modifying the residue numbers.
8888         If all atoms are modified, then the table of residue names is also shifted. Otherwise,
8889         the table of residue names is not touched. */
8890 int
8891 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
8892 {
8893         int i, maxSeq, nmodatoms;
8894         Atom *ap;
8895         IntGroupIterator iter;
8896         IntGroupIteratorInit(group, &iter);
8897         maxSeq = 0;
8898         if (nresidues < 0)
8899                 nresidues = mp->nresidues;
8900         nmodatoms = 0;
8901         __MoleculeLock(mp);
8902         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8903                 ap = ATOM_AT_INDEX(mp->atoms, i);
8904                 ap->resSeq += offset;
8905                 if (ap->resSeq < 0) {
8906                         /*  Bad argument; undo change and returns this index + 1  */
8907                         int bad_index = i;
8908                         ap->resSeq -= offset;
8909                         while ((i = IntGroupIteratorLast(&iter)) >= 0) {
8910                                 ap = ATOM_AT_INDEX(mp->atoms, i);
8911                                 ap->resSeq -= offset;
8912                         }
8913                         IntGroupIteratorRelease(&iter);
8914                         return bad_index + 1;
8915                 }
8916                 if (ap->resSeq > maxSeq)
8917                         maxSeq = ap->resSeq;
8918                 nmodatoms++;
8919         }
8920         if (maxSeq >= nresidues)
8921                 nresidues = maxSeq + 1;
8922         if (offset < 0 && nmodatoms == mp->natoms) {
8923                 /*  Shift the residue names downward  */
8924                 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
8925         }
8926         __MoleculeUnlock(mp);
8927         MoleculeChangeNumberOfResidues(mp, nresidues);
8928         if (offset > 0 && nmodatoms == mp->natoms) {
8929                 /*  Shift the residue names upward  */
8930                 __MoleculeLock(mp);
8931                 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
8932                 __MoleculeUnlock(mp);
8933         }
8934         IntGroupIteratorRelease(&iter);
8935
8936         MoleculeIncrementModifyCount(mp);
8937         
8938         return 0;
8939 }
8940
8941 /*  Change residue names for the specified residue numbers. Names is an array of
8942     chars containing argc*4 characters, and every 4 characters represent a
8943         residue name; characters '\x01'-'\x1f' are converted to '\0', which allow 
8944         names to be handled as a C string.  */
8945 int
8946 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
8947 {
8948         int i, maxSeq;
8949         Atom *ap;
8950         maxSeq = 0;
8951         for (i = 0; i < argc; i++) {
8952                 if (maxSeq < resSeqs[i])
8953                         maxSeq = resSeqs[i];
8954         }
8955         if (maxSeq >= mp->nresidues)
8956                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8957         __MoleculeLock(mp);
8958         for (i = 0; i < argc; i++) {
8959                 char *p = mp->residues[resSeqs[i]];
8960                 int j;
8961                 strncpy(p, names + i * 4, 4);
8962                 for (j = 0; j < 4; j++) {
8963                         if (p[j] >= 0 && p[j] < 0x20)
8964                                 p[j] = 0;
8965                 }
8966         }
8967         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8968                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8969         }
8970         __MoleculeUnlock(mp);
8971
8972         MoleculeIncrementModifyCount(mp);
8973         
8974         return 0;
8975 }
8976
8977 /*  Returns the maximum residue number actually used  */
8978 int
8979 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
8980 {
8981         int i, maxSeq;
8982         Atom *ap;
8983         maxSeq = -1;
8984         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8985                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8986                         continue;
8987                 if (ap->resSeq > maxSeq)
8988                         maxSeq = ap->resSeq;
8989         }
8990         return maxSeq;
8991 }
8992
8993 /*  Returns the minimum residue number actually used  */
8994 int
8995 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
8996 {
8997         int i, minSeq;
8998         Atom *ap;
8999         minSeq = ATOMS_MAX_NUMBER;
9000         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9001                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9002                         continue;
9003                 if (ap->resSeq < minSeq)
9004                         minSeq = ap->resSeq;
9005         }
9006         return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
9007 }
9008
9009 #pragma mark ====== Sort by Residues ======
9010
9011 static int
9012 sAtomSortComparator(const void *a, const void *b)
9013 {
9014         const Atom *ap, *bp;
9015         ap = *((const Atom **)a);
9016         bp = *((const Atom **)b);
9017         if (ap->resSeq == bp->resSeq) {
9018                 /*  Retain the original order (i.e. atom with larger pointer address is larger)  */
9019                 if (ap < bp)
9020                         return -1;
9021                 else if (ap > bp)
9022                         return 1;
9023                 else return 0;
9024         } else {
9025                 /*  Compare the residue sequence. However, residue sequence 0 is always larger.  */
9026                 if (ap->resSeq == 0)
9027                         return 1;
9028                 else if (bp->resSeq == 0)
9029                         return -1;
9030                 else if (ap->resSeq < bp->resSeq)
9031                         return -1;
9032                 else if (ap->resSeq > bp->resSeq)
9033                         return 1;
9034                 else return 0;
9035         }
9036 }
9037
9038 static void
9039 sMoleculeReorder(Molecule *mp)
9040 {
9041         int i, res, prevRes;
9042         Atom **apArray;
9043         Int *old2new;
9044         Atom *newAtoms;
9045         if (mp == NULL || mp->natoms <= 1)
9046                 return;
9047
9048         /*  Sort the atoms, bonds, etc. */
9049         apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
9050         old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9051         newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9052         if (apArray == NULL || old2new == NULL || newAtoms == NULL)
9053                 Panic("Low memory during reordering atoms");
9054         for (i = 0; i < mp->natoms; i++)
9055                 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
9056
9057         /*  Sort the atoms. Note: apArray is an array of "Pointer to Atom"  */
9058         qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
9059         
9060         /*  Make a table of 'which atom becomes which'  */
9061         for (i = 0; i < mp->natoms; i++) {
9062                 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
9063                 old2new[j] = i;
9064         }
9065         
9066         /*  Renumber the bonds, etc.  */
9067         for (i = 0; i < mp->nbonds * 2; i++) {
9068                 mp->bonds[i] = old2new[mp->bonds[i]];
9069         }
9070         for (i = 0; i < mp->nangles * 3; i++) {
9071                 mp->angles[i] = old2new[mp->angles[i]];
9072         }
9073         for (i = 0; i < mp->ndihedrals * 4; i++) {
9074                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9075         }
9076         for (i = 0; i < mp->nimpropers * 4; i++) {
9077                 mp->impropers[i] = old2new[mp->impropers[i]];
9078         }
9079         for (i = 0; i < mp->natoms; i++) {
9080                 Int *ip, j;
9081                 ip = AtomConnectData(&(apArray[i]->connect));
9082                 for (j = 0; j < apArray[i]->connect.count; j++, ip++)
9083                         *ip = old2new[*ip];
9084         }
9085         
9086         /*  Renumber the residues so that the residue numbers are contiguous  */
9087         res = prevRes = 0;
9088         for (i = 0; i < mp->natoms; i++) {
9089                 if (apArray[i]->resSeq == 0)
9090                         break;
9091                 if (apArray[i]->resSeq != prevRes) {
9092                         res++;
9093                         prevRes = apArray[i]->resSeq;
9094                         if (prevRes != res) {
9095                                 strncpy(mp->residues[res], mp->residues[prevRes], 4);
9096                         }
9097                 }
9098                 apArray[i]->resSeq = res;
9099         }
9100         mp->nresidues = res + 1;
9101
9102         /*  Sort the atoms and copy back to atoms[] */
9103         for (i = 0; i < mp->natoms; i++) {
9104                 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
9105         }
9106         memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
9107         
9108         /*  Free the locally allocated storage  */
9109         free(newAtoms);
9110         free(old2new);
9111         free(apArray);
9112 }
9113
9114 /*  Renumber atoms  */
9115 int
9116 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
9117 {
9118         Int *old2new, i, j, retval;
9119         Atom *saveAtoms;
9120         if (mp == NULL)
9121                 return 0;
9122         if (mp->noModifyTopology)
9123                 return -1;
9124         if (old2new_out != NULL)
9125                 old2new = old2new_out;
9126         else
9127                 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9128         saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9129         if (old2new == NULL || saveAtoms == NULL)
9130                 Panic("Low memory during reordering atoms");
9131         memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
9132         __MoleculeLock(mp);
9133         for (i = 0; i < mp->natoms; i++)
9134                 old2new[i] = -1;
9135         for (i = 0; i < isize && i < mp->natoms; i++) {
9136                 j = new2old[i];
9137                 if (j < 0 || j >= mp->natoms) {
9138                         retval = 1; /* Out of range */
9139                         goto end;
9140                 }
9141                 if (old2new[j] != -1) {
9142                         retval = 2;  /*  Duplicate entry  */
9143                         goto end;
9144                 }
9145                 old2new[j] = i;
9146         }
9147         if (i < mp->natoms) {
9148                 for (j = 0; j < mp->natoms; j++) {
9149                         if (old2new[j] != -1)
9150                                 continue;
9151                         old2new[j] = i++;
9152                 }
9153         }
9154         if (i != mp->natoms) {
9155                 retval = 3;  /*  Internal inconsistency  */
9156                 goto end;
9157         }
9158
9159         /*  Renumber the bonds, etc.  */
9160         for (i = 0; i < mp->nbonds * 2; i++) {
9161                 mp->bonds[i] = old2new[mp->bonds[i]];
9162         }
9163         for (i = 0; i < mp->nangles * 3; i++) {
9164                 mp->angles[i] = old2new[mp->angles[i]];
9165         }
9166         for (i = 0; i < mp->ndihedrals * 4; i++) {
9167                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9168         }
9169         for (i = 0; i < mp->nimpropers * 4; i++) {
9170                 mp->impropers[i] = old2new[mp->impropers[i]];
9171         }
9172         /*  Renumber the connection table and pi anchor table  */
9173         for (i = 0; i < mp->natoms; i++) {
9174                 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
9175                 Int *ip = AtomConnectData(&ap->connect);
9176                 for (j = 0; j < ap->connect.count; j++, ip++)
9177                         *ip = old2new[*ip];
9178                 if (ap->anchor != NULL) {
9179                         ip = AtomConnectData(&ap->anchor->connect);
9180                         for (j = 0; j < ap->anchor->connect.count; j++, ip++)
9181                                 *ip = old2new[*ip];
9182                 }
9183         }
9184         
9185         if (mp->par != NULL) {
9186                 /*  Renumber the parameters  */
9187                 int n;
9188                 for (j = kFirstParType; j <= kLastParType; j++) {
9189                         n = ParameterGetCountForType(mp->par, j);
9190                         for (i = 0; i < n; i++) {
9191                                 UnionPar *up = ParameterGetUnionParFromTypeAndIndex(mp->par, j, i);
9192                                 if (up != NULL)
9193                                         ParameterRenumberAtoms(j, up, mp->natoms, old2new);
9194                         }
9195                 }
9196         }
9197         
9198         /*  Renumber the atoms  */
9199         for (i = 0; i < mp->natoms; i++)
9200                 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
9201         retval = 0;
9202         
9203         MoleculeIncrementModifyCount(mp);
9204         mp->needsMDRebuild = 1;
9205
9206   end:
9207         __MoleculeUnlock(mp);
9208         free(saveAtoms);
9209         if (old2new_out == NULL)
9210                 free(old2new);
9211         return retval;
9212 }
9213
9214 #pragma mark ====== Coordinate Transform ======
9215
9216 void
9217 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
9218 {
9219         int i;
9220         Atom *ap;
9221         Symop new_symop;
9222         Transform rtr, symtr;
9223         if (mp == NULL || tr == NULL)
9224                 return;
9225         TransformInvert(rtr, tr);
9226         __MoleculeLock(mp);
9227         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9228                 if (group == NULL || IntGroupLookup(group, i, NULL) != 0) {
9229                         TransformVec(&ap->r, tr, &ap->r);
9230                         if (!SYMOP_ALIVE(ap->symop))
9231                                 continue;
9232                         /*  Transform symop  */
9233                         if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9234                                 continue;
9235                         TransformMul(symtr, tr, symtr);
9236                         if (group == NULL || IntGroupLookup(group, ap->symbase, NULL) != 0)
9237                                 TransformMul(symtr, symtr, rtr);
9238                 } else {
9239                         if (!SYMOP_ALIVE(ap->symop))
9240                                 continue;
9241                         /*  Transform symop if the base atom is transformed  */
9242                         if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
9243                                 continue;
9244                         if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9245                                 continue;
9246                         TransformMul(symtr, symtr, rtr);
9247                 }
9248                 if (MoleculeGetSymopForTransform(mp, symtr, &new_symop, 1) != 0)
9249                         continue;
9250                 ap->symop = new_symop;
9251         }
9252         mp->needsMDCopyCoordinates = 1;
9253         __MoleculeUnlock(mp);
9254         sMoleculeNotifyChangeAppearance(mp);
9255 }
9256
9257 /*
9258 void
9259 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
9260 {
9261         int i;
9262         Atom *ap;
9263         if (mp == NULL || tr == NULL)
9264                 return;
9265         __MoleculeLock(mp);
9266         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9267                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9268                         continue;
9269                 TransformVec(&ap->r, tr, &ap->r);
9270         }
9271         mp->needsMDCopyCoordinates = 1;
9272         __MoleculeUnlock(mp);
9273         sMoleculeNotifyChangeAppearance(mp);
9274 }
9275 */
9276
9277 void
9278 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
9279 {
9280         Transform tr;
9281         if (mp == NULL || vp == NULL)
9282                 return;
9283         memset(tr, 0, sizeof(tr));
9284         tr[0] = tr[4] = tr[8] = 1.0;
9285         tr[9] = vp->x;
9286         tr[10] = vp->y;
9287         tr[11] = vp->z;
9288         MoleculeTransform(mp, tr, group);
9289 }
9290
9291 void
9292 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
9293 {
9294         Transform tr;
9295         TransformForRotation(tr, axis, angle, center);
9296         MoleculeTransform(mp, tr, group);
9297 }
9298
9299 int
9300 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
9301 {
9302         int i;
9303         Atom *ap;
9304         Double w;
9305         if (mp == NULL || center == NULL)
9306                 return 1;
9307         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9308                 return 2;   /*  Empty molecule  */
9309         w = 0.0;
9310         center->x = center->y = center->z = 0.0;
9311         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9312                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9313                         continue;
9314                 VecScaleInc(*center, ap->r, ap->weight);
9315                 w += ap->weight;
9316         }
9317         if (w < 1e-7)
9318                 return 3;  /*  Atomic weights are not defined?  */
9319         w = 1.0 / w;
9320         VecScaleSelf(*center, w);
9321         return 0;
9322 }
9323
9324 int
9325 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
9326 {
9327         Vector vmin, vmax;
9328         int i;
9329         Atom *ap;
9330         if (mp == NULL)
9331                 return 1;
9332         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9333                 return 2;   /*  Empty molecule  */
9334         vmin.x = vmin.y = vmin.z = 1e50;
9335         vmax.x = vmax.y = vmax.z = -1e50;
9336         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9337                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9338                         continue;
9339                 if (vmin.x > ap->r.x)
9340                         vmin.x = ap->r.x;
9341                 if (vmin.y > ap->r.y)
9342                         vmin.y = ap->r.y;
9343                 if (vmin.z > ap->r.z)
9344                         vmin.z = ap->r.z;
9345                 if (vmax.x < ap->r.x)
9346                         vmax.x = ap->r.x;
9347                 if (vmax.y < ap->r.y)
9348                         vmax.y = ap->r.y;
9349                 if (vmax.z < ap->r.z)
9350                         vmax.z = ap->r.z;
9351         }
9352         if (min != NULL)
9353                 *min = vmin;
9354         if (max != NULL)
9355                 *max = vmax;
9356         return 0;       
9357 }
9358
9359 #pragma mark ====== Measurements ======
9360
9361 Double
9362 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
9363 {
9364         Vector r1, r2;
9365 /*      if (mp->is_xtal_coord) {
9366                 TransformVec(&r1, mp->cell->tr, vp1);
9367                 TransformVec(&r2, mp->cell->tr, vp2);
9368         } else */ {
9369                 r1 = *vp1;
9370                 r2 = *vp2;
9371         }
9372         VecDec(r1, r2);
9373         return VecLength(r1);
9374 }
9375
9376 Double
9377 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
9378 {
9379         Vector r1, r2, r3;
9380         double w;
9381 /*      if (mp->is_xtal_coord) {
9382                 TransformVec(&r1, mp->cell->tr, vp1);
9383                 TransformVec(&r2, mp->cell->tr, vp2);
9384                 TransformVec(&r3, mp->cell->tr, vp3);
9385         } else */ {
9386                 r1 = *vp1;
9387                 r2 = *vp2;
9388                 r3 = *vp3;
9389         }
9390         VecDec(r1, r2);
9391         VecDec(r3, r2);
9392         w = VecLength(r1) * VecLength(r3);
9393         if (w < 1e-20)
9394                 return NAN;
9395         return acos(VecDot(r1, r3) / w) * kRad2Deg;
9396 }
9397
9398 Double
9399 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
9400 {
9401         Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
9402         double w1, w2, w3;
9403 /*      if (mp->is_xtal_coord) {
9404                 TransformVec(&r1, mp->cell->tr, vp1);
9405                 TransformVec(&r2, mp->cell->tr, vp2);
9406                 TransformVec(&r3, mp->cell->tr, vp3);
9407                 TransformVec(&r4, mp->cell->tr, vp4);
9408         } else */ {
9409                 r1 = *vp1;
9410                 r2 = *vp2;
9411                 r3 = *vp3;
9412                 r4 = *vp4;
9413         }
9414         VecSub(r21, r1, r2);
9415         VecSub(r32, r2, r3);
9416         VecSub(r43, r3, r4);
9417         VecCross(v1, r21, r32);
9418         VecCross(v2, r32, r43);
9419         VecCross(v3, r32, v1);
9420         w1 = VecLength(v1);
9421         w2 = VecLength(v2);
9422         w3 = VecLength(v3);
9423         if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
9424                 return NAN;
9425         } else {
9426                 w1 = 1.0 / w1;
9427                 w2 = 1.0 / w2;
9428                 w3 = 1.0 / w3;
9429                 VecScaleSelf(v1, w1);
9430                 VecScaleSelf(v2, w2);
9431                 VecScaleSelf(v3, w3);
9432                 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
9433         }
9434 }
9435
9436 #pragma mark ====== XtalCell Parameters ======
9437
9438 void
9439 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
9440 {
9441         if (mp->cell != NULL) {
9442                 TransformVec(dst, mp->cell->tr, src);
9443         } else *dst = *src;
9444 }
9445
9446 void
9447 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
9448 {
9449         if (mp->cell != NULL) {
9450                 TransformVec(dst, mp->cell->rtr, src);
9451         } else *dst = *src;
9452 }
9453
9454 int
9455 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
9456 {
9457         static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
9458         int n1, n2, n3;
9459         Vector *vp1, *vp2, *vp3;
9460         Vector v1, v2;
9461
9462         if (cp == NULL)
9463                 return 0;
9464         for (n1 = 0; n1 < 3; n1++) {
9465                 if (cp->flags[n1] != 0)
9466                         break;
9467         }
9468         if (n1 == 3) {
9469                 /*  All directions are non-periodic  */
9470                 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
9471                 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
9472         } else {
9473                 n2 = (n1 + 1) % 3;
9474                 n3 = (n1 + 2) % 3;
9475                 vp1 = &(cp->axes[n1]);
9476                 vp2 = &(cp->axes[n2]);
9477                 vp3 = &(cp->axes[n3]);
9478                 cp->tr[n1*3] = vp1->x;
9479                 cp->tr[n1*3+1] = vp1->y;
9480                 cp->tr[n1*3+2] = vp1->z;
9481                 cp->tr[9] = cp->origin.x;
9482                 cp->tr[10] = cp->origin.y;
9483                 cp->tr[11] = cp->origin.z;
9484                 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
9485                         /*  1-dimensional or 2-dimensional system  */
9486                         /*  Create "dummy" axes, so that transforms between internal and cartesian coordinates are
9487                          possible with a single matrix  */
9488                         if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
9489                                 /*  1-dimensional  */
9490                                 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
9491                                 VecCross(v1, *vp1, xvec);
9492                                 VecCross(v2, *vp1, yvec);
9493                                 if (VecLength2(v1) < VecLength2(v2))
9494                                         v1 = v2;
9495                                 VecCross(v2, *vp1, v1);
9496                                 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
9497                                         return -1;   /*  Non-regular transform  */
9498                         } else if (cp->flags[n2] == 0) {
9499                                 v2 = *vp3;
9500                                 VecCross(v1, v2, *vp1);
9501                                 if (NormalizeVec(&v1, &v1))
9502                                         return -1;  /*  Non-regular transform  */
9503                         } else {
9504                                 v1 = *vp2;
9505                                 VecCross(v2, *vp1, v1);
9506                                 if (NormalizeVec(&v2, &v2))
9507                                         return -1;  /*  Non-regular transform  */
9508                         }
9509                         cp->tr[n2*3] = v1.x;
9510                         cp->tr[n2*3+1] = v1.y;
9511                         cp->tr[n2*3+2] = v1.z;
9512                         cp->tr[n3*3] = v2.x;
9513                         cp->tr[n3*3+1] = v2.y;
9514                         cp->tr[n3*3+2] = v2.z;
9515                 } else {
9516                         VecCross(v1, *vp1, *vp2);
9517                         if (fabs(VecDot(v1, *vp3)) < 1e-7)
9518                                 return -1;  /*  Non-regular transform  */
9519                         cp->tr[n2*3] = vp2->x;
9520                         cp->tr[n2*3+1] = vp2->y;
9521                         cp->tr[n2*3+2] = vp2->z;
9522                         cp->tr[n3*3] = vp3->x;
9523                         cp->tr[n3*3+1] = vp3->y;
9524                         cp->tr[n3*3+2] = vp3->z;
9525                 }
9526         }
9527         if (TransformInvert(cp->rtr, cp->tr))
9528                 return -1;  /*  Non-regular transform  */
9529
9530         /*  Calculate the reciprocal cell parameters  */
9531         cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[3] * cp->rtr[3] + cp->rtr[6] * cp->rtr[6]);
9532         cp->rcell[1] = sqrt(cp->rtr[1] * cp->rtr[1] + cp->rtr[4] * cp->rtr[4] + cp->rtr[7] * cp->rtr[7]);
9533         cp->rcell[2] = sqrt(cp->rtr[2] * cp->rtr[2] + cp->rtr[5] * cp->rtr[5] + cp->rtr[8] * cp->rtr[8]);
9534         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;
9535         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;
9536         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;
9537         
9538         if (calc_abc) {
9539                 /*  Calculate a, b, c, alpha, beta, gamma  */
9540                 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[1] * cp->tr[1] + cp->tr[2] * cp->tr[2]);
9541                 cp->cell[1] = sqrt(cp->tr[3] * cp->tr[3] + cp->tr[4] * cp->tr[4] + cp->tr[5] * cp->tr[5]);
9542                 cp->cell[2] = sqrt(cp->tr[6] * cp->tr[6] + cp->tr[7] * cp->tr[7] + cp->tr[8] * cp->tr[8]);
9543                 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;
9544                 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;
9545                 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;
9546         }
9547         return 0;
9548 }
9549
9550 void
9551 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
9552 {
9553         XtalCell *cp;
9554         int i;
9555         Atom *ap;
9556         Transform cmat;
9557         if (mp == NULL)
9558                 return;
9559         __MoleculeLock(mp);
9560         memset(&cmat, 0, sizeof(Transform));
9561         if (mp->cell != NULL)
9562                 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
9563         else
9564                 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
9565         if (a == 0.0) {
9566                 if (mp->cell != NULL) {
9567                         free(mp->cell);
9568                         mp->needsMDRebuild = 1;
9569                 }
9570                 mp->cell = NULL;
9571         } else {
9572                 cp = mp->cell;
9573                 if (cp == NULL) {
9574                         cp = (XtalCell *)calloc(sizeof(XtalCell), 1);
9575                         if (cp == NULL)
9576                                 Panic("Low memory during setting cell parameters");
9577                         mp->cell = cp;
9578                         mp->needsMDRebuild = 1;
9579                 }
9580                 /*  alpha, beta, gamma are in degree  */
9581                 cp->cell[0] = a;
9582                 cp->cell[1] = b;
9583                 cp->cell[2] = c;
9584                 cp->cell[3] = alpha;
9585                 cp->cell[4] = beta;
9586                 cp->cell[5] = gamma;
9587                 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
9588                         /*  c unique (hexagonal etc.)  */
9589                         Double cosa, cosb, sinb, cosg;
9590                         cosa = cos(alpha * kDeg2Rad);
9591                         cosb = cos(beta * kDeg2Rad);
9592                         sinb = sin(beta * kDeg2Rad);
9593                         cosg = cos(gamma * kDeg2Rad);
9594                         cp->axes[0].x = a * sinb;
9595                         cp->axes[0].y = 0;
9596                         cp->axes[0].z = a * cosb;
9597                         cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
9598                         cp->axes[1].z = b * cosa;
9599                         cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
9600                         cp->axes[2].x = 0;
9601                         cp->axes[2].y = 0;
9602                         cp->axes[2].z = c;
9603                 } else {
9604                         /*  b unique  */
9605                         Double cosg, sing, cosa, cosb;
9606                         cosa = cos(alpha * kDeg2Rad);
9607                         cosb = cos(beta * kDeg2Rad);
9608                         cosg = cos(gamma * kDeg2Rad);
9609                         sing = sin(gamma * kDeg2Rad);
9610                         cp->axes[0].x = a * sing;
9611                         cp->axes[0].y = a * cosg;
9612                         cp->axes[0].z = 0;
9613                         cp->axes[1].x = 0;
9614                         cp->axes[1].y = b;
9615                         cp->axes[1].z = 0;
9616                         cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
9617                         cp->axes[2].y = c * cosa;
9618                         cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
9619                 }
9620                 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
9621                 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
9622                 MoleculeCalculateCellFromAxes(cp, 0);
9623                 TransformMul(cmat, cp->tr, cmat);
9624         }
9625         
9626         /*  Update the coordinates (if requested)  */
9627         if (convertCoordinates) {
9628                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9629                         TransformVec(&(ap->r), cmat, &(ap->r));
9630                 }
9631         }
9632         
9633         /*  Update the anisotropic parameters  */
9634         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9635                 Aniso *anp = ap->aniso;
9636                 if (anp != NULL) {
9637                         MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
9638                 }
9639         }
9640         __MoleculeUnlock(mp);
9641         sMoleculeNotifyChangeAppearance(mp);
9642 }
9643
9644 void
9645 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x13, Double x23, const Double *sigmaptr)
9646 {
9647         Double d, dx;
9648         int u = 0;
9649         const Double log2 = 0.693147180559945;
9650         const Double pi22 = 19.7392088021787;  /* 2*pi**2 */
9651         Transform m1, m2;
9652         Aniso *anp;
9653         XtalCell *cp;
9654         Vector axis[3];
9655         Double val[3];
9656         if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
9657                 return;
9658         anp = mp->atoms[n1].aniso;
9659         __MoleculeLock(mp);
9660         if (anp == NULL) {
9661                 anp = (Aniso *)calloc(sizeof(Aniso), 1);
9662                 if (anp == NULL) {
9663                         __MoleculeUnlock(mp);
9664                         Panic("Low memory during setting anisotropic atom parameters");
9665                 }
9666                 mp->atoms[n1].aniso = anp;
9667         }
9668         switch (type) {
9669                 case 1: d = 1; dx = 0.5; break;
9670                 case 2: d = log2; dx = log2; break;
9671                 case 3: d = log2; dx = log2 * 0.5; break;
9672                 case 4: u = 1; d = 0.25; dx = 0.25; break;
9673                 case 5: u = 1; d = 0.25; dx = 0.125; break;
9674                 case 8: u = 1; d = pi22; dx = pi22; break;
9675                 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
9676                 case 10: d = pi22; dx = pi22; break;
9677                 default: d = dx = 1; break;
9678         }
9679         anp->bij[0] = x11 * d;
9680         anp->bij[1] = x22 * d;
9681         anp->bij[2] = x33 * d;
9682         anp->bij[3] = x12 * dx;
9683         anp->bij[4] = x13 * dx;
9684         anp->bij[5] = x23 * dx;
9685         if (sigmaptr != NULL) {
9686                 anp->has_bsig = 1;
9687                 anp->bsig[0] = sigmaptr[0] * d;
9688                 anp->bsig[1] = sigmaptr[1] * d;
9689                 anp->bsig[2] = sigmaptr[2] * d;
9690                 anp->bsig[3] = sigmaptr[3] * dx;
9691                 anp->bsig[4] = sigmaptr[4] * dx;
9692                 anp->bsig[5] = sigmaptr[5] * dx;
9693         } else {
9694                 anp->has_bsig = 0;
9695                 anp->bsig[0] = anp->bsig[1] = anp->bsig[2] = anp->bsig[3] = anp->bsig[4] = anp->bsig[5] = 0.0;
9696         }
9697         cp = mp->cell;
9698         if (cp != NULL && u == 1) {
9699                 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
9700                 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
9701                 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
9702                 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
9703                 anp->bij[4] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[3] * kDeg2Rad); */
9704                 anp->bij[5] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[4] * kDeg2Rad); */
9705                 if (sigmaptr != NULL) {
9706                         anp->bsig[0] *= cp->rcell[0] * cp->rcell[0];
9707                         anp->bsig[1] *= cp->rcell[1] * cp->rcell[1];
9708                         anp->bsig[2] *= cp->rcell[2] * cp->rcell[2];
9709                         anp->bsig[3] *= cp->rcell[0] * cp->rcell[1];
9710                         anp->bsig[4] *= cp->rcell[2] * cp->rcell[0];
9711                         anp->bsig[5] *= cp->rcell[1] * cp->rcell[2];
9712                 }
9713         }
9714         
9715         /*  Calculate the principal axes (in Cartesian coordinates)  */
9716         /*  The principal axes are the eigenvectors of matrix At(B^-1)A, where
9717                 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
9718                 in which x and z are the crystal-space and cartesian coordinates. */
9719         m1[0] = anp->bij[0] / pi22;
9720         m1[4] = anp->bij[1] / pi22;
9721         m1[8] = anp->bij[2] / pi22;
9722         m1[1] = m1[3] = anp->bij[3] / pi22;
9723         m1[2] = m1[6] = anp->bij[4] / pi22;
9724         m1[5] = m1[7] = anp->bij[5] / pi22;
9725         MatrixInvert(m1, m1);
9726         if (cp != NULL) {
9727                 memmove(m2, cp->rtr, sizeof(Mat33));
9728                 MatrixMul(m1, m1, m2);
9729                 MatrixTranspose(m2, m2);
9730                 MatrixMul(m1, m2, m1);
9731         }
9732         MatrixSymDiagonalize(m1, val, axis);
9733         for (u = 0; u < 3; u++) {
9734                 if (val[u] < 0) {
9735                         fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
9736                         val[u] = 0.001;
9737                 } else {
9738                         val[u] = 1 / sqrt(val[u]);
9739                 }
9740                 anp->pmat[u*3] = axis[u].x * val[u];
9741                 anp->pmat[u*3+1] = axis[u].y * val[u];
9742                 anp->pmat[u*3+2] = axis[u].z * val[u];
9743         }
9744         __MoleculeUnlock(mp);
9745 }
9746
9747 /*  Set the anisotropic parameter for atom idx according to the symop. If symop is not alive, nothing is done. */
9748 void
9749 MoleculeSetAnisoBySymop(Molecule *mp, int idx)
9750 {
9751         Atom *ap, *ap2;
9752         Transform t1, t2;
9753         if (mp == NULL || idx < 0 || idx >= mp->natoms)
9754                 return;
9755         ap = ATOM_AT_INDEX(mp->atoms, idx);
9756         if (!SYMOP_ALIVE(ap->symop))
9757                 return;
9758         ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
9759         if (ap2->aniso == NULL) {
9760                 if (ap->aniso != NULL) {
9761                         free(ap->aniso);
9762                         ap->aniso = NULL;
9763                 }
9764                 return;
9765         }
9766         if (ap->aniso == NULL)
9767                 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
9768         if (ap->symop.sym == 0 || ap->symop.sym >= mp->nsyms) {
9769                 /*  Just copy the aniso parameters  */
9770                 memmove(ap->aniso, ap2->aniso, sizeof(Aniso));
9771                 return;
9772         }
9773         memmove(t1, SYMMETRY_AT_INDEX(mp->syms, ap->symop.sym), sizeof(Transform));
9774         t1[9] = t1[10] = t1[11] = 0.0;
9775         memset(t2, 0, sizeof(Transform));
9776         t2[0] = ap2->aniso->bij[0];
9777         t2[4] = ap2->aniso->bij[1];
9778         t2[8] = ap2->aniso->bij[2];
9779         t2[1] = t2[3] = ap2->aniso->bij[3];
9780         t2[2] = t2[6] = ap2->aniso->bij[4];
9781         t2[5] = t2[7] = ap2->aniso->bij[5];
9782         TransformMul(t2, t1, t2);
9783         TransformInvert(t1, t1);
9784         TransformMul(t2, t2, t1);
9785         MoleculeSetAniso(mp, idx, 0, t2[0], t2[4], t2[8], t2[1], t2[2], t2[5], (ap2->aniso->has_bsig ? ap2->aniso->bsig : NULL));
9786 }
9787
9788 int
9789 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic, int convertCoordinates)
9790 {
9791         static Vector zeroVec = {0, 0, 0};
9792         XtalCell b;
9793         Transform cmat;
9794         int i, n;
9795         Atom *ap;
9796         if (mp == NULL)
9797                 return 0;
9798         if (mp->cell != NULL)
9799                 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
9800         else
9801                 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
9802         if (ax == NULL) {
9803                 if (mp->cell != NULL) {
9804                         free(mp->cell);
9805                         mp->needsMDRebuild = 1;
9806                 }
9807                 mp->cell = NULL;
9808                 return 0;
9809         }       
9810         memset(&b, 0, sizeof(b));
9811         b.axes[0] = (ax != NULL ? *ax : zeroVec);
9812         b.axes[1] = (ay != NULL ? *ay : zeroVec);
9813         b.axes[2] = (az != NULL ? *az : zeroVec);
9814         b.origin = *ao;
9815         memmove(b.flags, periodic, 3);
9816         if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
9817                 return -1;
9818         __MoleculeLock(mp);
9819         if (mp->cell == NULL) {
9820                 mp->needsMDRebuild = 1;
9821         } else {
9822                 if (mp->cell->has_sigma) {
9823                         /*  Keep the sigma  */
9824                         b.has_sigma = 1;
9825                         memmove(b.cellsigma, mp->cell->cellsigma, sizeof(mp->cell->cellsigma));
9826                 }
9827                 if ((b.flags[0] != mp->cell->flags[0]) || (b.flags[1] != mp->cell->flags[1]) || (b.flags[2] != mp->cell->flags[2])) {
9828                         mp->needsMDRebuild = 1;
9829                 }
9830                 free(mp->cell);
9831         }
9832         mp->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
9833         if (mp->cell != NULL) {
9834                 memmove(mp->cell, &b, sizeof(XtalCell));
9835                 TransformMul(cmat, b.tr, cmat);
9836                 /*  Update the coordinates (if requested)  */
9837                 if (convertCoordinates) {
9838                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9839                                 TransformVec(&(ap->r), cmat, &(ap->r));
9840                         }
9841                 }
9842                 
9843                 /*  Update the anisotropic parameters  */
9844                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9845                         Aniso *anp = ap->aniso;
9846                         if (anp != NULL) {
9847                                 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
9848                         }
9849                 }
9850                 n = 0;
9851         } else n = -2;  /*  Out of memory  */
9852         __MoleculeUnlock(mp);
9853         sMoleculeNotifyChangeAppearance(mp);
9854         return n;
9855 }
9856
9857 #pragma mark ====== Fragment manipulation ======
9858
9859 static void
9860 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
9861 {
9862         Atom *ap;
9863         Int i, *cp, idx2;
9864         if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
9865                 return;
9866         IntGroupAdd(result, idx, 1);
9867         ap = ATOM_AT_INDEX(mp->atoms, idx);
9868         cp = AtomConnectData(&ap->connect);
9869         for (i = 0; i < ap->connect.count; i++) {
9870                 idx2 = cp[i];
9871                 if (IntGroupLookup(result, idx2, NULL))
9872                         continue;
9873                 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, idx2)->anchor != NULL)
9874                         continue;  /*  bond between two pi_anchors is ignored  */
9875                 sMoleculeFragmentSub(mp, idx2, result, exatoms);
9876         }
9877         if (ap->anchor != NULL) {
9878                 cp = AtomConnectData(&ap->anchor->connect);
9879                 for (i = 0; i < ap->anchor->connect.count; i++) {
9880                         idx2 = cp[i];
9881                         if (IntGroupLookup(result, idx2, NULL))
9882                                 continue;
9883                         sMoleculeFragmentSub(mp, idx2, result, exatoms);
9884                 }
9885         }
9886 }
9887
9888 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
9889     not containing the atoms in exatoms  */
9890 IntGroup *
9891 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
9892 {
9893         IntGroup *result;
9894         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9895                 return NULL;
9896         result = IntGroupNew();
9897         sMoleculeFragmentSub(mp, n1, result, exatoms);
9898         return result;
9899 }
9900
9901 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
9902     not containing the atoms n2, n3, ... (terminated by -1)  */
9903 IntGroup *
9904 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
9905 {
9906         int i;
9907         IntGroup *exatoms, *result;
9908         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9909                 return NULL;
9910         exatoms = IntGroupNew();
9911         for (i = 0; i < argc; i++)
9912                 IntGroupAdd(exatoms, argv[i], 1);
9913         result = IntGroupNew();
9914         sMoleculeFragmentSub(mp, n1, result, exatoms);
9915         IntGroupRelease(exatoms);
9916         return result;
9917 }
9918
9919 /*  The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
9920     not containing the atoms in exatoms  */
9921 IntGroup *
9922 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
9923 {
9924         IntGroupIterator iter;
9925         IntGroup *result;
9926         int i;
9927         if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
9928                 return NULL;
9929         IntGroupIteratorInit(inatoms, &iter);
9930         result = IntGroupNew();
9931         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9932                 sMoleculeFragmentSub(mp, i, result, exatoms);
9933         }
9934         IntGroupIteratorRelease(&iter);
9935         return result;
9936 }
9937
9938 /*  Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
9939     group is bound to the rest of the molecule via only one bond.
9940         If the result is true, then the atoms belonging to the (only) bond are returned
9941         in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
9942         and n2 can be NULL, if those informations are not needed.  */
9943 int
9944 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
9945 {
9946         Int i, i1, i2, j, k, bond_count, nval1, nval2, *cp;
9947         Atom *ap;
9948         if (mp == NULL || mp->natoms == 0 || group == NULL)
9949                 return 0;  /*  Invalid arguments  */
9950         bond_count = 0;
9951         for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
9952                 i2 = IntGroupGetEndPoint(group, i);
9953                 for (j = i1; j < i2; j++) {
9954                         if (j < 0 || j >= mp->natoms)
9955                                 return 0;  /*  Invalid atom group  */
9956                         ap = ATOM_AT_INDEX(mp->atoms, j);
9957                         cp = AtomConnectData(&ap->connect);
9958                         for (k = 0; k < ap->connect.count; k++) {
9959                                 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, cp[k])->anchor != NULL)
9960                                         continue;  /*  Ignore bond between two pi_anchors  */
9961                                 if (IntGroupLookup(group, cp[k], NULL) == 0) {
9962                                         bond_count++;
9963                                         nval1 = j;
9964                                         nval2 = cp[k];
9965                                         if (bond_count > 1)
9966                                                 return 0;  /*  Too many bonds  */
9967                                 }
9968                         }
9969                         if (ap->anchor != NULL) {
9970                                 cp = AtomConnectData(&ap->anchor->connect);
9971                                 for (k = 0; k < ap->anchor->connect.count; k++) {
9972                                         if (IntGroupLookup(group, cp[k], NULL) == 0) {
9973                                                 bond_count++;
9974                                                 nval1 = j;
9975                                                 nval2 = cp[k];
9976                                                 if (bond_count > 1)
9977                                                         return 0;  /*  Too many bonds  */
9978                                         }
9979                                 }                                       
9980                         }
9981                 }
9982         }
9983         if (bond_count == 1) {
9984                 if (n1 != NULL)
9985                         *n1 = nval1;
9986                 if (n2 != NULL)
9987                         *n2 = nval2;
9988                 return 1;
9989         } else {
9990                 return 0;
9991         }       
9992 }
9993
9994 /*  Returns non-zero if the given group is 'rotatable' in the molecule. The group
9995     is said to be 'rotatable' when either of the following conditions are met; (1)
9996         the group is detachable, or (2) the group consists of two bonded atoms that define
9997         a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
9998         (either a new IntGroup or 'group' with incremented reference count; thus the caller
9999         is responsible for releasing the returned value).  */
10000 int
10001 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
10002 {
10003         int i1, i2;
10004         if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
10005                 if (rotGroup != NULL) {
10006                         IntGroupRetain(group);
10007                         *rotGroup = group;
10008                 }
10009                 return 1;
10010         }
10011         if (group != NULL && IntGroupGetCount(group) == 2) {
10012                 i1 = IntGroupGetNthPoint(group, 0);
10013                 i2 = IntGroupGetNthPoint(group, 1);
10014                 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
10015                         IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
10016                         if (frag == NULL)
10017                                 return 0;
10018                         i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
10019                         if (i1 == 0) {
10020                                 IntGroupRelease(frag);
10021                                 if (rotGroup != NULL)
10022                                         *rotGroup = NULL;
10023                                 return 0;
10024                         }
10025                         if (rotGroup != NULL)
10026                                 *rotGroup = frag;
10027                         else if (frag != NULL)
10028                                 IntGroupRelease(frag);
10029                         return i1;
10030                 }
10031         }
10032         return 0;
10033 }
10034
10035 #pragma mark ====== Multiple frame ======
10036
10037 int
10038 MoleculeGetNumberOfFrames(Molecule *mp)
10039 {
10040         if (mp == NULL)
10041                 return 0;
10042         if (mp->nframes <= 0) {
10043                 /*  Recalculate  */
10044                 int i, n;
10045                 Atom *ap;
10046                 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10047                         if (ap->nframes > n)
10048                                 n = ap->nframes;
10049                 }
10050                 if (n == 0)
10051                         n = 1;
10052                 mp->nframes = n;
10053         }
10054         return mp->nframes;
10055 }
10056
10057 int
10058 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
10059 {
10060         int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
10061         Vector *tempv, *vp;
10062         Atom *ap;
10063         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
10064                 return -1;
10065
10066         n_old = MoleculeGetNumberOfFrames(mp);
10067         n_new = n_old + count;
10068         last_inserted = IntGroupGetNthPoint(group, count - 1);
10069         if (n_new <= last_inserted) {
10070                 exframes = last_inserted - n_new + 1;  /*  number of extra frames that will be silently inserted  */
10071                 n_new += exframes;
10072         } else exframes = 0;
10073
10074         tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4);  /*  "*4" for handling cells  */
10075         if (tempv == NULL)
10076                 return -1;
10077
10078         __MoleculeLock(mp);
10079
10080         /*  Copy back the current coordinates  */
10081         /*  No change in the current coordinates, but the frame buffer is updated  */
10082         MoleculeSelectFrame(mp, mp->cframe, 1); 
10083         
10084         /*  Expand ap->frames for all atoms  */
10085         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10086                 if (ap->frames == NULL)
10087                         vp = (Vector *)calloc(sizeof(Vector), n_new);
10088                 else
10089                         vp = (Vector *)realloc(ap->frames, sizeof(Vector) * n_new);
10090                 if (vp == NULL) {
10091                         __MoleculeUnlock(mp);
10092                         return -1;
10093                 }
10094                 for (j = ap->nframes; j < n_new; j++)
10095                         vp[j] = ap->r;
10096                 ap->frames = vp;
10097         }
10098         if (mp->cell != NULL) {
10099                 j = mp->nframe_cells;
10100                 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, n_new - 1, NULL);
10101                 for (i = j; i < n_new; i++) {
10102                         /*  Set the current cell parameters to the expanded frames  */
10103                         mp->frame_cells[i * 4] = mp->cell->axes[0];
10104                         mp->frame_cells[i * 4 + 1] = mp->cell->axes[1];
10105                         mp->frame_cells[i * 4 + 2] = mp->cell->axes[2];
10106                         mp->frame_cells[i * 4 + 3] = mp->cell->origin;
10107                 }
10108         }
10109         
10110         /*  group = [n0..n1-1, n2..n3-1, ...]  */
10111         /*  s = t = 0,  */
10112         /*  tempv[0..n0-1] <- ap[0..n0-1], s += n0,
10113             tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
10114                 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
10115                 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
10116                 ...
10117                 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
10118                 At last, s will become n_old and t will become count.  */
10119         for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10120                 int s, t, ns, ne, mult;
10121                 Vector cr;
10122                 ne = s = t = 0;
10123                 if (i == mp->natoms) {
10124                         if (mp->cell == NULL || mp->frame_cells == NULL)
10125                                 break;
10126                         vp = mp->frame_cells;
10127                         mult = 4;
10128                 } else {
10129                         cr = ap->r;
10130                         vp = ap->frames;
10131                         mult = 1;
10132                 }
10133                 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10134                         if (ns > ne) {
10135                                 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
10136                                 s += ns - ne;
10137                         }
10138                         ne = IntGroupGetEndPoint(group, j);
10139                         while (ns < ne) {
10140                                 if (i == mp->natoms) {
10141                                         if (inFrameCell != NULL) {
10142                                                 tempv[ns * 4] = inFrameCell[t * 4];
10143                                                 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
10144                                                 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
10145                                                 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
10146                                         } else {
10147                                                 tempv[ns * 4] = mp->cell->axes[0];
10148                                                 tempv[ns * 4 + 1] = mp->cell->axes[1];
10149                                                 tempv[ns * 4 + 2] = mp->cell->axes[2];
10150                                                 tempv[ns * 4 + 3] = mp->cell->origin;
10151                                         }
10152                                 } else {
10153                                         if (inFrame != NULL)
10154                                                 tempv[ns] = inFrame[natoms * t + i];
10155                                         else
10156                                                 tempv[ns] = cr;
10157                                 }
10158                                 t++;
10159                                 ns++;
10160                         }
10161                 }
10162                 if (n_new > ne) {
10163                         memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
10164                         s += n_new - ne;
10165                 }
10166                 if (i < mp->natoms)
10167                         ap->nframes = n_new;
10168                 memmove(vp, tempv, sizeof(Vector) * mult * n_new);
10169         }
10170         free(tempv);
10171         mp->nframes = n_new;
10172         MoleculeSelectFrame(mp, last_inserted, 0);
10173         MoleculeIncrementModifyCount(mp);
10174         __MoleculeUnlock(mp);
10175         return count;
10176 }
10177
10178 int
10179 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
10180 {
10181         int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
10182         Vector *tempv, *vp;
10183         Atom *ap;
10184         IntGroup *group, *group2;
10185
10186         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
10187                 return -1;
10188
10189         /*  outFrame[] should have enough size for Vector * natoms * group.count  */
10190         memset(outFrame, 0, sizeof(Vector) * natoms * count);
10191         if (mp->cell != NULL && mp->frame_cells != NULL)
10192                 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
10193
10194         n_old = MoleculeGetNumberOfFrames(mp);
10195         if (n_old == 1)
10196                 return -2;  /*  Cannot delete last frame  */
10197
10198         group = IntGroupNew();
10199         group2 = IntGroupNewWithPoints(0, n_old, -1);
10200         IntGroupIntersect(inGroup, group2, group);
10201         IntGroupRelease(group2);
10202         count = IntGroupGetCount(group);
10203         n_new = n_old - count;
10204         if (n_new < 1) {
10205                 IntGroupRelease(group);
10206                 return -2;  /*  Trying to delete too many frames  */
10207         }
10208         tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4);  /*  "*4" for handling cells  */
10209         if (tempv == NULL) {
10210                 IntGroupRelease(group);
10211                 return -1;
10212         }
10213
10214         __MoleculeLock(mp);
10215
10216         /*  Copy back the current coordinates  */
10217         /*  No change in the current coordinates, but the frame buffer is updated  */
10218         MoleculeSelectFrame(mp, mp->cframe, 1); 
10219
10220         /*  Determine which frame should be selected after removal is completed  */
10221         {
10222                 int n1;
10223                 if (IntGroupLookup(group, mp->cframe, &i)) {
10224                         /*  cframe will be removed  */
10225                         n1 = IntGroupGetStartPoint(group, i) - 1;
10226                         if (n1 < 0)
10227                                 n1 = IntGroupGetEndPoint(group, i);
10228                 } else n1 = mp->cframe;
10229                 /*  Change to that frame  */
10230                 MoleculeSelectFrame(mp, n1, 0);
10231                 group2 = IntGroupNewFromIntGroup(group);
10232                 IntGroupReverse(group2, 0, n_old);
10233                 new_cframe = IntGroupLookupPoint(group2, n1);
10234                 if (new_cframe < 0)
10235                         return -3;  /*  This cannot happen  */
10236                 IntGroupRelease(group2);
10237         }
10238
10239         /*  group = [n0..n1-1, n2..n3-1, ...]  */
10240         /*  s = t = 0, */
10241         /*  tempv[0..n0-1] -> ap[0..n0-1], s += n0,
10242             tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
10243                 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
10244                 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
10245                 ...
10246                 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
10247                 At last, s will become n_new and t will become count.  */
10248         nframes = 0;
10249         for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10250                 int s, t, j, ns, ne;
10251                 int mult;
10252                 /*  if i == mp->natoms, mp->frame_cells is handled  */
10253                 if (i == mp->natoms) {
10254                         if (mp->cell == NULL || mp->frame_cells == NULL)
10255                                 break;
10256                         mult = 4;
10257                         vp = mp->frame_cells;
10258                         old_count = n_old;
10259                 } else {
10260                         mult = 1;
10261                         vp = ap->frames;
10262                         if (vp == NULL) {
10263                                 ap->frames = vp = (Vector *)calloc(sizeof(Vector), n_old);
10264                                 if (vp == NULL) {
10265                                         __MoleculeUnlock(mp);
10266                                         return -1;
10267                                 }
10268                         }
10269                         old_count = ap->nframes;
10270                 }
10271
10272                 /*  Copy vp to tempv  */
10273                 memset(tempv, 0, sizeof(Vector) * mult * n_old);
10274                 memmove(tempv, vp, sizeof(Vector) * mult * (old_count > n_old ? n_old : old_count));
10275                 ne = ns = s = t = 0;
10276                 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10277                         if (ns > n_old)
10278                                 ns = n_old;
10279                         if (ns > ne) {
10280                                 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (ns - ne));
10281                                 s += ns - ne;
10282                         }
10283                         ne = IntGroupGetEndPoint(group, j);
10284                         if (ne > n_old)
10285                                 ne = n_old;
10286                         while (ns < ne) {
10287                                 if (i < mp->natoms)
10288                                         outFrame[natoms * t + i] = tempv[ns];
10289                                 else if (outFrameCell != NULL) {
10290                                         outFrameCell[t * 4] = tempv[ns * 4];
10291                                         outFrameCell[t * 4 + 1] = tempv[ns * 4 + 1];
10292                                         outFrameCell[t * 4 + 2] = tempv[ns * 4 + 2];
10293                                         outFrameCell[t * 4 + 3] = tempv[ns * 4 + 3];
10294                                 }
10295                                 t++;
10296                                 ns++;
10297                         }
10298                 }
10299                 if (n_old > ne) {
10300                         memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (n_old - ne));
10301                         s += n_old - ne;
10302                 }
10303                 if (i < mp->natoms)
10304                         ap->nframes = s;
10305                 if (nframes < s)
10306                         nframes = s;
10307                 if (s <= 1) {
10308                         if (i < mp->natoms) {
10309                                 free(ap->frames);
10310                                 ap->frames = NULL;
10311                                 ap->nframes = 0;
10312                         } else {
10313                                 free(mp->frame_cells);
10314                                 mp->frame_cells = NULL;
10315                                 mp->nframe_cells = 0;
10316                         }
10317                 } else {
10318                         if (i < mp->natoms)
10319                                 ap->frames = (Vector *)realloc(ap->frames, sizeof(Vector) * s);
10320                         else {
10321                                 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, s - 1, NULL);
10322                                 mp->nframe_cells = s;
10323                         }
10324                 }
10325         }
10326         free(tempv);
10327         mp->nframes = nframes;
10328         
10329         /*  Select the "last" frame; do not "copy back" the coordinates to the frame table  */
10330 /*      i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
10331         MoleculeSelectFrame(mp, new_cframe, 0);
10332
10333         IntGroupRelease(group);
10334
10335         MoleculeIncrementModifyCount(mp);
10336         __MoleculeUnlock(mp);
10337         return count;
10338 }
10339
10340 int
10341 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
10342 {
10343         int i, cframe, nframes, modified;
10344         Atom *ap;
10345         cframe = mp->cframe;
10346         nframes = MoleculeGetNumberOfFrames(mp);
10347         if (frame == -1)
10348                 frame = mp->cframe;
10349         if (mp == NULL || mp->natoms == 0 || frame < 0 || frame >= nframes)
10350                 return -1;
10351         modified = 0;
10352         __MoleculeLock(mp);
10353         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10354                 if (copyback && cframe >= 0 && cframe < ap->nframes) {
10355                         /*  Write the current coordinate back to the frame array  */
10356                         ap->frames[cframe] = ap->r;
10357                 }
10358                 if (frame != cframe && frame >= 0 && frame < ap->nframes) {
10359                         /*  Read the coordinate from the frame array  */
10360                         ap->r = ap->frames[frame];
10361                         modified = 1;
10362                 }
10363         }
10364
10365         if (mp->cell != NULL && mp->frame_cells != NULL) {
10366                 /*  Write the current cell back to the frame_cells array  */
10367                 if (copyback && cframe >= 0) {
10368                         Vector *vp = (Vector *)AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, cframe, NULL);
10369                         vp[0] = mp->cell->axes[0];
10370                         vp[1] = mp->cell->axes[1];
10371                         vp[2] = mp->cell->axes[2];
10372                         vp[3] = mp->cell->origin;
10373                 }
10374                 /*  Set the cell from the frame array  */
10375                 if (frame != cframe && frame >= 0 && frame < mp->nframe_cells) {
10376                         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);
10377                         modified = 1;
10378                         MoleculeAmendBySymmetry(mp, NULL, NULL, NULL);
10379                 }
10380         }
10381         mp->cframe = frame;
10382         if (modified)
10383                 mp->needsMDCopyCoordinates = 1;
10384         __MoleculeUnlock(mp);
10385         sMoleculeNotifyChangeAppearance(mp);
10386         return frame;
10387 }
10388
10389 /*  If molecule is multi-frame, then flush the current information to the frame buffer.
10390     Returns the number of frames.  */
10391 int
10392 MoleculeFlushFrames(Molecule *mp)
10393 {
10394         int nframes = MoleculeGetNumberOfFrames(mp);
10395         if (nframes > 1)
10396                 MoleculeSelectFrame(mp, mp->cframe, 1);
10397         return nframes;
10398 }
10399
10400 #pragma mark ====== Pi Atoms ======
10401
10402 static inline void
10403 sMoleculeCalculatePiAnchorPosition(Atom *ap, Atom *atoms)
10404 {
10405         Int *cp, j, n;
10406         Atom *ap2;
10407         cp = AtomConnectData(&ap->anchor->connect);
10408         n = ap->anchor->connect.count;
10409         VecZero(ap->r);
10410         for (j = 0; j < n; j++) {
10411                 Double w = ap->anchor->coeffs[j];
10412                 ap2 = ATOM_AT_INDEX(atoms, cp[j]);
10413                 VecScaleInc(ap->r, ap2->r, w);
10414         }       
10415 }
10416
10417 void
10418 MoleculeUpdatePiAnchorPositions(Molecule *mol)
10419 {
10420         Int i;
10421         Atom *ap;
10422         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
10423                 if (ap->anchor == NULL)
10424                         continue;
10425                 sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
10426         }
10427 }
10428
10429 void
10430 MoleculeCalculatePiAnchorPosition(Molecule *mol, int idx)
10431 {
10432         Atom *ap;
10433         if (mol == NULL || idx < 0 || idx >= mol->natoms)
10434                 return;
10435         ap = ATOM_AT_INDEX(mol->atoms, idx);
10436         if (ap->anchor == NULL)
10437                 return;
10438         sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
10439 }
10440
10441 int
10442 MoleculeSetPiAnchorList(Molecule *mol, Int idx, Int nentries, Int *entries, Double *weights, Int *nUndoActions, struct MolAction ***undoActions)
10443 {
10444         Atom *ap;
10445         Int *ip, i, j, n, *np;
10446         Double d;
10447         if (mol == NULL || idx < 0 || idx >= mol->natoms || nentries <= 1)
10448                 return -1;  /*  Invalid argument  */
10449         if (weights != NULL) {
10450                 d = 0.0;
10451                 for (i = 0; i < nentries; i++) {
10452                         if (weights[i] <= 0.0) {
10453                                 return 10;  /*  Weights must be positive  */
10454                         }
10455                         d += weights[i];
10456                 }
10457                 d = 1.0 / d;
10458         } else d = 1.0 / nentries;
10459         ap = ATOM_AT_INDEX(mol->atoms, idx);
10460         if (ap->anchor != NULL) {
10461                 /*  Already an anchor: check if bonds/angles/dihedrals have entries related to this anchor  */
10462                 IntGroup *bg, *ag, *dg, *ig;
10463                 Int *ibuf, ibufsize;
10464                 MolAction *act;
10465                 bg = ag = dg = ig = NULL;
10466                 ip = AtomConnectData(&ap->anchor->connect);
10467                 for (i = 0; i < ap->anchor->connect.count; i++) {
10468                         n = ip[i];
10469                         for (j = 0; j < nentries; j++) {
10470                                 if (n == entries[j])
10471                                         break;
10472                         }
10473                         if (j == nentries) {
10474                                 /*  This entry will disappear: if any bond/angle/dihedral has idx-n pair, that should be removed.  */
10475                                 for (j = 0, np = mol->bonds; j < mol->nbonds; j++, np += 2) {
10476                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[0])) {
10477                                                 if (bg == NULL)
10478                                                         bg = IntGroupNew();
10479                                                 IntGroupAdd(bg, j, 1);
10480                                         }
10481                                 }
10482                                 for (j = 0, np = mol->angles; j < mol->nangles; j++, np += 3) {
10483                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) ||
10484                                                 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1])) {
10485                                                 if (ag == NULL)
10486                                                         ag = IntGroupNew();
10487                                                 IntGroupAdd(ag, j, 1);
10488                                         }
10489                                 }
10490                                 for (j = 0, np = mol->dihedrals; j < mol->ndihedrals; j++, np += 4) {
10491                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) || (idx == np[2] && n == np[3]) ||
10492                                                 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[3] && n == np[2])) {
10493                                                 if (dg == NULL)
10494                                                         dg = IntGroupNew();
10495                                                 IntGroupAdd(dg, j, 1);
10496                                         }
10497                                 }
10498                                 for (j = 0, np = mol->impropers; j < mol->nimpropers; j++, np += 4) {
10499                                         if ((idx == np[0] && n == np[2]) || (idx == np[1] && n == np[2]) || (idx == np[3] && n == np[2]) ||
10500                                                 (idx == np[2] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[2] && n == np[3])) {
10501                                                 if (ig == NULL)
10502                                                         ig = IntGroupNew();
10503                                                 IntGroupAdd(ig, j, 1);
10504                                         }
10505                                 }
10506                         }
10507                 }
10508                 ibuf = NULL;
10509                 ibufsize = 0;
10510                 if (ig != NULL) {
10511                         /*  Delete impropers (with undo info) */
10512                         i = IntGroupGetCount(ig);
10513                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
10514                         MoleculeDeleteImpropers(mol, ibuf, ig);
10515                         if (nUndoActions != NULL && undoActions != NULL) {
10516                                 act = MolActionNew(gMolActionAddImpropers, i * 4, ibuf, ig);
10517                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10518                         }
10519                         IntGroupRelease(ig);
10520                 }
10521                 if (dg != NULL) {
10522                         /*  Delete dihedrals (with undo info)  */
10523                         i = IntGroupGetCount(dg);
10524                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
10525                         MoleculeDeleteDihedrals(mol, ibuf, dg);
10526                         if (nUndoActions != NULL && undoActions != NULL) {
10527                                 act = MolActionNew(gMolActionAddDihedrals, i * 4, ibuf, dg);
10528                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10529                         }
10530                         IntGroupRelease(dg);
10531                 }
10532                 if (ag != NULL) {
10533                         /*  Delete angles (with undo info) */
10534                         i = IntGroupGetCount(ag);
10535                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 3 - 1, NULL);
10536                         MoleculeDeleteAngles(mol, ibuf, ag);
10537                         if (nUndoActions != NULL && undoActions != NULL) {
10538                                 act = MolActionNew(gMolActionAddAngles, i * 3, ibuf, ag);
10539                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10540                         }
10541                         IntGroupRelease(ag);
10542                 }
10543                 if (bg != NULL) {
10544                         /*  Delete bonds (with undo info) */
10545                         i = IntGroupGetCount(bg);
10546                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 2 - 1, NULL);
10547                         MoleculeDeleteBonds(mol, ibuf, bg, NULL, NULL);
10548                         if (nUndoActions != NULL && undoActions != NULL) {
10549                                 act = MolActionNew(gMolActionAddBondsForUndo, i * 2, ibuf, bg);
10550                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10551                         }
10552                         IntGroupRelease(bg);
10553                 }
10554         } else {
10555                 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
10556         }
10557         AtomConnectResize(&ap->anchor->connect, nentries);
10558         memmove(AtomConnectData(&ap->anchor->connect), entries, sizeof(Int) * nentries);
10559         AssignArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), nentries - 1, NULL);
10560         if (weights != NULL) {
10561                 memmove(ap->anchor->coeffs, weights, sizeof(Double) * nentries);
10562                 for (i = 0; i < nentries; i++)
10563                         ap->anchor->coeffs[i] *= d;   /*  Normalize weight  */
10564         } else {
10565                 for (i = 0; i < nentries; i++)
10566                         ap->anchor->coeffs[i] = d;
10567         }
10568         MoleculeCalculatePiAnchorPosition(mol, idx);
10569         return 0;
10570 }
10571
10572 #pragma mark ====== MO calculation ======
10573
10574 /*  Calculate an MO value for a single point.  */
10575 /*  Index is the MO number (1-based)  */
10576 /*  tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom.  */
10577 static Double
10578 sCalcMOPoint(Molecule *mp, const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
10579 {
10580         ShellInfo *sp;
10581         PrimInfo *pp;
10582         Double val, tval, *cnp, *tmpp, *mobasep, *mop;
10583         Int i, j;
10584         /*  Cache dr and |dr|^2  */
10585         for (i = 0; i < mp->natoms; i++) {
10586                 Vector r;
10587                 r = ATOM_AT_INDEX(mp->atoms, i)->r;
10588                 tmp[i * 4] = r.x = (vp->x - r.x) * kAngstrom2Bohr;
10589                 tmp[i * 4 + 1] = r.y = (vp->y - r.y) * kAngstrom2Bohr;
10590                 tmp[i * 4 + 2] = r.z = (vp->z - r.z) * kAngstrom2Bohr;
10591                 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
10592         }
10593         /*  Iterate over all shells  */
10594         val = 0.0;
10595         mobasep = bset->mo + (index - 1) * bset->ncomps;
10596         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
10597                 pp = bset->priminfos + sp->p_idx;
10598                 cnp = bset->cns + sp->cn_idx;
10599                 if (sp->a_idx >= mp->natoms)
10600                         return 0.0; /*  This may happen when molecule is edited after setting up MO info  */
10601                 tmpp = tmp + sp->a_idx * 4;
10602                 mop = mobasep + sp->m_idx;
10603                 switch (sp->sym) {
10604                         case kGTOType_S: {
10605                                 tval = 0;
10606                                 for (j = 0; j < sp->nprim; j++) {
10607                                         tval += *cnp++ * exp(-pp->A * tmpp[3]);
10608                                         pp++;
10609                                 }
10610                                 val += mop[0] * tval;
10611                                 break;
10612                         }
10613                         case kGTOType_P: {
10614                                 Double x, y, z;
10615                                 x = y = z = 0;
10616                                 for (j = 0; j < sp->nprim; j++) {
10617                                         tval = exp(-pp->A * tmpp[3]);
10618                                         x += *cnp++ * tval;
10619                                         y += *cnp++ * tval;
10620                                         z += *cnp++ * tval;
10621                                         pp++;
10622                                 }
10623                                 x *= mop[0] * tmpp[0];
10624                                 y *= mop[1] * tmpp[1];
10625                                 z *= mop[2] * tmpp[2];
10626                                 val += x + y + z;
10627                                 break;
10628                         }
10629                         case kGTOType_SP: {
10630                                 Double t, x, y, z;
10631                                 t = x = y = z = 0;
10632                                 for (j = 0; j < sp->nprim; j++) {
10633                                         tval = exp(-pp->A * tmpp[3]);
10634                                         t += *cnp++ * tval;
10635                                         x += *cnp++ * tval;
10636                                         y += *cnp++ * tval;
10637                                         z += *cnp++ * tval;
10638                                         pp++;
10639                                 }
10640                                 t *= mop[0];
10641                                 x *= mop[1] * tmpp[0];
10642                                 y *= mop[2] * tmpp[1];
10643                                 z *= mop[3] * tmpp[2];
10644                                 val += t + x + y + z;
10645                                 break;
10646                         }
10647                         case kGTOType_D: {
10648                                 Double xx, yy, zz, xy, xz, yz;
10649                                 xx = yy = zz = xy = xz = yz = 0;
10650                                 for (j = 0; j < sp->nprim; j++) {
10651                                         tval = exp(-pp->A * tmpp[3]);
10652                                         xx += *cnp++ * tval;
10653                                         yy += *cnp++ * tval;
10654                                         zz += *cnp++ * tval;
10655                                         xy += *cnp++ * tval;
10656                                         xz += *cnp++ * tval;
10657                                         yz += *cnp++ * tval;
10658                                         pp++;
10659                                 }
10660                                 xx *= mop[0] * tmpp[0] * tmpp[0];
10661                                 yy *= mop[1] * tmpp[1] * tmpp[1];
10662                                 zz *= mop[2] * tmpp[2] * tmpp[2];
10663                                 xy *= mop[3] * tmpp[0] * tmpp[1];
10664                                 xz *= mop[4] * tmpp[0] * tmpp[2];
10665                                 yz *= mop[5] * tmpp[1] * tmpp[2];
10666                                 val += xx + yy + zz + xy + xz + yz;
10667                                 break;
10668                         }
10669                         case kGTOType_D5: {
10670                                 Double d0, d1p, d1n, d2p, d2n;
10671                                 d0 = d1p = d1n = d2p = d2n = 0;
10672                                 for (j = 0; j < sp->nprim; j++) {
10673                                         tval = exp(-pp->A * tmpp[3]);
10674                                         d0 += *cnp++ * tval;
10675                                         d1p += *cnp++ * tval;
10676                                         d1n += *cnp++ * tval;
10677                                         d2p += *cnp++ * tval;
10678                                         d2n += *cnp++ * tval;
10679                                         pp++;
10680                                 }
10681                                 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
10682                                 d1p *= mop[1] * tmpp[0] * tmpp[2];
10683                                 d1n *= mop[2] * tmpp[1] * tmpp[2];
10684                                 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
10685                                 d2n *= mop[4] * tmpp[0] * tmpp[1];
10686                                 val += d0 + d1p + d1n + d2p + d2n;
10687                                 break;
10688                         }
10689                         /*  TODO: Support F/F7 and G/G9 type orbitals  */
10690                 }
10691         }
10692         return val;
10693 }
10694
10695 /*  Calculate one MO. The input vectors are angstrom unit (changed from bohr unit: 20140520)  */
10696 /*  mono is the MO number (1-based)  */
10697 int
10698 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)
10699 {
10700         int ix, iy, iz, n, nn;
10701         Cube *cp;
10702         Double *tmp;
10703         if (mp == NULL || mp->bset == NULL)
10704                 return -1;
10705         if (mp->bset->cns == NULL) {
10706                 if (sSetupGaussianCoefficients(mp->bset) != 0)
10707                         return -1;
10708         }
10709         if (mp->bset->natoms_bs > mp->natoms)
10710                 return -3;  /*  Number of atoms is smaller than expected (internal error)  */
10711         
10712         cp = (Cube *)calloc(sizeof(Cube), 1);
10713         if (cp == NULL) {
10714                 return -1;
10715         }
10716         cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
10717         if (cp->dp == NULL) {
10718                 free(cp);
10719                 return -1;
10720         }
10721         cp->idn = mono;
10722         cp->origin = *op;
10723         cp->dx = *dxp;
10724         cp->dy = *dyp;
10725         cp->dz = *dzp;
10726         cp->nx = nx;
10727         cp->ny = ny;
10728         cp->nz = nz;
10729         
10730         /*  TODO: use multithread  */
10731         tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms_bs * 4);
10732         if (tmp == NULL) {
10733                 free(cp->dp);
10734                 free(cp);
10735                 return -1;
10736         }
10737         n = nn = 0;
10738         for (ix = 0; ix < nx; ix++) {
10739                 Vector p;
10740                 for (iy = 0; iy < ny; iy++) {
10741                         for (iz = 0; iz < nz; iz++) {
10742                                 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
10743                                 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
10744                                 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
10745                                 cp->dp[n++] = sCalcMOPoint(mp, mp->bset, mono, &p, tmp);
10746                         }
10747                         if (callback != NULL && n - nn > 100) {
10748                                 nn = n;
10749                                 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
10750                                         free(cp->dp);
10751                                         free(cp);
10752                                         free(tmp);
10753                                         return -2;  /*  User interrupt  */
10754                                 }
10755                         }
10756                 }
10757         }
10758         free(tmp);
10759
10760         AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
10761         return mp->bset->ncubes - 1;
10762 }
10763
10764 /*  Output values are in angstrom unit (changed from bohr unit: 20140520)  */
10765 int
10766 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
10767 {
10768         int i;
10769         Vector rmin, rmax, r;
10770         Double dr, dx, dy, dz;
10771         Atom *ap;
10772         if (mp == NULL || mp->bset == NULL)
10773                 return -1;
10774         if (npoints <= 0)
10775                 npoints = 1000000;
10776         rmin.x = rmin.y = rmin.z = 1e10;
10777         rmax.x = rmax.y = rmax.z = -1e10;
10778         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10779                 dr = RadiusForAtomicNumber(ap->atomicNumber);
10780                 r = ap->r;
10781                 if (dr == 0.0)
10782                         dr = 1.0;
10783                 dr = dr * 3.0 + 2.0;
10784                 if (rmin.x > r.x - dr)
10785                         rmin.x = r.x - dr;
10786                 if (rmin.y > r.y - dr)
10787                         rmin.y = r.y - dr;
10788                 if (rmin.z > r.z - dr)
10789                         rmin.z = r.z - dr;
10790                 if (rmax.x < r.x + dr)
10791                         rmax.x = r.x + dr;
10792                 if (rmax.y < r.y + dr)
10793                         rmax.y = r.y + dr;
10794                 if (rmax.z < r.z + dr)
10795                         rmax.z = r.z + dr;
10796         }
10797         dx = rmax.x - rmin.x;
10798         dy = rmax.y - rmin.y;
10799         dz = rmax.z - rmin.z;
10800         dr = pow(dx * dy * dz / npoints, 1.0/3.0);
10801         *nx = floor(dx / dr + 0.5);
10802         *ny = floor(dy / dr + 0.5);
10803         *nz = floor(dz / dr + 0.5);
10804         if (*nx == 0)
10805                 *nx = 1;
10806         if (*ny == 0)
10807                 *ny = 1;
10808         if (*nz == 0)
10809                 *nz = 1;
10810         *op = rmin;
10811         xp->x = yp->y = zp->z = dr;
10812         xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
10813         return 0;
10814 }
10815
10816 const Cube *
10817 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
10818 {
10819         if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
10820                 return NULL;
10821         return mp->bset->cubes[index];
10822 }
10823
10824 int
10825 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
10826 {
10827         int i;
10828         if (mp == NULL || mp->bset == NULL)
10829                 return -1;
10830         for (i = 0; i < mp->bset->ncubes; i++) {
10831                 if (mp->bset->cubes[i]->idn == mono)
10832                         return i;
10833         }
10834         return -1;
10835 }
10836
10837 int
10838 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
10839 {
10840         int n;
10841         if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
10842                 return -1;
10843         CubeRelease(mp->bset->cubes[index]);
10844         if (index < n - 1)
10845                 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
10846         if (--(mp->bset->ncubes) == 0) {
10847                 free(mp->bset->cubes);
10848                 mp->bset->cubes = NULL;
10849         }
10850         return mp->bset->ncubes;
10851 }
10852
10853 int
10854 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
10855 {
10856         const Cube *cp;
10857         int i, j, k, n;
10858         FILE *fp;
10859         if (mp == NULL || mp->bset == NULL)
10860                 return -1;  /*  Molecule or the basis set information is empty  */
10861         cp = MoleculeGetCubeAtIndex(mp, index);
10862         if (cp == NULL)
10863                 return -2;  /*  MO not yet calculated  */
10864         fp = fopen(fname, "wb");
10865         if (fp == NULL)
10866                 return -3;  /*  Cannot create file  */
10867
10868         /*  Comment lines  */
10869         fprintf(fp, "%s MO=%d\n", comment, cp->idn);
10870         fprintf(fp, " MO coefficients\n");
10871         
10872         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms_bs),
10873                         cp->origin.x * kAngstrom2Bohr, cp->origin.y * kAngstrom2Bohr, cp->origin.z * kAngstrom2Bohr);
10874         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx,
10875                         cp->dx.x * kAngstrom2Bohr, cp->dx.y * kAngstrom2Bohr, cp->dx.z * kAngstrom2Bohr);
10876         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny,
10877                         cp->dy.x * kAngstrom2Bohr, cp->dy.y * kAngstrom2Bohr, cp->dy.z * kAngstrom2Bohr);
10878         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz,
10879                         cp->dz.x * kAngstrom2Bohr, cp->dz.y * kAngstrom2Bohr, cp->dz.z * kAngstrom2Bohr);
10880         
10881         /*  Atomic information  */
10882         for (i = 0; i < mp->natoms; i++) {
10883                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
10884                 /*  The second number should actually be the effective charge  */
10885                 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber,
10886                                 ap->r.x * kAngstrom2Bohr, ap->r.y * kAngstrom2Bohr, ap->r.z * kAngstrom2Bohr);
10887         }
10888         fprintf(fp, "%5d%5d\n", 1, 1);
10889         
10890         /*  3D data  */
10891         for (i = n = 0; i < cp->nx; i++) {
10892                 for (j = 0; j < cp->ny; j++) {
10893                         for (k = 0; k < cp->nz; k++) {
10894                                 /*  On Windows, the "%e" format writes the exponent in 3 digits, but
10895                                     this is not standard. So we avoid using %e  */
10896                                 Double d = cp->dp[n++];
10897                                 int exponent = (int)floor(log10(fabs(d)));
10898                                 Double base = d * pow(10, -1.0 * exponent);
10899                                 fprintf(fp, " %8.5fe%+03d", base, exponent);
10900                         /*      fprintf(fp, " %12.5e", d); */
10901                                 if (k == cp->nz - 1 || k % 6 == 5)
10902                                         fprintf(fp, "\n");
10903                         }
10904                 }
10905         }
10906         fclose(fp);
10907         return 0;
10908 }
10909
10910 #pragma mark ====== Marching Cube (for isosurface) ======
10911
10912 MCube *
10913 MoleculeClearMCube(Molecule *mol, Int nx, Int ny, Int nz, const Vector *origin, Double dx, Double dy, Double dz)
10914 {
10915         MCube *mc = mol->mcube;
10916         int i;
10917         float rgba[8] = { 1, 1, 1, 0.6, 0, 0, 1, 0.6 };
10918         if (mc != NULL) {
10919                 free(mc->dp);
10920                 free(mc->radii);
10921                 free(mc->c[0].fp);
10922                 free(mc->c[0].cubepoints);
10923                 free(mc->c[0].triangles);
10924                 free(mc->c[1].fp);
10925                 free(mc->c[1].cubepoints);
10926                 free(mc->c[1].triangles);
10927                 memmove(rgba, mc->c[0].rgba, sizeof(float) * 4);
10928                 memmove(rgba + 4, mc->c[1].rgba, sizeof(float) * 4);
10929                 free(mc);
10930                 mol->mcube = NULL;
10931         }
10932         if (nx > 0 && ny > 0 && nz > 0) {
10933                 mc = (MCube *)calloc(sizeof(MCube), 1);
10934                 /*  round up to nearest 4N+1 integer  */
10935                 dx *= nx;
10936                 dy *= ny;
10937                 dz *= nz;
10938                 mc->nx = (nx + 2) / 4 * 4 + 1;
10939                 mc->ny = (ny + 2) / 4 * 4 + 1;
10940                 mc->nz = (nz + 2) / 4 * 4 + 1;
10941                 mc->dx = dx / mc->nx;
10942                 mc->dy = dy / mc->ny;
10943                 mc->dz = dz / mc->nz;
10944                 mc->origin = *origin;
10945                 mc->dp = (Double *)malloc(sizeof(Double) * mc->nx * mc->ny * mc->nz);
10946                 if (mc->dp == NULL) {
10947                         free(mc);
10948                         return NULL;
10949                 }
10950                 mc->c[0].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
10951                 mc->c[1].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
10952                 if (mc->c[0].fp == NULL || mc->c[1].fp == NULL) {
10953                         free(mc->c[0].fp);
10954                         free(mc->c[1].fp);
10955                         free(mc->dp);
10956                         free(mc);
10957                         return NULL;
10958                 }
10959                 for (i = 0; i < mc->nx * mc->ny * mc->nz; i++) {
10960                         mc->dp[i] = DBL_MAX;
10961                 }
10962                 memmove(mc->c[0].rgba, rgba, sizeof(float) * 4);
10963                 memmove(mc->c[1].rgba, rgba + 4, sizeof(float) * 4);
10964                 mol->mcube = mc;
10965         }
10966         MoleculeCallback_notifyModification(mol, 0);
10967         return mol->mcube;
10968 }
10969
10970 static int sMarchingCubeTable[256][16] = {
10971         {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10972         {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10973         {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10974         {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10975         {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10976         {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10977         {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10978         {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
10979         {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10980         {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10981         {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10982         {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
10983         {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10984         {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
10985         {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
10986         {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10987         {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10988         {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10989         {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10990         {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
10991         {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10992         {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
10993         {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
10994         {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
10995         {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10996         {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
10997         {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
10998         {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
10999         {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
11000         {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
11001         {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
11002         {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
11003         {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11004         {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11005         {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11006         {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
11007         {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11008         {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
11009         {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
11010         {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
11011         {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11012         {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
11013         {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
11014         {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
11015         {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
11016         {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
11017         {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
11018         {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
11019         {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11020         {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
11021         {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
11022         {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11023         {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
11024         {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
11025         {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
11026         {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
11027         {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
11028         {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
11029         {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
11030         {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
11031         {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
11032         {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
11033         {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
11034         {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11035         {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11036         {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11037         {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11038         {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
11039         {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11040         {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
11041         {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
11042         {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
11043         {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11044         {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
11045         {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
11046         {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
11047         {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
11048         {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
11049         {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
11050         {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
11051         {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11052         {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
11053         {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
11054         {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
11055         {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
11056         {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
11057         {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
11058         {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
11059         {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
11060         {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
11061         {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
11062         {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
11063         {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
11064         {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
11065         {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
11066         {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
11067         {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11068         {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
11069         {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
11070         {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
11071         {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
11072         {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
11073         {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11074         {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
11075         {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
11076         {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
11077         {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
11078         {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
11079         {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
11080         {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
11081         {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
11082         {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11083         {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
11084         {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
11085         {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
11086         {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
11087         {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
11088         {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
11089         {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
11090         {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11091         {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
11092         {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
11093         {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
11094         {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
11095         {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
11096         {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11097         {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
11098         {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11099         {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11100         {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11101         {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11102         {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
11103         {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11104         {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
11105         {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
11106         {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
11107         {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11108         {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
11109         {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
11110         {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
11111         {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
11112         {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
11113         {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
11114         {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
11115         {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11116         {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
11117         {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
11118         {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
11119         {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
11120         {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
11121         {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
11122         {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
11123         {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
11124         {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11125         {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
11126         {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
11127         {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
11128         {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
11129         {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
11130         {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11131         {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11132         {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
11133         {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
11134         {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
11135         {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
11136         {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
11137         {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
11138         {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
11139         {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
11140         {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
11141         {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
11142         {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
11143         {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
11144         {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
11145         {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
11146         {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
11147         {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
11148         {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
11149         {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
11150         {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
11151         {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
11152         {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
11153         {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
11154         {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
11155         {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
11156         {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
11157         {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
11158         {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11159         {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
11160         {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
11161         {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11162         {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11163         {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11164         {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
11165         {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
11166         {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
11167         {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
11168         {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
11169         {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
11170         {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
11171         {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
11172         {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
11173         {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
11174         {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
11175         {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11176         {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
11177         {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
11178         {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11179         {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
11180         {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
11181         {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
11182         {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
11183         {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
11184         {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
11185         {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
11186         {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11187         {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
11188         {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
11189         {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
11190         {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
11191         {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
11192         {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11193         {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
11194         {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11195         {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
11196         {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
11197         {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
11198         {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
11199         {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
11200         {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
11201         {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
11202         {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
11203         {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
11204         {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
11205         {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
11206         {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11207         {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
11208         {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
11209         {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11210         {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11211         {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11212         {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
11213         {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
11214         {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11215         {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
11216         {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
11217         {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11218         {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11219         {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
11220         {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11221         {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
11222         {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11223         {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11224         {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11225         {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11226         {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
11227 };
11228
11229 /*  Recalculate the MCube  */
11230 /*  If idn < 0, then the current grid settings and values are unchanged, and */
11231 /*  only the marching cubes are regenerated.  */
11232 int
11233 MoleculeUpdateMCube(Molecule *mol, int idn)
11234 {
11235         Int flags, retval, step, hstep, sn;
11236         Int n, ix, iy, iz, nx, ny, nz;
11237         Int nn, iix, iiy, iiz;
11238         Int ncubepoints, c1, c2, c3;
11239         Int *ip;
11240         Double thres, *tmp, dd;
11241         Vector p;
11242         MCube *mc;
11243         MCubePoint *mcp;
11244         Atom *ap;
11245
11246         if (mol == NULL || mol->bset == NULL || mol->mcube == NULL)
11247                 return -1;
11248         if (mol->bset->cns == NULL) {
11249                 if (sSetupGaussianCoefficients(mol->bset) != 0)
11250                         return -1;
11251         }
11252         if (mol->bset->natoms_bs > mol->natoms)
11253                 return -1;  /*  Number of atoms is smaller than expected  */
11254
11255         mc = mol->mcube;
11256         if (idn > 0) {
11257                 ShellInfo *sp;
11258                 Double *mobasep, *mop, mopmax;
11259                 Double xmin, xmax, ymin, ymax, zmin, zmax;
11260                 /*  Clear mcube values  */
11261                 for (ix = 0; ix < mc->nx * mc->ny * mc->nz; ix++) {
11262                         mc->dp[ix] = DBL_MAX;
11263                         mc->c[0].fp[ix] = 0;
11264                         mc->c[1].fp[ix] = 0;
11265                 }
11266                 mc->idn = idn;
11267                 /*  Estimate the orbital sizes  */
11268                 mc->radii = (Double *)realloc(mc->radii, sizeof(Double) * mol->natoms);
11269                 if (mc->radii == NULL)
11270                         return -2;  /*  Out of memory  */
11271                 mc->nradii = mol->natoms;
11272                 memset(mc->radii, 0, sizeof(Double) * mc->nradii);
11273                 mobasep = mol->bset->mo + (mc->idn - 1) * mol->bset->ncomps;
11274                 mopmax = 0.0;
11275                 for (ix = 0, sp = mol->bset->shells; ix < mol->bset->nshells; ix++, sp++) {
11276                         if (sp->a_idx >= mol->natoms)
11277                                 continue;  /*  This may happen when molecule is edited after setting up MO info  */
11278                         mop = mobasep + sp->m_idx;
11279                         for (iy = 0; iy < sp->ncomp; iy++) {
11280                                 dd = fabs(mop[iy]);
11281                                 if (dd > mc->radii[sp->a_idx])
11282                                         mc->radii[sp->a_idx] = dd;
11283                                 if (dd > mopmax)
11284                                         mopmax = dd;
11285                         }
11286                 }
11287                 xmin = ymin = zmin = 1e10;
11288                 xmax = ymax = zmax = -1e10;
11289                 for (ix = 0, ap = mol->atoms; ix < mol->natoms; ix++, ap = ATOM_NEXT(ap)) {
11290                         dd = RadiusForAtomicNumber(ap->atomicNumber);
11291                         dd = (dd * 2.0 + 1.0) * (mc->radii[ix] / mopmax) * (mc->expand > 0.0 ? mc->expand : 1.0);
11292                         mc->radii[ix] = dd;
11293                         p = ap->r;
11294                         dd += 0.1;
11295                         if (p.x - dd < xmin)
11296                                 xmin = p.x - dd;
11297                         if (p.y - dd < ymin)
11298                                 ymin = p.y - dd;
11299                         if (p.z - dd < zmin)
11300                                 zmin = p.z - dd;
11301                         if (p.x + dd > xmax)
11302                                 xmax = p.x + dd;
11303                         if (p.y + dd > ymax)
11304                                 ymax = p.y + dd;
11305                         if (p.z + dd > zmax)
11306                                 zmax = p.z + dd;
11307                 }
11308                 mc->origin.x = xmin;
11309                 mc->origin.y = ymin;
11310                 mc->origin.z = zmin;
11311                 mc->dx = (xmax - xmin) / mc->nx;
11312                 mc->dy = (ymax - ymin) / mc->ny;
11313                 mc->dz = (zmax - zmin) / mc->nz;
11314         }
11315         
11316         /*  Temporary work area  */
11317         tmp = (Double *)calloc(sizeof(Double), mol->bset->natoms_bs * 4);
11318         if (tmp == NULL)
11319                 return -2;
11320         
11321         /*  TODO: use multithread  */
11322         nx = mc->nx;
11323         ny = mc->ny;
11324         nz = mc->nz;
11325         step = 4;
11326         
11327 #if 1
11328         /*  Calculate points within certain distances from atoms  */
11329         for (nn = 0, ap = mol->atoms; nn < mol->natoms; nn++, ap = ATOM_NEXT(ap)) {
11330         /*      dd = RadiusForAtomicNumber(ap->atomicNumber);
11331                 if (dd == 0.0)
11332                         dd = 1.0;
11333                 dd = dd * 1.5 + 1.0; */
11334                 dd = mc->radii[nn];
11335                 p.x = ap->r.x - dd - mc->origin.x;
11336                 p.y = ap->r.y - dd - mc->origin.y;
11337                 p.z = ap->r.z - dd - mc->origin.z;
11338                 c1 = p.x / mc->dx;
11339                 c2 = p.y / mc->dy;
11340                 c3 = p.z / mc->dz;
11341                 iix = c1 + ceil(dd * 2.0 / mc->dx);
11342                 iiy = c2 + ceil(dd * 2.0 / mc->dy);
11343                 iiz = c3 + ceil(dd * 2.0 / mc->dz);
11344                 if (c1 < 0)
11345                         c1 = 0;
11346                 if (c2 < 0)
11347                         c2 = 0;
11348                 if (c3 < 0)
11349                         c3 = 0;
11350                 if (iix >= nx)
11351                         iix = nx - 1;
11352                 if (iiy >= ny)
11353                         iiy = ny - 1;
11354                 if (iiz >= nz)
11355                         iiz = nz - 1;
11356                 for (ix = c1; ix <= iix; ix++) {
11357                         p.x = mc->origin.x + mc->dx * ix;
11358                         for (iy = c2; iy <= iiy; iy++) {
11359                                 p.y = mc->origin.y + mc->dy * iy;
11360                                 for (iz = c3; iz <= iiz; iz++) {
11361                                         n = (ix * ny + iy) * nz + iz;
11362                                         if (mc->dp[n] == DBL_MAX) {
11363                                                 p.z = mc->origin.z + mc->dz * iz;
11364                                                 mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
11365                                         }
11366                                 }
11367                         }
11368                 }
11369         }
11370         
11371 #else
11372         /*  (i * step, j * step, k * step)  */
11373         for (ix = 0; ix < nx; ix += step) {
11374                 for (iy = 0; iy < ny; iy += step) {
11375                         for (iz = 0; iz < nz; iz += step) {
11376                                 n = (ix * ny + iy) * nz + iz;
11377                                 if (mc->dp[n] == DBL_MAX) {
11378                                         p.x = mc->origin.x + mc->dx * ix;
11379                                         p.y = mc->origin.y + mc->dy * iy;
11380                                         p.z = mc->origin.z + mc->dz * iz;
11381                                         mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
11382                                 }
11383                                 n += step;
11384                         }
11385                 }
11386         }
11387         
11388         /*  Intermediate points  */
11389         for (step = 4; step > 1; step /= 2) {
11390                 hstep = step / 2;
11391                 for (sn = 0; sn <= 1; sn++) {
11392                         n = 0;
11393                         for (ix = 0; ix < nx - 1; ix += step) {
11394                                 for (iy = 0; iy < ny - 1; iy += step) {
11395                                         for (iz = 0; iz < nz - 1; iz += step) {
11396                                                 flags = 0;
11397                                                 thres = mc->thres * (sn == 0 ? 1 : -1);
11398                                                 n = (ix * ny + iy) * nz + iz;
11399                                                 if (mc->dp[n] == DBL_MAX || mc->dp[n + step * (nz * (ny + 1) + 1)] == DBL_MAX)
11400                                                         continue;
11401                                                 /*  (ix, iy, iz)  */
11402                                                 if (mc->dp[n] >= thres)
11403                                                         flags |= 1;
11404                                                 /*  (ix + step, iy, iz)  */
11405                                                 if (mc->dp[n + step * ny * nz] >= thres)
11406                                                         flags |= 2;
11407                                                 /*  (ix, iy + step, iz)  */
11408                                                 if (mc->dp[n + step * nz] >= thres)
11409                                                         flags |= 4;
11410                                                 /*  (ix + 4, iy + step, iz)  */
11411                                                 if (mc->dp[n + step * nz * (ny + 1)] >= thres)
11412                                                         flags |= 8;
11413                                                 /*  (ix, iy, iz + step)  */
11414                                                 if (mc->dp[n + step] >= thres)
11415                                                         flags |= 16;
11416                                                 if (mc->dp[n + step * (ny * nz + 1)] >= thres)
11417                                                         flags |= 32;
11418                                                 /*  (ix, iy + step, iz + step)  */
11419                                                 if (mc->dp[n + step * (nz + 1)] >= thres)
11420                                                         flags |= 64;
11421                                                 /*  (ix + step, iy + step, iz + step)  */
11422                                                 if (mc->dp[n + step * (nz * (ny + 1) + 1)] >= thres)
11423                                                         flags |= 128;
11424                                                 if (flags != 0 && flags != 255) {
11425                                                         /*  Calc the intermediate points  */
11426                                                         for (iix = 0; iix <= step; iix += hstep) {
11427                                                                 for (iiy = 0; iiy <= step; iiy += hstep) {
11428                                                                         for (iiz = 0; iiz <= step; iiz += hstep) {
11429                                                                                 if (iix % step == 0 && iiy % step == 0 && iiz % step == 0)
11430                                                                                         continue;
11431                                                                                 nn = n + (iix * ny + iiy) * nz + iiz;
11432                                                                                 if (mc->dp[nn] == DBL_MAX) {
11433                                                                                         p.x = mc->origin.x + mc->dx * (ix + iix);
11434                                                                                         p.y = mc->origin.y + mc->dy * (iy + iiy);
11435                                                                                         p.z = mc->origin.z + mc->dz * (iz + iiz);
11436                                                                                         mc->dp[nn] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
11437                                                                                 }
11438                                                                         }
11439                                                                 }
11440                                                         }
11441                                                 }
11442                                         }
11443                                 }
11444                         }
11445                 }
11446         }
11447         
11448 #endif
11449
11450         free(tmp);
11451         
11452         /*  Calculate vertex positions and normal vectors  */
11453         for (sn = 0; sn <= 1; sn++) {
11454                 n = 0;
11455                 thres = mc->thres * (sn == 0 ? 1 : -1);
11456                 VecZero(p);
11457                 for (ix = 0; ix < nx - 1; ix++) {
11458                         for (iy = 0; iy < ny - 1; iy++) {
11459                                 for (iz = 0; iz < nz - 1; iz++) {
11460                                         Double dd0, dd1;
11461                                         nn = (ix * ny + iy) * nz + iz;
11462                                         dd0 = mc->dp[nn];
11463                                         if (dd0 == DBL_MAX)
11464                                                 continue;
11465                                         if (0) {
11466                                                 dd1 = mc->dp[nn + ny * nz];
11467                                                 if (dd1 != DBL_MAX)
11468                                                         p.x = (dd1 - dd0) / mc->dx;
11469                                                 else if (ix > 0 && (dd1 = mc->dp[nn - ny * nz]) != DBL_MAX)
11470                                                         p.x = (dd0 - dd1) / mc->dx;
11471                                                 else continue;  /*  Cannot define gradient  */
11472                                                 dd1 = mc->dp[nn + nz];
11473                                                 if (dd1 != DBL_MAX)
11474                                                         p.y = (dd1 - dd0) / mc->dy;
11475                                                 else if (iy > 0 && (dd1 = mc->dp[nn - nz]) != DBL_MAX)
11476                                                         p.y = (dd0 - dd1) / mc->dy;
11477                                                 else continue;
11478                                                 dd1 = mc->dp[nn + 1];
11479                                                 if (dd1 != DBL_MAX)
11480                                                         p.z = (dd1 - dd0) / mc->dz;
11481                                                 else if (iz > 0 && (dd1 = mc->dp[nn - 1]) != DBL_MAX)
11482                                                         p.z = (dd0 - dd1) / mc->dz;
11483                                                 else continue;
11484                                                 NormalizeVec(&p, &p);
11485                                         }
11486                                         if (n + 3 >= mc->c[sn].ncubepoints) {
11487                                                 /*  Expand cubepoints[] array  */
11488                                                 mc->c[sn].cubepoints = (MCubePoint *)realloc(mc->c[sn].cubepoints, sizeof(MCubePoint) * (mc->c[sn].ncubepoints + 8192));
11489                                                 if (mc->c[sn].cubepoints == NULL) {
11490                                                         mc->c[sn].ncubepoints = 0;
11491                                                         retval = -3;
11492                                                         goto end;
11493                                                 }
11494                                                 mc->c[sn].ncubepoints += 8192;
11495                                         }
11496                                         mcp = mc->c[sn].cubepoints + n;
11497                                         iix = (dd0 >= thres ? 1 : -1);
11498                                         /*  (x, y, z)->(x + 1, y, z)  */
11499                                         dd1 = mc->dp[nn + ny * nz];
11500                                         if (dd1 != DBL_MAX) {
11501                                                 iiy = (dd1 >= thres ? 1 : -1);
11502                                                 if (iix != iiy) {
11503                                                         /*  Register  */
11504                                                         mcp->key = nn * 3;
11505                                                         mcp->d = (thres - dd0) / (dd1 - dd0);
11506                                                         mcp->pos[0] = mc->origin.x + mc->dx * (ix + mcp->d);
11507                                                         mcp->pos[1] = mc->origin.y + mc->dy * iy;
11508                                                         mcp->pos[2] = mc->origin.z + mc->dz * iz;
11509                                                         mcp->grad[0] = p.x;
11510                                                         mcp->grad[1] = p.y;
11511                                                         mcp->grad[2] = p.z;
11512                                                         mcp++;
11513                                                         n++;
11514                                                 }
11515                                         }
11516                                         /*  (x, y, z)->(x, y + 1, z)  */
11517                                         dd1 = mc->dp[nn + nz];
11518                                         if (dd1 != DBL_MAX) {
11519                                                 iiy = (dd1 >= thres ? 1 : -1);
11520                                                 if (iix != iiy) {
11521                                                         /*  Register  */
11522                                                         mcp->key = nn * 3 + 1;
11523                                                         mcp->d = (thres - dd0) / (dd1 - dd0);
11524                                                         mcp->pos[0] = mc->origin.x + mc->dx * ix;
11525                                                         mcp->pos[1] = mc->origin.y + mc->dy * (iy + mcp->d);
11526                                                         mcp->pos[2] = mc->origin.z + mc->dz * iz;
11527                                                         mcp->grad[0] = p.x;
11528                                                         mcp->grad[1] = p.y;
11529                                                         mcp->grad[2] = p.z;
11530                                                         mcp++;
11531                                                         n++;
11532                                                 }
11533                                         }
11534                                         /*  (x, y, z)->(x, y, z + 1)  */
11535                                         dd1 = mc->dp[nn + 1];
11536                                         if (dd1 != DBL_MAX) {
11537                                                 iiy = (dd1 >= thres ? 1 : -1);
11538                                                 if (iix != iiy) {
11539                                                         /*  Register  */
11540                                                         mcp->key = nn * 3 + 2;
11541                                                         mcp->d = (thres - dd0) / (dd1 - dd0);
11542                                                         mcp->pos[0] = mc->origin.x + mc->dx * ix;
11543                                                         mcp->pos[1] = mc->origin.y + mc->dy * iy;
11544                                                         mcp->pos[2] = mc->origin.z + mc->dz * (iz + mcp->d);
11545                                                         mcp->grad[0] = p.x;
11546                                                         mcp->grad[1] = p.y;
11547                                                         mcp->grad[2] = p.z;
11548                                                         mcp++;
11549                                                         n++;
11550                                                 }
11551                                         }
11552                                 }
11553                         }
11554                 }
11555                 if (n < mc->c[sn].ncubepoints)
11556                         mc->c[sn].cubepoints[n].key = -1;  /*  End mark  */
11557                 ncubepoints = n;
11558                 if (ncubepoints < 3) {
11559                         /*  Less than 3 points: no triangles  */
11560                         if (mc->c[sn].ntriangles > 0)
11561                                 mc->c[sn].triangles[0] = -1;  /*  End mark  */
11562                         retval = 0;
11563                         goto end;
11564                 }
11565                 
11566                 /*  Create triangle table  */
11567                 n = 0;
11568                 for (ix = 0; ix < nx - 1; ix++) {
11569                         for (iy = 0; iy < ny - 1; iy++) {
11570                                 for (iz = 0; iz < nz - 1; iz++) {
11571                                         nn = (ix * ny + iy) * nz + iz;
11572                                         iix = 0;
11573                                         if ((dd = mc->dp[nn]) == DBL_MAX)
11574                                                 continue;
11575                                         else if (dd >= thres)
11576                                                 iix |= 1;
11577                                         if ((dd = mc->dp[nn + ny * nz]) == DBL_MAX)
11578                                                 continue;
11579                                         else if (dd >= thres)
11580                                                 iix |= 2;
11581                                         if ((dd = mc->dp[nn + ny * nz + nz]) == DBL_MAX)
11582                                                 continue;
11583                                         else if (dd >= thres)
11584                                                 iix |= 4;
11585                                         if ((dd = mc->dp[nn + nz]) == DBL_MAX)
11586                                                 continue;
11587                                         else if (dd >= thres)
11588                                                 iix |= 8;
11589                                         if ((dd = mc->dp[nn + 1]) == DBL_MAX)
11590                                                 continue;
11591                                         else if (dd >= thres)
11592                                                 iix |= 16;
11593                                         if ((dd = mc->dp[nn + ny * nz + 1]) == DBL_MAX)
11594                                                 continue;
11595                                         else if (dd >= thres)
11596                                                 iix |= 32;
11597                                         if ((dd = mc->dp[nn + ny * nz + nz + 1]) == DBL_MAX)
11598                                                 continue;
11599                                         else if (dd >= thres)
11600                                                 iix |= 64;
11601                                         if ((dd = mc->dp[nn + nz + 1]) == DBL_MAX)
11602                                                 continue;
11603                                         else if (dd >= thres)
11604                                                 iix |= 128;
11605                                         for (iiy = 0; iiy < 15; iiy++) {
11606                                                 nn = sMarchingCubeTable[iix][iiy];
11607                                                 if (nn < 0)
11608                                                         break;
11609                                                 /*  key index for edges 0-11  */
11610                                                 switch (nn) {
11611                                                         case 0:  iiz = (( ix      * ny + iy    ) * nz + iz    ) * 3;     break;
11612                                                         case 1:  iiz = (((ix + 1) * ny + iy    ) * nz + iz    ) * 3 + 1; break;
11613                                                         case 2:  iiz = (( ix      * ny + iy + 1) * nz + iz    ) * 3;     break;
11614                                                         case 3:  iiz = (( ix      * ny + iy    ) * nz + iz    ) * 3 + 1; break;
11615                                                         case 4:  iiz = (( ix      * ny + iy    ) * nz + iz + 1) * 3;     break;
11616                                                         case 5:  iiz = (((ix + 1) * ny + iy    ) * nz + iz + 1) * 3 + 1; break;
11617                                                         case 6:  iiz = (( ix      * ny + iy + 1) * nz + iz + 1) * 3;     break;
11618                                                         case 7:  iiz = (( ix      * ny + iy    ) * nz + iz + 1) * 3 + 1; break;
11619                                                         case 8:  iiz = (( ix      * ny + iy    ) * nz + iz    ) * 3 + 2; break;
11620                                                         case 9:  iiz = (((ix + 1) * ny + iy    ) * nz + iz    ) * 3 + 2; break;
11621                                                         case 10: iiz = (((ix + 1) * ny + iy + 1) * nz + iz    ) * 3 + 2; break;
11622                                                         case 11: iiz = (( ix      * ny + iy + 1) * nz + iz    ) * 3 + 2; break;
11623                                                         default:
11624                                                                 /*  Skip this triangle  */
11625                                                                 iiy = (iiy - iiy % 3) + 2;
11626                                                                 n = n - n % 3;
11627                                                                 continue;
11628                                                 }
11629                                                 /*  Look for the key index in cubepoints  */
11630                                                 c1 = 0;
11631                                                 c3 = ncubepoints - 1;
11632                                                 mcp = mc->c[sn].cubepoints;
11633                                                 while (1) {
11634                                                         int w;
11635                                                         /*  c1 is always less than c3  */
11636                                                         if (c1 + 1 == c3) {
11637                                                                 /*  end of search  */
11638                                                                 if (mcp[c1].key == iiz) {
11639                                                                         c2 = c1;
11640                                                                 } else if (mcp[c3].key == iiz) {
11641                                                                         c2 = c3;
11642                                                                 } else {
11643                                                                         c2 = -1;
11644                                                                 }
11645                                                                 break;
11646                                                         }
11647                                                         c2 = (c1 + c3) / 2;
11648                                                         w = mcp[c2].key - iiz;
11649                                                         if (w == 0)
11650                                                                 break;
11651                                                         if (w < 0) {
11652                                                                 c1 = c2;
11653                                                         } else {
11654                                                                 c3 = c2;
11655                                                         }
11656                                                 }
11657                                                 if (c2 < 0) {
11658                                                         /*  Not found: skip this triangle  */
11659                                                         iiy = (iiy - iiy % 3) + 2;
11660                                                         n = n - n % 3;
11661                                                         continue;
11662                                                 }
11663                                                 if (n + 1 >= mc->c[sn].ntriangles) {
11664                                                         /*  Expand triangles[] array  */
11665                                                         mc->c[sn].triangles = (Int *)realloc(mc->c[sn].triangles, sizeof(Int) * (mc->c[sn].ntriangles + 8192));
11666                                                         if (mc->c[sn].triangles == NULL) {
11667                                                                 mc->c[sn].ntriangles = 0;
11668                                                                 retval = -4;
11669                                                                 goto end;
11670                                                         }
11671                                                         mc->c[sn].ntriangles += 8192;
11672                                                 }
11673                                                 mc->c[sn].triangles[n] = c2;
11674                                                 n++;
11675                                         }
11676                                 }
11677                         }
11678                 }
11679                 if (n < mc->c[sn].ntriangles)
11680                         mc->c[sn].triangles[n] = -1;  /*  End mark  */
11681                 
11682                 /*  Estimate the normal vector  */
11683                 for (n = 0, ip = mc->c[sn].triangles; ip[n] >= 0; n += 3) {
11684                         Vector v[3];
11685                         for (ix = 0; ix < 3; ix++) {
11686                                 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
11687                                 v[ix].x = mcp->pos[0];
11688                                 v[ix].y = mcp->pos[1];
11689                                 v[ix].z = mcp->pos[2];
11690                         }
11691                         VecDec(v[2], v[0]);
11692                         VecDec(v[1], v[0]);
11693                         VecCross(v[0], v[1], v[2]);
11694                         NormalizeVec(v, v);
11695                         for (ix = 0; ix < 3; ix++) {
11696                                 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
11697                                 mcp->grad[0] += v[0].x;
11698                                 mcp->grad[1] += v[0].y;
11699                                 mcp->grad[2] += v[0].z;
11700                         }
11701                 }
11702                 for (n = 0, mcp = mc->c[sn].cubepoints; mcp->key >= 0; mcp++) {
11703                         if (mcp->grad[0] != 0.0 || mcp->grad[1] != 0.0 || mcp->grad[2] != 0.0) {
11704                                 dd = 1.0 / sqrt(mcp->grad[0] * mcp->grad[0] + mcp->grad[1] * mcp->grad[1] + mcp->grad[2] * mcp->grad[2]);
11705                                 mcp->grad[0] *= dd;
11706                                 mcp->grad[1] *= dd;
11707                                 mcp->grad[2] *= dd;
11708                         }
11709                 }
11710         }
11711         retval = 0;
11712         MoleculeCallback_notifyModification(mol, 0);
11713 end:
11714         /*  For debug  */
11715         if (0) {
11716                 char *MyAppCallback_getDocumentHomeDir(void);
11717                 FILE *fp;
11718                 char *s;
11719                 Double dmax, dmin;
11720                 asprintf(&s, "%s/%s", MyAppCallback_getDocumentHomeDir(), "mcube_log.txt");
11721                 fp = fopen(s, "w");
11722                 dmax = -1e8;
11723                 dmin = 1e8;
11724                 for (n = 0; n < mc->nx * mc->ny * mc->nz; n++) {
11725                         if (mc->dp[n] == DBL_MAX)
11726                                 continue;
11727                         if (dmax < mc->dp[n])
11728                                 dmax = mc->dp[n];
11729                         if (dmin > mc->dp[n])
11730                                 dmin = mc->dp[n];
11731                 }
11732                 dmax = fabs(dmax);
11733                 dmin = fabs(dmin);
11734                 if (dmax < dmin)
11735                         dmax = dmin;
11736                 dmax = 1.001 * dmax;
11737                 fprintf(fp, "thres = %g = 100\n", mc->thres);
11738                 for (iz = 0; iz < mc->nz; iz++) {
11739                         fprintf(fp, "z = %d\n", iz);
11740                         for (iy = 0; iy < mc->ny; iy++) {
11741                                 for (ix = 0; ix < mc->nx; ix++) {
11742                                         n = (ix * ny + iy) * nz + iz;
11743                                         dd = mc->dp[n];
11744                                         if (dd == DBL_MAX)
11745                                                 fprintf(fp, " XXX ");
11746                                         else {
11747                                                 dd = dd * 100 / mc->thres;
11748                                                 if (dd > 999.0)
11749                                                         dd = 999.0;
11750                                                 else if (dd < -999.0)
11751                                                         dd = -999.0;
11752                                                 fprintf(fp, "%4d ", (int)(dd));
11753                                         }
11754                                 }
11755                                 fprintf(fp, "\n");
11756                         }
11757                         fprintf(fp, "\n");
11758                 }
11759                 
11760                 for (sn = 0; sn <= 1; sn++) {
11761                         for (n = 0; n < mc->c[sn].ncubepoints; n++) {
11762                                 MCubePoint *mcp = mc->c[sn].cubepoints + n;
11763                                 nn = mcp->key;
11764                                 if (nn == -1)
11765                                         break;
11766                                 iix = nn % 3;
11767                                 iz = nn / 3 % mc->nz;
11768                                 iy = nn / (3 * mc->nz) % mc->ny;
11769                                 ix = nn / (3 * mc->nz * mc->ny);
11770                                 fprintf(fp, "%c%d:[%d,%d,%d,%d] (%g,[%g,%g,%g],[%g,%g,%g])\n", (sn == 0 ? 'p' : 'P'),
11771                                                 n, ix, iy, iz, iix,
11772                                                 mcp->d, mcp->pos[0], mcp->pos[1], mcp->pos[2], mcp->grad[0], mcp->grad[1], mcp->grad[2]);
11773                         }
11774                         for (n = 0; n < mc->c[sn].ntriangles; n += 3) {
11775                                 if (mc->c[sn].triangles[n] < 0)
11776                                         break;
11777                                 fprintf(fp, "%c%d:(%d,%d,%d)\n", (sn == 0 ? 't' : 'T'), n / 3,
11778                                                 mc->c[sn].triangles[n], mc->c[sn].triangles[n + 1], mc->c[sn].triangles[n + 2]);
11779                         }
11780                 }
11781                 fclose(fp);
11782         }
11783         
11784         return retval;
11785 }