OSDN Git Service

create_surface is somewhat improved (looks like working)
[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, 1.2, &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, 1.2, &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, 1.2, &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         for (i = 1, ap = ATOM_NEXT(mp->atoms); i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5708                 Vector r = ap->r;
5709                 Int an = ap->atomicNumber;
5710                 Double rad;
5711                 if (an >= 0 && an < gCountElementParameters)
5712                         rad = gElementParameters[an].radius;
5713                 else rad = gElementParameters[6].radius;
5714                 MoleculeFindCloseAtoms(mp, &r, rad, limit, &nbonds, &bonds, -i);
5715         }
5716         if (nbonds > 0) {
5717                 newbond[0] = kInvalidIndex;
5718                 newbond[1] = 0;
5719                 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5720                 nbonds--;
5721         }
5722         if (outNbonds != NULL)
5723                 *outNbonds = nbonds;
5724         if (outBonds != NULL)
5725                 *outBonds = bonds;
5726         return 0;
5727 }
5728
5729 /*  Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information  */
5730 int
5731 MoleculeRebuildTablesFromConnects(Molecule *mp)
5732 {
5733         int i, j, k, retval;
5734         Atom *ap;
5735         Int ibuf[6], *cp;
5736         
5737         __MoleculeLock(mp);
5738
5739         /*  Find bonds   */
5740         if (mp->nbonds == 0) {
5741                 for (i = 0; i < mp->natoms; i++) {
5742                         ap = ATOM_AT_INDEX(mp->atoms, i);
5743                         cp = AtomConnectData(&ap->connect);
5744                         for (j = 0; j < ap->connect.count; j++) {
5745                                 k = cp[j];
5746                                 if (i >= k)
5747                                         continue;
5748                                 ibuf[0] = i;
5749                                 ibuf[1] = k;
5750                                 /*  MoleculeAddBonds() should not be used, because it assumes connects[] and
5751                                     bonds are already in sync  */
5752                                 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
5753                         /*      retval = MoleculeAddBonds(mp, 1, ibuf);
5754                                 if (retval != 0)
5755                                         goto abort; */
5756                         }
5757                 }
5758         }
5759         
5760         /*  Find angles  */
5761         if (mp->nangles == 0) {
5762                 for (i = 0; i < mp->natoms; i++) {
5763                         ap = ATOM_AT_INDEX(mp->atoms, i);
5764                         cp = AtomConnectData(&ap->connect);
5765                         for (j = 0; j < ap->connect.count; j++) {
5766                                 for (k = j + 1; k < ap->connect.count; k++) {
5767                                         ibuf[0] = cp[j];
5768                                         ibuf[1] = i;
5769                                         ibuf[2] = cp[k];
5770                                         ibuf[3] = -1;
5771                                         retval = MoleculeAddAngles(mp, ibuf, NULL);
5772                                         if (retval < 0)
5773                                                 goto abort;
5774                                 }
5775                         }
5776                 }
5777         }
5778         
5779         /*  Find dihedrals  */
5780         if (mp->ndihedrals == 0) {
5781                 for (i = 0; i < mp->natoms; i++) {
5782                         ap = ATOM_AT_INDEX(mp->atoms, i);
5783                         cp = AtomConnectData(&ap->connect);
5784                         for (j = 0; j < ap->connect.count; j++) {
5785                                 int jj, kk, mm, m;
5786                                 Atom *apjj;
5787                                 Int *cpjj;
5788                                 jj = cp[j];
5789                                 if (i >= jj)
5790                                         continue;
5791                                 apjj = ATOM_AT_INDEX(mp->atoms, jj);
5792                                 cpjj = AtomConnectData(&apjj->connect);
5793                                 for (k = 0; k < ap->connect.count; k++) {
5794                                         if (k == j)
5795                                                 continue;
5796                                         kk = cp[k];
5797                                         for (m = 0; m < apjj->connect.count; m++) {
5798                                                 mm = cpjj[m];
5799                                                 if (mm == i || mm == kk)
5800                                                         continue;
5801                                                 ibuf[0] = kk;
5802                                                 ibuf[1] = i;
5803                                                 ibuf[2] = jj;
5804                                                 ibuf[3] = mm;
5805                                                 ibuf[4] = -1;
5806                                                 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
5807                                                 if (retval < 0)
5808                                                         goto abort;
5809                                         }
5810                                 }
5811                         }
5812                 }
5813         }
5814         
5815         /*  Find impropers  */
5816         if (mp->nimpropers == 0) {
5817                 for (i = 0; i < mp->natoms; i++) {
5818                         int i1, i2, i4, n1, n2, n4;
5819                         ap = ATOM_AT_INDEX(mp->atoms, i);
5820                         cp = AtomConnectData(&ap->connect);
5821                         for (i1 = 0; i1 < ap->connect.count; i1++) {
5822                                 n1 = cp[i1];
5823                                 for (i2 = i1 + 1; i2 < ap->connect.count; i2++) {
5824                                         n2 = cp[i2];
5825                                         for (i4 = i2 + 1; i4 < ap->connect.count; i4++) {
5826                                                 n4 = cp[i4];
5827                                                 ibuf[0] = n1;
5828                                                 ibuf[1] = n2;
5829                                                 ibuf[2] = i;
5830                                                 ibuf[3] = n4;
5831                                                 ibuf[4] = -1;
5832                                                 retval = MoleculeAddImpropers(mp, ibuf, NULL);
5833                                                 if (retval < 0)
5834                                                         goto abort;
5835                                         }
5836                                 }
5837                         }
5838                 }
5839         }
5840
5841         mp->needsMDRebuild = 1;
5842         __MoleculeUnlock(mp);
5843         return 0;
5844
5845   abort:
5846         __MoleculeUnlock(mp);
5847         return retval;
5848 }
5849
5850 int
5851 MoleculeAreAtomsConnected(Molecule *mol, int idx1, int idx2)
5852 {
5853         Atom *ap1 = ATOM_AT_INDEX(mol->atoms, idx1);
5854         if (AtomConnectHasEntry(&ap1->connect, idx2))
5855                 return 1;
5856         else if (ap1->anchor != NULL && AtomConnectHasEntry(&(ap1->anchor->connect), idx2))
5857                 return 2;
5858         else return 0;
5859 }
5860
5861 #pragma mark ====== Atom names ======
5862
5863 /*  Look for the n1-th atom in resno-th residue (n1 is 0-based)  */
5864 int
5865 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
5866 {
5867         int i, j, lasti;
5868         Atom *ap;
5869         if (mp == NULL || mp->natoms == 0)
5870                 return -1;
5871         lasti = -1;
5872         for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5873                 if (ap->resSeq == resno) {
5874                         lasti = i;
5875                         if (j++ == n1)
5876                                 return i;
5877                 }
5878         }
5879         if (n1 == -1)
5880                 return lasti; /* max */
5881         return -1;
5882 }
5883
5884 int
5885 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
5886 {
5887     int n;
5888     char *p;
5889         n = strtol(s, &p, 0);
5890         if (p > s) {
5891                 while (isspace(*p))
5892                         p++;
5893                 if (*p == 0) {
5894                   resName[0] = 0;
5895                   *resSeq = -1;
5896                   atomName[0] = 0;
5897                   return n;
5898                 }
5899         }
5900
5901         if ((p = strchr(s, ':')) != NULL) {
5902                 /*  Residue is specified  */
5903                 char *pp;
5904                 if ((pp = strchr(s, '.')) != NULL && pp < p) {
5905                         /*  Residue number is also specified  */
5906                         char *ppp;
5907                         n = pp - s;
5908                         *resSeq = strtol(pp + 1, &ppp, 0);
5909                         if (ppp == pp + 1)
5910                                 return -2;  /*  Bad format  */
5911                         while (isspace(*ppp))
5912                                 ppp++;
5913                         if (ppp != p)
5914                                 return -2;  /*  Bad format  */
5915                 } else {
5916                         *resSeq = -1;
5917                         /*  Check whether the "residue name" is an integer  */
5918                         n = strtol(s, &pp, 0);
5919                         if (pp > s) {
5920                                 while (isspace(*pp))
5921                                         pp++;
5922                                 if (*pp == 0 || *pp == ':') {
5923                                         *resSeq = n;
5924                                         if (*resSeq < 0)
5925                                                 return -2;  /*  Bad format  */
5926                                 }
5927                         }
5928                         if (*resSeq >= 0)
5929                                 n = 0;
5930                         else
5931                                 n = p - s;
5932                 }
5933                 if (n >= sizeof(resName))
5934                         n = sizeof(resName) - 1;
5935                 strncpy(resName, s, n);
5936                 resName[n] = 0;
5937                 p++;
5938         } else {
5939                 resName[0] = 0;
5940                 *resSeq = -1;
5941                 p = (char *)s;
5942         }
5943         strncpy(atomName, p, 4);
5944         atomName[4] = 0;
5945         return 0;
5946 }
5947
5948 /*  Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer  */
5949 int
5950 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
5951 {
5952         char resName[6];
5953         int resSeq, n;
5954         char atomName[6];
5955         /*      char *p; */
5956
5957         n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
5958         if (atomName[0] == 0) {
5959           if (n >= mp->natoms)
5960             n = -1;  /* Out of range */
5961           return n;
5962         }
5963         for (n = 0; n < mp->natoms; n++) {
5964                 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
5965                 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
5966                         && (resSeq < 0 || ap->resSeq == resSeq)
5967                         && strncmp(atomName, ap->aname, 4) == 0) {
5968                         return n;
5969                 }
5970         }
5971         return -1;  /*  Not found  */
5972 }
5973
5974 void
5975 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
5976 {
5977         Atom *ap;
5978         int n;
5979         if (mp == NULL || index < 0 || index >= mp->natoms) {
5980                 buf[0] = 0;
5981                 return;
5982         }
5983         ap = mp->atoms + index;
5984         if (ap->resSeq != 0) {
5985                 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
5986                 buf += n;
5987                 bufsize -= n;
5988         }
5989         snprintf(buf, bufsize, "%.4s", ap->aname);
5990 }
5991
5992 #pragma mark ====== Selection ======
5993
5994 static void
5995 sMoleculeNotifyChangeSelection(Molecule *mp)
5996 {
5997         /*  TODO: Finer control of notification types may be necessary  */
5998         MoleculeCallback_notifyModification(mp, 0);
5999 }
6000
6001 void
6002 MoleculeSetSelection(Molecule *mp, IntGroup *select)
6003 {
6004         if (mp == NULL)
6005                 return;
6006         if (select != NULL)
6007                 IntGroupRetain(select);
6008         if (mp->selection != NULL)
6009                 IntGroupRelease(mp->selection);
6010         mp->selection = select;
6011         sMoleculeNotifyChangeSelection(mp);
6012 }
6013
6014 IntGroup *
6015 MoleculeGetSelection(Molecule *mp)
6016 {
6017         if (mp == NULL)
6018                 return NULL;
6019         else return mp->selection;
6020 }
6021
6022 void
6023 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
6024 {
6025         if (mp->selection == NULL)
6026                 mp->selection = IntGroupNew();
6027         if (!extending)
6028                 IntGroupClear(mp->selection);
6029         IntGroupAdd(mp->selection, n1, 1);
6030         sMoleculeNotifyChangeSelection(mp);
6031 }
6032
6033 void
6034 MoleculeUnselectAtom(Molecule *mp, int n1)
6035 {
6036         if (mp->selection != NULL)
6037                 IntGroupRemove(mp->selection, n1, 1);
6038         sMoleculeNotifyChangeSelection(mp);
6039 }
6040
6041 void
6042 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
6043 {
6044         if (mp->selection == NULL)
6045                 mp->selection = IntGroupNew();
6046         IntGroupReverse(mp->selection, n1, 1);
6047         sMoleculeNotifyChangeSelection(mp);
6048 }
6049
6050 int
6051 MoleculeIsAtomSelected(Molecule *mp, int n1)
6052 {
6053         if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
6054                 return 1;
6055         else return 0;
6056 }
6057
6058 int
6059 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
6060 {
6061         if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
6062                 return 1;
6063         else return 0;
6064 }
6065
6066 IntGroup *
6067 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
6068 {
6069         int status;
6070         IntGroup *remain, *ig1, *ig2;
6071         ig1 = ig2 = NULL;
6072         remain = IntGroupNewFromIntGroup(remove);
6073         if (remain == NULL)
6074                 status = -1;
6075         else
6076                 status = IntGroupReverse(remain, 0, mp->natoms);
6077         if (status == 0) {
6078                 ig1 = IntGroupNew();
6079                 if (ig1 == NULL)
6080                         status = -1;
6081                 else
6082                         status = IntGroupDifference(selection, remove, ig1);
6083         }
6084         if (status == 0) {
6085                 ig2 = IntGroupNew();
6086                 if (ig2 == NULL)
6087                         status = -1;
6088                 else
6089                         status = IntGroupDeconvolute(ig1, remain, ig2);
6090         }
6091         if (remain != NULL)
6092                 IntGroupRelease(remain);
6093         if (ig1 != NULL)
6094                 IntGroupRelease(ig1);
6095         if (status == 0)
6096                 return ig2;
6097         else {
6098                 if (ig2 != NULL)
6099                         IntGroupRelease(ig2);
6100                 return NULL;
6101         }
6102 }
6103
6104 #pragma mark ====== Atom Equivalence ======
6105
6106 struct sEqList {
6107         int i[2];
6108         struct sEqList *next;
6109         struct sEqList *link;
6110 };
6111
6112 static struct sEqList *sListBase = NULL;
6113 static struct sEqList *sListFree = NULL;
6114
6115 static struct sEqList *
6116 sAllocEqList(void)
6117 {
6118         struct sEqList *lp;
6119         if (sListFree != NULL) {
6120                 lp = sListFree;
6121                 sListFree = lp->next;
6122                 lp->i[0] = lp->i[1] = 0;
6123                 lp->next = NULL;
6124                 return lp;
6125         }
6126         lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
6127         lp->link = sListBase;
6128         sListBase = lp;
6129         return lp;
6130 }
6131
6132 static void
6133 sFreeEqList(struct sEqList *list)
6134 {
6135         list->next = sListFree;
6136         sListFree = list;
6137 }
6138
6139 static void
6140 sDeallocateEqLists(void)
6141 {
6142         struct sEqList *lp, *lp_link;
6143         for (lp = sListBase; lp != NULL; lp = lp_link) {
6144                 lp_link = lp->link;
6145                 free(lp);
6146         }
6147         sListBase = NULL;
6148         sListFree = NULL;
6149 }
6150
6151 static int
6152 sExistInEqList(int i, int idx, struct sEqList *list)
6153 {
6154         while (list != NULL) {
6155                 if (list->i[idx] == i)
6156                         return 1;
6157                 list = list->next;
6158         }
6159         return 0;
6160 }
6161
6162 static struct sEqList *
6163 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
6164 {
6165         Atom *api, *apj;
6166         struct sEqList *list1, *list2;
6167         Int ii, jj, ni, nj, *cpi, *cpj;
6168         api = ATOM_AT_INDEX(mol->atoms, i);
6169         apj = ATOM_AT_INDEX(mol->atoms, j);
6170         if (api->atomicNumber != apj->atomicNumber)
6171                 return NULL;
6172         list1 = sAllocEqList();
6173         if (list1 == NULL)
6174                 return NULL;
6175         list1->i[0] = i;
6176         list1->i[1] = j;
6177         list1->next = list;
6178         if (i == j || (db[i] != NULL && db[i] == db[j]))
6179                 return list1;
6180         cpi = AtomConnectData(&api->connect);
6181         cpj = AtomConnectData(&apj->connect);
6182         for (ni = 0; ni < api->connect.count; ni++) {
6183                 ii = cpi[ni];
6184                 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
6185                         continue;
6186                 if (sExistInEqList(ii, 0, list1))
6187                         continue;
6188                 list2 = NULL;
6189                 for (nj = 0; nj < apj->connect.count; nj++) {
6190                         jj = cpj[nj];
6191                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6192                                 continue;
6193                         if (sExistInEqList(jj, 1, list1))
6194                                 continue;
6195                         list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
6196                         if (list2 != NULL)
6197                                 break;
6198                 }
6199                 if (list2 == NULL) {
6200                         sFreeEqList(list1);
6201                         return NULL;    /*  No equivalent to ii  */
6202                 }
6203                 list1 = list2;      /*  ii is OK, try next  */
6204         }
6205         return list1;
6206 }
6207
6208 int
6209 sDBInclude(Int *ip, int i)
6210 {
6211         int j;
6212         if (ip == NULL)
6213                 return -1;
6214         for (j = ip[0] - 1; j >= 0; j--) {
6215                 if (ip[j] == i)
6216                         return j;
6217         }
6218         return -1;
6219 }
6220
6221 Int *
6222 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
6223 {
6224         Int **db;  /*  List of equivalents for each atom  */
6225         Int *ip, *result;
6226         Atom *api, *apj, *apk;
6227         Int *cpi, *cpj, *ibuf, nibuf;
6228         int i, j, k, ii, jj, kk;
6229         if (mol == NULL || mol->natoms == 0)
6230                 return NULL;
6231         db = (Int **)calloc(sizeof(Int *), mol->natoms);
6232         ibuf = NULL;
6233         nibuf = 0;
6234
6235         /*  Find the equivalent univalent atoms  */
6236         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6237                 if (api->connect.count < 2)
6238                         continue;
6239                 cpi = AtomConnectData(&api->connect);
6240                 for (j = 0; j < api->connect.count; j++) {
6241                         Int n;
6242                         n = 0;
6243                         jj = cpi[j];
6244                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6245                                 continue;
6246                         AssignArray(&ibuf, &nibuf, sizeof(Int), n, &jj);
6247                         n++;
6248                         apj = ATOM_AT_INDEX(mol->atoms, jj);
6249                         if (apj->connect.count != 1 || db[jj] != NULL)
6250                                 continue;
6251                         cpj = AtomConnectData(&apj->connect);
6252                         for (k = j + 1; k < api->connect.count; k++) {
6253                                 kk = cpj[k];
6254                                 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
6255                                         continue;
6256                                 apk = ATOM_AT_INDEX(mol->atoms, kk);
6257                                 if (apk->connect.count != 1 || db[kk] != NULL)
6258                                         continue;
6259                                 if (apj->atomicNumber == apk->atomicNumber) {
6260                                         AssignArray(&ibuf, &nibuf, sizeof(Int), n, &kk);
6261                                         n++;
6262                                 }
6263                         }
6264                         if (n > 1) {
6265                                 ip = (Int *)calloc(sizeof(Int), n + 1);
6266                                 if (ip == NULL)
6267                                         return NULL;
6268                                 ip[0] = n;
6269                                 memmove(ip + 1, ibuf, sizeof(Int) * n);
6270                                 for (k = 0; k < n; k++)
6271                                         db[ip[k + 1]] = ip;
6272                         }
6273                 }
6274         }
6275         if (ibuf != NULL) {
6276                 free(ibuf);
6277                 ibuf = NULL;
6278         }
6279         
6280         /*  Try matching (i,j) pair  */
6281         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6282                 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
6283                         continue;
6284                 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
6285                         struct sEqList *list;
6286                         if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
6287                                 continue;
6288                         if (api->atomicNumber != apj->atomicNumber)
6289                                 continue;  /*  Different elements do not match  */
6290                         if (db[i] != NULL && db[i] == db[j])
6291                                 continue;  /*  Already equivalent  */
6292                         list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
6293                         if (list == NULL)
6294                                 continue;  /*  (i,j) do not match  */
6295                         while (list != NULL) {
6296                                 ii = list->i[0];
6297                                 jj = list->i[1];
6298                                 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
6299                                         /*  Merge db[ii] and db[jj]  */
6300                                         k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
6301                                         ip = (Int *)calloc(sizeof(Int), k + 1);
6302                                         if (ip == NULL)
6303                                                 return NULL;  /*  Out of memory  */
6304                                         if (db[ii] == NULL) {
6305                                                 ip[1] = ii;
6306                                                 k = 2;
6307                                         } else {
6308                                                 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
6309                                                 k = db[ii][0] + 1;
6310                                         }
6311                                         if (db[jj] == NULL) {
6312                                                 ip[k++] = jj;
6313                                         } else {
6314                                                 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
6315                                                 k += db[jj][0];
6316                                         }
6317                                         ip[0] = k - 1;
6318                                         /*  Free old ones  */
6319                                         if (db[ii] != NULL)
6320                                                 free(db[ii]);
6321                                         if (db[jj] != NULL)
6322                                                 free(db[jj]);
6323                                         for (k = 0; k < ip[0]; k++)
6324                                                 db[ip[k + 1]] = ip;
6325                                         if (0) {
6326                                                 /*  For debug  */
6327                                                 printf("(%d,%d) matched: ", ii, jj);
6328                                                 for (k = 0; k < ip[0]; k++) {
6329                                                         printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
6330                                                 }
6331                                                 printf("]\n");
6332                                         }
6333                                 }
6334                                 list = list->next;
6335                         }
6336                 }
6337         }
6338         
6339         /*  Record the equivalent atoms with the lowest index for each atom  */
6340         result = (Int *)calloc(sizeof(Int), mol->natoms);
6341         for (i = 0; i < mol->natoms; i++)
6342                 result[i] = -1;
6343         for (i = 0; i < mol->natoms; i++) {
6344                 if (result[i] >= 0 || (ip = db[i]) == NULL)
6345                         continue;
6346                 k = mol->natoms;
6347                 for (j = 0; j < ip[0]; j++) {
6348                         kk = ip[j + 1];
6349                         if (kk < k)
6350                                 k = kk;
6351                 }
6352                 for (j = 0; j < ip[0]; j++) {
6353                         result[ip[j + 1]] = k;
6354                         db[ip[j + 1]] = NULL;
6355                 }
6356                 free(ip);
6357         }
6358         sDeallocateEqLists();
6359         return result;
6360 }
6361
6362 #pragma mark ====== Symmetry expansion ======
6363
6364 int
6365 MoleculeGetTransformForSymop(Molecule *mp, Symop symop, Transform *tf, int is_cartesian)
6366 {
6367         Transform t;
6368         if (mp == NULL || mp->cell == NULL)
6369                 return -1;
6370         if (symop.sym >= mp->nsyms && symop.sym != 0)
6371                 return -2;
6372         memmove(*tf, SYMMETRY_AT_INDEX(mp->syms, symop.sym), sizeof(Transform));
6373         (*tf)[9] += symop.dx;
6374         (*tf)[10] += symop.dy;
6375         (*tf)[11] += symop.dz;
6376         if (is_cartesian) {
6377                 TransformMul(t, *tf, mp->cell->rtr);
6378                 TransformMul(*tf, mp->cell->tr, t);
6379         }
6380         return 0;
6381 }
6382
6383 int
6384 MoleculeGetSymopForTransform(Molecule *mp, const Transform tf, Symop *symop, int is_cartesian)
6385 {
6386         Transform t;
6387         int i, j, n[3];
6388         if (mp == NULL || mp->cell == NULL)
6389                 return -1;
6390         if (is_cartesian) {
6391                 TransformMul(t, tf, mp->cell->tr);
6392                 TransformMul(t, mp->cell->rtr, t);
6393         } else {
6394                 memmove(t, tf, sizeof(Transform));
6395         }
6396         for (i = 0; i < mp->nsyms || i == 0; i++) {
6397                 Transform *tp = &(SYMMETRY_AT_INDEX(mp->syms, i));
6398                 for (j = 0; j < 9; j++) {
6399                         if (fabs((*tp)[j] - t[j]) > 1e-4)
6400                                 break;
6401                 }
6402                 if (j == 9) {
6403                         for (j = 9; j < 12; j++) {
6404                                 double f1 = t[j] - (*tp)[j];
6405                                 double f2 = floor(f1 + 0.5);
6406                                 if (fabs(f1 - f2) > 1e-4)
6407                                         break;
6408                                 n[j - 9] = f2;
6409                         }
6410                         if (j == 12) {
6411                                 /*  Found  */
6412                                 symop->sym = i;
6413                                 symop->dx = n[0];
6414                                 symop->dy = n[1];
6415                                 symop->dz = n[2];
6416                                 symop->alive = (SYMOP_ALIVE((*symop)) != 0);
6417                                 return 0;
6418                         }
6419                 }
6420         }
6421         return -3;  /*  Not found  */
6422 }
6423
6424 int
6425 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
6426 {
6427         if (mp == NULL)
6428                 return 1;
6429         if (symop.sym >= mp->nsyms && symop.sym != 0)
6430                 return 2;
6431         if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
6432                 TransformVec(vpout, mp->cell->rtr, vpin);
6433                 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpout);
6434                 vpout->x += symop.dx;
6435                 vpout->y += symop.dy;
6436                 vpout->z += symop.dz;
6437                 TransformVec(vpout, mp->cell->tr, vpout);
6438         } else {
6439                 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpin);
6440                 vpout->x += symop.dx;
6441                 vpout->y += symop.dy;
6442                 vpout->z += symop.dz;
6443         }
6444         return 0;
6445 }
6446
6447 /*  Add expanded atoms. Returns the number of newly created atoms.
6448         If indices is non-NULL, it should be an array of Int with at least 
6449         IntGroupGetCount(group) entries, and on return it contains the
6450     indices of the expanded atoms (may be existing atoms if the expanded
6451     atoms are already present)
6452     If allowOverlap is non-zero, then the new atom is created even when the
6453     coordinates coincide with the some other atom (special position) of the
6454     same element; otherwise, such atom will not be created and the existing
6455     atom is returned in indices[].  */
6456 int
6457 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group, Int *indices, Int allowOverlap)
6458 {
6459         int i, n, n0, n1, n2, base, count, *table;
6460         Atom *ap;
6461         IntGroupIterator iter;
6462         Transform tr, t1;
6463         Symop symop1;
6464         Atom *ap2;
6465         Vector nr, dr;
6466         
6467         if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
6468                 return -1;
6469         if (symop.sym != 0 && symop.sym >= mp->nsyms)
6470                 return -2;
6471
6472         /*  Create atoms, with avoiding duplicates  */
6473         n0 = n1 = mp->natoms;
6474         table = (int *)malloc(sizeof(int) * n0);
6475         if (table == NULL)
6476                 return -3;
6477         for (i = 0; i < n0; i++)
6478                 table[i] = -1;
6479         IntGroupIteratorInit(group, &iter);
6480         MoleculeGetTransformForSymop(mp, symop, &tr, 0);
6481         __MoleculeLock(mp);
6482         for (i = 0; i < count; i++) {
6483                 n = IntGroupIteratorNext(&iter);
6484                 ap = ATOM_AT_INDEX(mp->atoms, n);
6485                 if (SYMOP_ALIVE(ap->symop)) {
6486                         /*  Calculate the cumulative symop  */
6487                         Transform tr2;
6488                         MoleculeGetTransformForSymop(mp, ap->symop, &t1, 0);
6489                         TransformMul(tr2, tr, t1);
6490                         if (MoleculeGetSymopForTransform(mp, tr2, &symop1, 0) != 0) {
6491                                 if (indices != NULL)
6492                                         indices[i] = -1;
6493                                 continue;  /*  Skip this atom  */
6494                         }
6495                         base = ap->symbase;
6496                 } else {
6497                         symop1 = symop;
6498                         base = n;
6499                 }
6500
6501                 /*  Calculate the expande position  */
6502                 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
6503                 
6504                 /*  Is this expansion already present?  */
6505                 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
6506                         /*  Symmetry operation and the base atom are the same  */
6507                         if (ap2->symbase == base && SYMOP_EQUAL(symop1, ap2->symop))
6508                                 break;
6509                         /*  Atomic number and the position are the same  */
6510                         if (ap2->atomicNumber == ap->atomicNumber && allowOverlap == 0) {
6511                                 VecSub(dr, ap2->r, nr);
6512                                 if (VecLength2(dr) < 1e-6)
6513                                         break;
6514                         }
6515                 }
6516                 if (n2 < n0) {
6517                         /*  If yes, then skip it  */
6518                         if (indices != NULL)
6519                                 indices[i] = n2;
6520                         continue;
6521                 } else {
6522                         /*  Create a new atom  */
6523                         Atom newAtom;
6524                         AtomDuplicate(&newAtom, ap);
6525                         MoleculeCreateAnAtom(mp, &newAtom, -1);
6526                         AtomClean(&newAtom);
6527                         ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
6528                         ap2->r = nr;
6529                         ap2->symbase = base;
6530                         ap2->symop = symop1;
6531                         ap2->symop.alive = (symop1.dx != 0 || symop1.dy != 0 || symop1.dz != 0 || symop1.sym != 0);
6532                         table[n] = n1;  /*  The index of the new atom  */
6533                         MoleculeSetAnisoBySymop(mp, n1);  /*  Recalculate anisotropic parameters according to symop  */
6534                         if (indices != NULL)
6535                                 indices[i] = n1;
6536                         n1++;
6537                 }
6538         }
6539         IntGroupIteratorRelease(&iter);
6540
6541         /*  Create bonds  */
6542         for (i = n0; i < n1; i++) {
6543                 Int b[2], j;
6544                 ap = ATOM_AT_INDEX(mp->atoms, i);
6545                 if (SYMOP_ALIVE(ap->symop) && MoleculeGetTransformForSymop(mp, ap->symop, &tr, 1) == 0) {
6546                         /*  For each connected atom, look for the transformed atom  */
6547                         Int *cp;
6548                         ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6549                         cp = AtomConnectData(&ap2->connect);
6550                         n2 = ap2->connect.count;
6551                         for (n = 0; n < n2; n++) {
6552                                 Atom *apn = ATOM_AT_INDEX(mp->atoms, cp[n]);
6553                                 nr = apn->r;
6554                                 TransformVec(&nr, tr, &nr);
6555                                 /*  Look for the bonded atom transformed by ap->symop  */
6556                                 for (j = 0, ap2 = mp->atoms; j < mp->natoms; j++, ap2 = ATOM_NEXT(ap2)) {
6557                                         if (ap2->symbase == cp[n] && SYMOP_EQUAL(ap->symop, ap2->symop))
6558                                                 break;
6559                                         VecSub(dr, nr, ap2->r);
6560                                         if (ap2->atomicNumber == apn->atomicNumber && VecLength2(dr) < 1e-6)
6561                                                 break;
6562                                 }
6563                                 if (j < mp->natoms) {
6564                                         /*  Bond i-j is created  */
6565                                         b[0] = i;
6566                                         b[1] = j;
6567                                         if (MoleculeLookupBond(mp, b[0], b[1]) < 0)
6568                                                 MoleculeAddBonds(mp, 1, b, NULL, 1);
6569                                 }
6570                         }
6571                 }
6572         }
6573         mp->needsMDRebuild = 1;
6574         __MoleculeUnlock(mp);
6575         free(table);
6576         return n1 - n0;  /*  The number of added atoms  */
6577 }
6578
6579 /*  Recalculate the coordinates of symmetry expanded atoms.
6580     (Also recalculate the positions of pi-anchor atoms)
6581         Returns the number of affected atoms.
6582     If group is non-NULL, only the expanded atoms whose base atoms are in the
6583     given group are considered.
6584         If groupout and vpout are non-NULL, the indices of the affected atoms
6585         and the original positions are returned (for undo operation).
6586         The pointers returned in *groupout and *vpout must be released and 
6587         free()'ed by the caller  */
6588 int
6589 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
6590 {
6591         int i, count;
6592         Atom *ap, *bp;
6593         Vector nr, dr;
6594         IntGroup *ig = NULL;
6595         Vector *vp = NULL;
6596         
6597         if (mp == NULL || mp->natoms == 0)
6598                 return 0;
6599
6600         __MoleculeLock(mp);
6601         count = 0;
6602         if (mp->nsyms != 0) {
6603                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6604                         if (!SYMOP_ALIVE(ap->symop))
6605                                 continue;
6606                         if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
6607                                 continue;
6608                         bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6609                         MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
6610                         VecSub(dr, nr, ap->r);
6611                         if (VecLength2(dr) < 1e-20)
6612                                 continue;
6613                         if (groupout != NULL) {
6614                                 if (ig == NULL) {
6615                                         ig = IntGroupNew();
6616                                         vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
6617                                 }
6618                                 vp[count] = ap->r;
6619                                 IntGroupAdd(ig, i, 1);
6620                         }
6621                         ap->r = nr;
6622                         count++;
6623                 }
6624         }
6625         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6626                 Int *ip, j, n;
6627                 if (ap->anchor == NULL)
6628                         continue;
6629                 if (group != NULL) {
6630                         if (IntGroupLookup(group, i, NULL) == 0) {
6631                                 n = ap->anchor->connect.count;
6632                                 ip = AtomConnectData(&(ap->anchor->connect));
6633                                 for (j = 0; j < n; j++) {
6634                                         if (IntGroupLookup(group, ip[j], NULL) != 0)
6635                                                 break;
6636                                 }
6637                                 if (j == n)
6638                                         continue;  /*  This pi-anchor should not be modified  */
6639                         }
6640                 }
6641                 nr = ap->r;
6642                 MoleculeCalculatePiAnchorPosition(mp, i);
6643                 VecSub(dr, nr, ap->r);
6644                 if (VecLength2(dr) < 1e-20) {
6645                         ap->r = nr;  /*  No change  */
6646                         continue;
6647                 }
6648                 if (groupout != NULL) {
6649                         if (ig == NULL) {
6650                                 ig = IntGroupNew();
6651                                 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
6652                         }
6653                         vp[count] = nr;
6654                         IntGroupAdd(ig, i, 1);
6655                 }
6656                 count++;
6657         }
6658         mp->needsMDCopyCoordinates = 1;
6659         __MoleculeUnlock(mp);
6660
6661         if (count > 0) {
6662                 if (groupout != NULL && vpout != NULL) {
6663                         *groupout = ig;
6664                         *vpout = (Vector *)realloc(vp, sizeof(Vector) * count);
6665                 } else {
6666                         IntGroupRelease(ig);
6667                         free(vp);
6668                 }
6669         } else {
6670                 if (groupout != NULL && vpout != NULL) {
6671                         *groupout = NULL;
6672                         *vpout = NULL;
6673                 }
6674         }
6675         return count;
6676 }
6677
6678 #pragma mark ====== Show/hide atoms ======
6679
6680 static void
6681 sMoleculeNotifyChangeAppearance(Molecule *mp)
6682 {
6683         /*  TODO: Finer control of notification types may be necessary  */
6684         MoleculeCallback_notifyModification(mp, 0);
6685 }
6686
6687
6688 static void
6689 sMoleculeUnselectHiddenAtoms(Molecule *mp)
6690 {
6691         int i;
6692         if (mp == NULL || mp->selection == NULL)
6693                 return;
6694         for (i = 0; i < mp->natoms; i++) {
6695                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6696                 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
6697                         IntGroupRemove(mp->selection, i, 1);
6698         }
6699         sMoleculeNotifyChangeAppearance(mp);
6700 }
6701
6702 int
6703 MoleculeShowAllAtoms(Molecule *mp)
6704 {
6705         int i;
6706         if (mp == NULL)
6707                 return 0;
6708         for (i = 0; i < mp->natoms; i++) {
6709                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6710                 ap->exflags &= ~kAtomHiddenFlag;
6711         }
6712         sMoleculeNotifyChangeAppearance(mp);
6713         return 1;
6714 }
6715
6716 int
6717 MoleculeShowReverse(Molecule *mp)
6718 {
6719         int i;
6720         if (mp == NULL)
6721                 return 0;
6722         for (i = 0; i < mp->natoms; i++) {
6723                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6724                 ap->exflags ^= kAtomHiddenFlag;
6725         }
6726         sMoleculeUnselectHiddenAtoms(mp);
6727         sMoleculeNotifyChangeAppearance(mp);
6728         return 1;
6729 }
6730
6731 int
6732 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
6733 {
6734         int i;
6735         if (mp == NULL || ig == NULL)
6736                 return 0;
6737         for (i = 0; i < mp->natoms; i++) {
6738                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6739                 if (ap->exflags & kAtomHiddenFlag)
6740                         continue;  /*  Already hidden  */
6741                 if (IntGroupLookupPoint(ig, i) >= 0)
6742                         ap->exflags |= kAtomHiddenFlag;
6743         }
6744         sMoleculeUnselectHiddenAtoms(mp);
6745         sMoleculeNotifyChangeAppearance(mp);
6746         return 1;
6747 }
6748
6749 #pragma mark ====== Reversible Editing ======
6750
6751 /*
6752 static void
6753 sMoleculeNotifyModification(Molecule *mp)
6754 {
6755         **  TODO: Finer control of notification types may be necessary  **
6756         MoleculeCallback_notifyModification(mp, 0);
6757 }
6758 */
6759
6760 /*  Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup  */
6761 int
6762 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
6763 {
6764         int n1, n2, n3, i;
6765         if (where == NULL) {
6766                 /*  Append the new objects at the end  */
6767                 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
6768                 return 0;
6769         }
6770         n1 = IntGroupGetCount(where);  /*  Position to get new object  */
6771         n2 = nobjs;                    /*  Position to get old object  */
6772         n3 = n1 + n2;                  /*  Position to place new/old object  */
6773         for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
6774                 int start = IntGroupGetStartPoint(where, i);
6775                 int end = IntGroupGetEndPoint(where, i);
6776                 if (end < n3) {
6777                         /*  old[end-(n3-n2)..n2-1] is moved to old[end..n3-1]  */
6778                         memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
6779                         n2 = end - (n3 - n2);
6780                         n3 = end;
6781                 }
6782                 /*  new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1]  */
6783                 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
6784                 n3 -= end - start;
6785                 n1 -= end - start;
6786         }
6787         return 0;
6788 }
6789
6790 /*  Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
6791 int
6792 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6793 {
6794         int n1, n2, n3, start, end, i;
6795         if (where == NULL || IntGroupGetCount(where) == 0)
6796                 return 0;  /*  No operation  */
6797         if (objs == NULL || nobjs == 0)
6798                 return 1;  /*  Bad argument  */
6799         n1 = 0;  /*  Position to move remaining elements to */
6800         n2 = 0;  /*  Position to move remaining elements from  */
6801         n3 = 0;  /*  Position to move removed elements to  */
6802         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6803                 end = IntGroupGetEndPoint(where, i);
6804                 if (n2 < start) {
6805                         /*  Move (start - n2) elements from objs[n2] to objs[n1]  */
6806                         if (n1 < n2)
6807                                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
6808                         n1 += start - n2;
6809                         n2 = start;
6810                 }
6811                 /*  Move (end - start) elements from objs[n2] to clip[n3]  */
6812                 if (clip != NULL)
6813                         memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
6814                 n3 += (end - start);
6815                 n2 += (end - start);
6816         }
6817         /*  Move (nobjs - n2) elements from objs[n2] to objs[n1]  */
6818         if (nobjs > n2)
6819                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
6820         return 0;
6821 }
6822
6823 /*  Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
6824 int
6825 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6826 {
6827         int n1, start, end, i;
6828         if (objs == NULL || where == NULL)
6829                 return 1;  /*  Bad argument  */
6830         n1 = 0;  /*  Position to move removed elements to  */
6831         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6832                 end = IntGroupGetEndPoint(where, i);
6833                 /*  Copy (end - start) elements from objs[start] to clip[n1]  */
6834                 if (clip != NULL)
6835                         memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
6836                 n1 += (end - start);
6837         }
6838         return 0;
6839 }
6840
6841 /*  Create a new atom with no bonding information. ap must _not_ be inside the given molecule
6842    (Use AtomDuplicate() first) */
6843 int
6844 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
6845 {
6846     Atom *ap1, *api;
6847         int i;
6848         if (mp == NULL || ap == NULL || mp->noModifyTopology)
6849                 return -1;
6850         __MoleculeLock(mp);
6851         if (pos < 0 || pos >= mp->natoms)
6852                 pos = mp->natoms;
6853         ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
6854         if (ap1 == NULL)
6855                 goto error;  /*  Out of memory  */
6856         ap1 = ATOM_AT_INDEX(mp->atoms, pos);
6857         if (pos < mp->natoms - 1) {
6858                 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6859         }
6860         if (AtomDuplicate(ap1, ap) == NULL) {
6861                 /*  Cannot duplicate: restore the original state  */
6862                 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6863                 mp->natoms--;
6864                 goto error;
6865         }
6866         ap1->connect.count = 0;
6867         if (ap1->resSeq >= mp->nresidues)
6868                 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
6869         if (ap1->resName[0] == 0)
6870           strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
6871         if (ap1->segName[0] == 0)
6872           strncpy(ap1->segName, "MAIN", 4);
6873         if (pos < mp->natoms - 1) {
6874                 /*  Renumber the connect table, bonds, angles, etc. */
6875                 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
6876                         int j;
6877                         Int *cp;
6878                         cp = AtomConnectData(&api->connect);
6879                         for (j = 0; j < api->connect.count; j++) {
6880                                 if (cp[j] >= pos)
6881                                         cp[j]++;
6882                         }
6883                         if (api->anchor != NULL) {
6884                                 cp = AtomConnectData(&api->anchor->connect);
6885                                 for (j = 0; j < api->anchor->connect.count; j++) {
6886                                         if (cp[j] >= pos)
6887                                                 cp[j]++;
6888                                 }
6889                         }
6890                 }
6891                 for (i = 0; i < mp->nbonds * 2; i++) {
6892                         if (mp->bonds[i] >= pos)
6893                                 mp->bonds[i]++;
6894                 }
6895                 for (i = 0; i < mp->nangles * 3; i++) {
6896                         if (mp->angles[i] >= pos)
6897                                 mp->angles[i]++;
6898                 }
6899                 for (i = 0; i < mp->ndihedrals * 4; i++) {
6900                         if (mp->dihedrals[i] >= pos)
6901                                 mp->dihedrals[i]++;
6902                 }
6903                 for (i = 0; i < mp->nimpropers * 4; i++) {
6904                         if (mp->impropers[i] >= pos)
6905                                 mp->impropers[i]++;
6906                 }
6907         }
6908         mp->nframes = -1;  /*  Should be recalculated later  */
6909         MoleculeIncrementModifyCount(mp);
6910         mp->needsMDRebuild = 1;
6911         __MoleculeUnlock(mp);
6912         return pos;
6913 error:
6914         __MoleculeUnlock(mp);
6915         return -1;
6916 }
6917
6918 #if defined(DEBUG)
6919
6920 static int s_error_count;
6921
6922 static int
6923 s_fprintf(FILE *fp, const char *fmt, ...)
6924 {
6925         va_list va;
6926         va_start(va, fmt);
6927         s_error_count++;
6928         return vfprintf(fp, fmt, va);
6929 }
6930
6931 int
6932 MoleculeCheckSanity(Molecule *mol)
6933 {
6934         const char *fail = "Sanity check failure";
6935         Int i, j, *ip, c[4];
6936         Atom *ap;
6937         s_error_count = 0;
6938         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
6939                 if (ap->resSeq >= mol->nresidues)
6940                         s_fprintf(stderr, "%s: atom %d residue %d but nresidues %d\n", fail, i, ap->resSeq, mol->nresidues);
6941                 if (ap->type != 0 && ap->type < kAtomTypeMinimum)
6942                         s_fprintf(stderr, "%s: atom %d atom type %d less than minimum\n", fail, i, ap->type);
6943                 if (ap->atomicNumber < 0 || ap->atomicNumber > 113)
6944                         s_fprintf(stderr, "%s: atom %d atomic number %d\n", fail, i, ap->atomicNumber);
6945                 ip = AtomConnectData(&ap->connect);
6946                 for (j = 0; j < ap->connect.count; j++) {
6947                         if (ip[j] < 0 || ip[j] >= mol->natoms)
6948                                 s_fprintf(stderr, "%s: atom %d connect[%d] = %d out of range\n", fail, i, j, ip[j]);
6949                         if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[j])->connect), i) == 0)
6950                                 s_fprintf(stderr, "%s: atom %d has connect %d but atom %d has no connect %d\n", fail, i, ip[j], ip[j], i);
6951                 }
6952         }
6953         for (i = 0, ip = mol->bonds; i < mol->nbonds; i++, ip += 2) {
6954                 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms)
6955                         s_fprintf(stderr, "%s: bond %d %d-%d out of range\n", fail, i, ip[0], ip[1]);
6956                 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[0])->connect), ip[1]) == 0)
6957                         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]);
6958         }
6959         for (i = 0, ip = mol->angles; i < mol->nangles; i++, ip += 3) {
6960                 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms)
6961                         s_fprintf(stderr, "%s: angle %d %d-%d-%d out of range\n", fail, i, ip[0], ip[1], ip[2]);
6962                 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
6963                 if (c[0] == 0)
6964                         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]);
6965                 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
6966                 if (c[1] == 0)
6967                         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]);
6968                 if (c[0] == 2 && c[1] == 2)
6969                         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]);
6970         }
6971         for (i = 0, ip = mol->dihedrals; i < mol->ndihedrals; i++, ip += 4) {
6972                 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)
6973                         s_fprintf(stderr, "%s: dihedral %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
6974                 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
6975                 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
6976                 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
6977                 if (c[0] == 0)
6978                         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]);
6979                 if (c[1] == 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[2]);
6981                 if (c[2] == 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[2], ip[3]);
6983         }
6984         for (i = 0, ip = mol->impropers; i < mol->nimpropers; i++, ip += 4) {
6985                 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)
6986                         s_fprintf(stderr, "%s: improper %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
6987                 c[0] = MoleculeAreAtomsConnected(mol, ip[2], ip[0]);
6988                 c[1] = MoleculeAreAtomsConnected(mol, ip[2], ip[1]);
6989                 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
6990                 if (c[0] == 0)
6991                         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]);
6992                 if (c[1] == 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[1]);
6994                 if (c[2] == 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[3]);
6996         }
6997         return s_error_count;
6998 }
6999 #endif
7000
7001 /*  Merge two molecules. We use this procedure for all add-atom operations.  */
7002 /*  resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
7003 /*  If nactions and actions are non-NULL, then the corresponding undo actions are created and returned. */
7004 /*  If forUndo is non-zero, then only the atoms are inserted; other information should be inserted
7005     separately by other undo actions.  */
7006 int
7007 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, Int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7008 {
7009         Int nsrc, ndst;
7010         Int i, j, n1, n2, n3, n4, *cp;
7011         Int *new2old, *old2new;
7012         IntGroup *ig;
7013         Atom *ap;
7014         MolAction *act;
7015         
7016         if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
7017                 return 0;  /*  Do nothing  */
7018
7019         if (dst->noModifyTopology)
7020                 return 1;  /*  Prohibited operation  */
7021
7022         if (where != NULL && IntGroupGetCount(where) != src->natoms)
7023                 return 1;  /*  Bad parameter  */
7024
7025         if (nactions != NULL)
7026                 *nactions = 0;
7027         if (actions != NULL)
7028                 *actions = NULL;
7029         act = NULL;
7030
7031         __MoleculeLock(dst);
7032
7033         nsrc = src->natoms;
7034         ndst = dst->natoms;
7035         if (resSeqOffset < 0)
7036                 resSeqOffset = 0;
7037
7038         /*  Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
7039             and ndst..ndst+nsrc-1 are for atoms in src.  */ 
7040         new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
7041         if (new2old == NULL)
7042                 goto panic;
7043         old2new = new2old + ndst + nsrc;
7044         n1 = 0;  /*  dst index  */
7045         n2 = 0;  /*  src index  */
7046         n3 = 0;  /*  "merged" index  */
7047         i = 0;
7048         while (n1 < ndst || n2 < nsrc) {
7049                 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
7050                         n4 = ndst - n1;
7051                 else n4 -= n3;
7052                 /*  n4 elements from dst[n1] will go to merged[n3]  */
7053                 for (j = 0; j < n4; j++) {
7054                         old2new[n1 + j] = n3 + j;
7055                         new2old[n3 + j] = n1 + j;
7056                 }
7057                 n3 += n4;
7058                 n1 += n4;
7059                 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
7060                         n4 = nsrc - n2;
7061                 /*  n4 elements from src[n2] will go to merged[n3]  */
7062                 for (j = 0; j < n4; j++) {
7063                         old2new[ndst + n2 + j] = n3 + j;
7064                         new2old[n3 + j] = ndst + n2 + j;
7065                 }
7066                 n3 += n4;
7067                 n2 += n4;
7068                 i++;
7069         }
7070
7071         /*  Expand the destination array  */
7072         if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
7073                 goto panic;
7074
7075         /*  Move the atoms  */
7076         if (where == NULL) {
7077                 /*  Duplicate atoms to the end of the destination array  */
7078                 for (i = 0; i < nsrc; i++) {
7079                         ap = ATOM_AT_INDEX(dst->atoms, ndst + i);
7080                         if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7081                                 goto panic;
7082                         if (forUndo)  /*  For undo action, all bonds come from another undo action, so connection info are cleared */
7083                                 AtomConnectResize(&ap->connect, 0);
7084                 }
7085         } else {
7086                 /*  Duplicate to a temporary storage and then insert  */
7087                 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
7088                 if (tempatoms == NULL)
7089                         goto panic;
7090                 for (i = 0; i < nsrc; i++) {
7091                         ap = ATOM_AT_INDEX(tempatoms, i);
7092                         if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7093                                 goto panic;
7094                         if (forUndo)  /*  See above  */
7095                                 AtomConnectResize(&ap->connect, 0);                             
7096                 }
7097                 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
7098                         goto panic;
7099                 free(tempatoms);
7100         }
7101         dst->natoms = ndst + nsrc;
7102
7103         /*  Renumber the atom indices in connect[] and symbase, and modify the residue numbers  */
7104         for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7105                 if (new2old[i] < ndst) {
7106                         /*  This atom is from dst  */
7107                         n1 = 0;
7108                 } else {
7109                         /*  This atom is from src  */
7110                         n1 = ndst;  /*  Offset to the internal number  */
7111                         if (ap->resSeq != 0)
7112                                 ap->resSeq += resSeqOffset;  /*  Modify residue number  */
7113                 }
7114                 cp = AtomConnectData(&ap->connect);
7115                 for (j = 0; j < ap->connect.count; j++)
7116                         cp[j] = old2new[cp[j] + n1];
7117                 if (SYMOP_ALIVE(ap->symop))
7118                         ap->symbase = old2new[ap->symbase + n1];
7119                 if (ap->anchor != NULL) {
7120                         cp = AtomConnectData(&ap->anchor->connect);
7121                         for (j = 0; j < ap->anchor->connect.count; j++)
7122                                 cp[j] = old2new[cp[j] + n1];
7123                 }
7124         }
7125         
7126         /*  Move the bonds, angles, dihedrals, impropers  */
7127         for (i = 0; i < 4; i++) {
7128                 Int *nitems, *nitems_src;
7129                 Int **items, **items_src;
7130                 Int nsize;  /*  Number of Ints in one element  */
7131                 switch (i) {
7132                         case 0:
7133                                 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
7134                         case 1:
7135                                 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
7136                         case 2:
7137                                 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
7138                         case 3:
7139                                 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
7140                 }
7141                 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
7142                 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
7143                 if (forUndo) {
7144                         /*  During undo, no bonds etc. are copied from src; they will be taken care later
7145                             by undo actions  */
7146                         n1 = *nitems;
7147                         n2 = 0;
7148                 } else {
7149                         /*  Keep the old number of entries in dst, because it is updated by AssignArray()  */
7150                         n1 = *nitems;
7151                         /*  Also keep the old number of entries in src, in case src and dst point the same molecule  */
7152                         n2 = *nitems_src;
7153                         /*  Expand the array  */
7154                         if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
7155                                 goto panic;
7156                         /*  Copy the items  */
7157                         memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
7158                         if (i == 0) {
7159                                 /*  Copy the bond order info if present */
7160                                 Int nn1 = dst->nbondOrders;
7161                                 if (dst->bondOrders != NULL || src->bondOrders != NULL) {
7162                                         if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), dst->nbonds - 1, NULL) == NULL)
7163                                                 goto panic;
7164                                         memset(dst->bondOrders + nn1, 0, sizeof(Double) * (dst->nbonds - nn1));
7165                                         if (src->bondOrders != NULL)
7166                                                 memmove(dst->bondOrders + n1, src->bondOrders, sizeof(Double) * n2);
7167                                 }
7168                         }
7169                 }
7170                 /*  Renumber  */
7171                 for (j = 0; j < n1 * nsize; j++)
7172                         (*items)[j] = old2new[(*items)[j]];
7173                 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
7174                         (*items)[j] = old2new[(*items)[j] + ndst];
7175                 if (forUndo == 0 && actions != NULL) {
7176                         ig = IntGroupNewWithPoints(n1, n2, -1);
7177                         switch (i) {
7178                                 case 0: act = MolActionNew(gMolActionDeleteBonds, ig); break;
7179                                 case 1: act = MolActionNew(gMolActionDeleteAngles, ig); break;
7180                                 case 2: act = MolActionNew(gMolActionDeleteDihedrals, ig); break;
7181                                 case 3: act = MolActionNew(gMolActionDeleteImpropers, ig); break;
7182                         }
7183                         IntGroupRelease(ig);
7184                         AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7185                         act = NULL;
7186                 }
7187         }
7188         
7189         /*  Renumber existing parameters  */
7190         if (dst->par != NULL) {
7191                 int type;
7192                 for (type = kFirstParType; type <= kLastParType; type++) {
7193                         UnionPar *up1;
7194                         n1 = ParameterGetCountForType(dst->par, type);
7195                         for (i = 0; i < n1; i++) {
7196                                 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7197                                 ParameterRenumberAtoms(type, up1, ndst, old2new);
7198                         }
7199                 }
7200         }
7201
7202         /*  Merge parameters from src  */
7203         if (src->par != NULL && forUndo == 0) {
7204                 UnionPar *up1, *up2;
7205                 int type;
7206                 if (dst->par == NULL)
7207                         dst->par = ParameterNew();
7208                 else {
7209                         /*  Renumber existing parameters  */
7210                         for (type = kFirstParType; type <= kLastParType; type++) {
7211                                 n1 = ParameterGetCountForType(dst->par, type);
7212                                 for (i = 0; i < n1; i++) {
7213                                         up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7214                                         ParameterRenumberAtoms(type, up1, ndst, old2new);
7215                                 }
7216                         }
7217                 }
7218                 ig = IntGroupNew();
7219                 for (type = kFirstParType; type <= kLastParType; type++) {
7220                         n1 = ParameterGetCountForType(src->par, type);
7221                         n2 = ParameterGetCountForType(dst->par, type);
7222                         if (n1 == 0)
7223                                 continue;
7224                         /*  Determine which parameter should be copied from src to dst  */
7225                         for (i = 0; i < n1; i++) {
7226                                 UInt types[4];
7227                                 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
7228                                 n3 = ParameterGetAtomTypes(type, up1, types);
7229                                 for (j = 0; j < n3; j++) {
7230                                         /*  If it includes explicit atom index, then it should be copied  */
7231                                         if (types[j] < kAtomTypeMinimum) {
7232                                                 IntGroupAdd(ig, i, 1);
7233                                                 break;
7234                                         }
7235                                 }
7236                                 if (j == n3) {
7237                                         for (j = 0; j < n2; j++) {
7238                                                 up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
7239                                                 if (ParameterCompare(up1, up2, type))
7240                                                         break;
7241                                         }
7242                                         if (j >= n2)
7243                                                 /*  This is an unknown parameter; should be copied  */
7244                                                 IntGroupAdd(ig, i, 1);
7245                                 }
7246                         }
7247                         n1 = IntGroupGetCount(ig);
7248                         if (n1 == 0)
7249                                 continue;
7250                         up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
7251                         if (up1 == NULL)
7252                                 goto panic;
7253                         /*  Copy parameters and renumber indices if necessary  */
7254                         for (i = j = 0; i < n1; i++) {
7255                                 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
7256                                 if (up2 == NULL)
7257                                         continue;
7258                                 up1[j] = *up2;
7259                                 ParameterRenumberAtoms(type, up1 + j, nsrc, old2new + ndst);
7260                                 j++;
7261                         }
7262                         /*  Merge parameters  */
7263                         IntGroupClear(ig);
7264                         IntGroupAdd(ig, n2, j);
7265                         if (ParameterInsert(dst->par, type, up1, ig) < j)
7266                                 goto panic;
7267                         if (actions != NULL) {
7268                                 act = MolActionNew(gMolActionDeleteParameters, type, ig);
7269                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7270                                 act = NULL;
7271                         }
7272                         IntGroupClear(ig);
7273                         free(up1);
7274                 }
7275                 IntGroupRelease(ig);
7276         }
7277         
7278         /*  Copy the residues if necessary  */
7279         /*  src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
7280             However, 1+resSeqOffset should not overwrite the existing residue in dst;
7281                 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1].  */
7282         if (forUndo == 0) {
7283                 n1 = dst->nresidues;
7284                 if (1 + resSeqOffset < n1) {
7285                         n2 = n1;
7286                 } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
7287                 if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
7288                         if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
7289                                 goto panic;
7290                         memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
7291                         if (nactions != NULL) {
7292                                 act = MolActionNew(gMolActionChangeNumberOfResidues, n1);
7293                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7294                                 act = NULL;
7295                         }
7296                 }
7297         }
7298
7299         MoleculeCleanUpResidueTable(dst);
7300         
7301         free(new2old);
7302         dst->nframes = -1;  /*  Should be recalculated later  */
7303
7304         MoleculeIncrementModifyCount(dst);
7305         dst->needsMDRebuild = 1;
7306         __MoleculeUnlock(dst);
7307         return 0;
7308
7309   panic:
7310         __MoleculeUnlock(dst);
7311     Panic("Low memory while adding atoms");
7312         return 1;  /*  Not reached  */
7313 }
7314
7315 /*  Unmerge the molecule. If necessary, the undo actions are stored in nactions/actions array.
7316     (The nactions/actions array must be initialized by the caller)  */
7317 static int
7318 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag, Int *nactions, MolAction ***actions, Int forUndo)
7319 {
7320         Int nsrc, ndst, nsrcnew;
7321         Int i, j, n1, n2, n3, n4, *cp;
7322         Int *new2old, *old2new;
7323         IntGroup *move_g, *del_g, *remain_g, *dst_par_g, *remove_par_g;
7324         Molecule *dst;
7325         Atom *ap, *dst_ap;
7326         UnionPar *up;
7327         MolAction *act;
7328
7329         if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
7330                 /*  Do nothing  */
7331                 if (dstp != NULL)
7332                         *dstp = NULL;
7333                 return 0;
7334         }
7335         
7336         if (src->noModifyTopology && moveFlag)
7337                 return 1;  /*  Prohibit editing  */
7338
7339         if ((ndst = IntGroupGetCount(where)) > src->natoms)
7340                 return 1;  /*  Bad parameter  */
7341
7342         __MoleculeLock(src);
7343         
7344         act = NULL;
7345         
7346         nsrc = src->natoms;
7347         nsrcnew = nsrc - ndst;
7348         if (resSeqOffset < 0)
7349                 resSeqOffset = 0;
7350
7351         /*  Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
7352             and nsrcnew..nsrc-1 are for atoms moved into dst.  */ 
7353         new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
7354         if (new2old == NULL)
7355                 goto panic;
7356         old2new = new2old + nsrc;
7357         n1 = 0;  /*  src index  */
7358         n2 = 0;  /*  dst index  */
7359         n3 = 0;  /*  src index after "unmerge"  */
7360         i = 0;
7361         while (n1 < nsrc || n2 < ndst) {
7362                 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
7363                         n4 = nsrc - n1;
7364                 else n4 -= n1;
7365                 /*  n4 elements from src[n1] will go to unmerged[n3]  */
7366                 for (j = 0; j < n4; j++) {
7367                         old2new[n1 + j] = n3 + j;
7368                         new2old[n3 + j] = n1 + j;
7369                 }
7370                 n3 += n4;
7371                 n1 += n4;
7372                 if ((n4 = IntGroupGetInterval(where, i)) < 0)
7373                         n4 = nsrc - n1;
7374                 /*  n4 elements from src[n1] will go to dst[n2]  */
7375                 for (j = 0; j < n4; j++) {
7376                         old2new[n1 + j] = nsrcnew + n2 + j;
7377                         new2old[nsrcnew + n2 + j] = n1 + j;
7378                 }
7379                 n1 += n4;
7380                 n2 += n4;
7381                 i++;
7382         }
7383
7384         /*  Atoms to remain in the source group  */
7385         if (moveFlag) {
7386                 remain_g = IntGroupNewWithPoints(0, nsrc, -1);
7387                 IntGroupRemoveIntGroup(remain_g, where);
7388         } else remain_g = NULL;
7389         
7390         /*  Find parameters to be moved to the dst (dst_par_g), and to be removed from the src (remove_par_g) */
7391         if (src->par != NULL) {
7392                 dst_par_g = IntGroupNew();
7393                 if (moveFlag)
7394                         remove_par_g = IntGroupNew();
7395                 else remove_par_g = NULL;
7396                 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7397                         n2 = ParameterGetCountForType(src->par, n1);
7398                         if (n2 == 0)
7399                                 continue;
7400                         for (i = 0; i < n2; i++) {
7401                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7402                                 if (ParameterIsRelevantToAtomGroup(n1, up, src->atoms, where)) {
7403                                         /*  This parameter is to be copied to dst  */
7404                                         IntGroupAdd(dst_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7405                                 }
7406                                 if (moveFlag && !ParameterIsRelevantToAtomGroup(n1, up, src->atoms, remain_g)) {
7407                                         /*  This parameter is to be removed  */
7408                                         IntGroupAdd(remove_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7409                                 }
7410                         }
7411                 }
7412         } else dst_par_g = remove_par_g = NULL;
7413         
7414         /*  Pi anchors should be modified if the anchor and its component atoms become separated between
7415             src anc dst  */
7416         if (moveFlag) {
7417                 Int ibufsize, *ibuf, flag_i, flag_j;
7418                 ibufsize = 8;
7419                 ibuf = (Int *)malloc(sizeof(Int) * ibufsize);
7420                 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7421                         if (ap->anchor == NULL)
7422                                 continue;
7423                         flag_i = (old2new[i] < nsrcnew);
7424                         cp = AtomConnectData(&ap->anchor->connect);
7425                         for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7426                                 flag_j = (old2new[cp[j]] < nsrcnew);
7427                                 if (flag_i == flag_j) {
7428                                         if (n1 >= ibufsize) {
7429                                                 ibufsize += 8;
7430                                                 ibuf = (Int *)realloc(ibuf, sizeof(Int) * ibufsize);
7431                                         }
7432                                         ibuf[n1++] = cp[j];
7433                                 }
7434                         }
7435                         if (n1 < j) {
7436                                 /*  Need to modify the pi anchor list  */
7437                                 if (n1 <= 1)
7438                                         n1 = 0;
7439                                 MolActionCreateAndPerform(src, SCRIPT_ACTION("isI"), "set_atom_attr", i, "anchor_list", n1, ibuf);
7440                         }
7441                 }
7442         }
7443         
7444         /*  Make a new molecule  */
7445         if (dstp != NULL) {
7446                 dst = MoleculeNew();
7447                 if (dst == NULL)
7448                         goto panic;
7449                 /*  Expand the destination array  */
7450                 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
7451                         goto panic;
7452                 dst_ap = dst->atoms;
7453         } else {
7454                 dst = NULL;
7455                 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
7456                 if (dst_ap == NULL)
7457                         goto panic;
7458         }
7459         
7460         /*  Move the atoms  */
7461         if (moveFlag) {
7462                 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
7463                         goto panic;
7464                 src->natoms = nsrcnew;
7465                 if (dst == NULL) {
7466                         /*  The atom record must be deallocated correctly  */
7467                         for (i = 0; i < ndst; i++)
7468                                 AtomClean(ATOM_AT_INDEX(dst_ap, i));
7469                 }
7470         } else {
7471                 if (dst != NULL) {
7472                         for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
7473                                 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
7474                 }
7475         }
7476         
7477         if (dst == NULL) {
7478                 /*  The dummy destination array is no longer needed  */
7479                 free(dst_ap);
7480                 dst_ap = NULL;
7481         }
7482         
7483         /*  Renumber the atom indices in connect[] (src) */
7484         if (moveFlag) {
7485                 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7486                         cp = AtomConnectData(&ap->connect);
7487                         for (j = n1 = 0; j < ap->connect.count; j++) {
7488                                 n2 = old2new[cp[j]];
7489                                 if (n2 < nsrcnew)
7490                                         cp[n1++] = n2;
7491                         }
7492                         AtomConnectResize(&ap->connect, n1);
7493                         if (ap->anchor != NULL) {
7494                                 cp = AtomConnectData(&ap->anchor->connect);
7495                                 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7496                                         n2 = old2new[cp[j]];
7497                                         if (n2 < nsrcnew)
7498                                                 cp[n1++] = n2;
7499                                 }
7500                                 if (n1 != ap->anchor->connect.count) {
7501                                         /*  This should not happen!!  */
7502                                         AtomConnectResize(&ap->anchor->connect, n1);
7503                                         fprintf(stderr, "Internal error in sMoleculeUnmergeSub (line %d)\n", __LINE__);
7504                                         if (n1 == 0) {
7505                                                 free(ap->anchor->coeffs);
7506                                                 free(ap->anchor);
7507                                                 ap->anchor = NULL;
7508                                         }
7509                                 }
7510                         }
7511                 }
7512         }
7513         
7514         /*  Renumber the atom indices in connect[] (dst)  */
7515         if (dst != NULL) {
7516                 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7517                         if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
7518                                 ap->resSeq -= resSeqOffset;
7519                         else ap->resSeq = 0;
7520                         cp = AtomConnectData(&ap->connect);
7521                         for (j = n1 = 0; j < ap->connect.count; j++) {
7522                                 n2 = old2new[cp[j]] - nsrcnew;
7523                                 if (n2 >= 0)
7524                                         cp[n1++] = n2;
7525                         }
7526                         AtomConnectResize(&ap->connect, n1);
7527                         if (ap->anchor != NULL) {
7528                                 cp = AtomConnectData(&ap->anchor->connect);
7529                                 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7530                                         n2 = old2new[cp[j]] - nsrcnew;
7531                                         if (n2 >= 0)
7532                                                 cp[n1++] = n2;
7533                                 }
7534                                 if (n1 != ap->anchor->connect.count) {
7535                                         /*  This can happen, and the anchor info is silently modified  */
7536                                         if (n1 <= 1) {
7537                                                 AtomConnectResize(&ap->anchor->connect, 0);
7538                                                 free(ap->anchor->coeffs);
7539                                                 free(ap->anchor);
7540                                                 ap->anchor = NULL;
7541                                         } else {
7542                                                 Double d;
7543                                                 AtomConnectResize(&ap->anchor->connect, n1);
7544                                                 d = 0.0;
7545                                                 for (j = 0; j < n1; j++)
7546                                                         d += ap->anchor->coeffs[j];
7547                                                 for (j = 0; j < n1; j++)
7548                                                         ap->anchor->coeffs[j] /= d;
7549                                                 MoleculeCalculatePiAnchorPosition(dst, i);
7550                                         }
7551                                 }
7552                         }
7553                 }
7554         }
7555
7556         /*  Separate the bonds, angles, dihedrals, impropers  */
7557         /*  TODO: Improper torsions should also be copied!  */
7558         move_g = IntGroupNew();
7559         if (move_g == NULL)
7560                 goto panic;
7561         for (i = 3; i >= 0; i--) {
7562                 Int *nitems, *nitems_dst;
7563                 Int **items, **items_dst;
7564                 Int nsize;  /*  Number of Ints in one element  */
7565                 unsigned char *counts;
7566                 del_g = IntGroupNew();
7567                 switch (i) {
7568                         case 0:
7569                                 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
7570                         case 1:
7571                                 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
7572                         case 2:
7573                                 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
7574                         case 3:
7575                                 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
7576                         default:
7577                                 nitems = NULL; items = NULL; nsize = 0; break;  /*  Not reached  */
7578                 }
7579                 if (dst != NULL) {
7580                         nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
7581                         items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
7582                 } else {
7583                         nitems_dst = NULL;
7584                         items_dst = NULL;
7585                 }
7586                 counts = (unsigned char *)calloc(1, *nitems);
7587                 /*  Find the entries that should be moved to dst  */
7588                 n2 = 0;
7589                 for (j = 0; j < *nitems * nsize; j++) {
7590                         n1 = old2new[(*items)[j]];
7591                         if (n1 >= nsrcnew)
7592                                 counts[j / nsize]++; /* Count the atom belonging to dst */ 
7593                 }
7594                 for (j = n2 = n3 = 0; j < *nitems; j++) {
7595                         if (counts[j] > 0) {
7596                                 /*  Remove from src  */
7597                                 n2++;
7598                                 if (IntGroupAdd(del_g, j, 1) != 0)
7599                                         goto panic;
7600                                 if (counts[j] == nsize) {
7601                                         /*  Move to dst  */
7602                                         n3++;
7603                                         if (IntGroupAdd(move_g, j, 1) != 0)
7604                                                 goto panic;
7605                                 }
7606                         }
7607                 }
7608                 if (n2 > 0) {
7609                         /*  Expand the destination array  */
7610                         if (items_dst != NULL && n3 > 0) {
7611                                 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
7612                                         goto panic;
7613                                 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
7614                                         goto panic;
7615                                 if (i == 0 && src->bondOrders != NULL) {
7616                                         if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), n3 - 1, NULL) == NULL)
7617                                                 goto panic;
7618                                         if (sCopyElementsFromArrayAtPositions(src->bondOrders, src->nbondOrders, dst->bondOrders, sizeof(Double), move_g) != 0)
7619                                                 goto panic;
7620                                 }
7621                         }
7622                         /*  Remove from src  */
7623                         if (moveFlag && forUndo == 0) {
7624                                 if (nactions != NULL) {
7625                                         Int k, *ip;
7626                                         Double *dp;
7627                                         ip = (Int *)malloc(sizeof(Int) * nsize * n2);
7628                                         for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
7629                                                 memmove(ip + j * nsize, *items + k * nsize, sizeof(Int) * nsize);
7630                                         if (i == 0 && src->bondOrders != NULL) {
7631                                                 dp = (Double *)malloc(sizeof(Double) * n2);
7632                                                 for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
7633                                                         dp[j] = src->bondOrders[k];
7634                                         } else dp = NULL;
7635                                         switch (i) {
7636                                                 case 0:
7637                                                         act = MolActionNew(gMolActionAddBondsForUndo, n2 * nsize, ip, del_g); break;
7638                                                 case 1:
7639                                                         act = MolActionNew(gMolActionAddAngles, n2 * nsize, ip, del_g); break;
7640                                                 case 2:
7641                                                         act = MolActionNew(gMolActionAddDihedrals, n2 * nsize, ip, del_g); break;
7642                                                 case 3:
7643                                                         act = MolActionNew(gMolActionAddImpropers, n2 * nsize, ip, del_g); break;
7644                                         }
7645                                         if (act != NULL) {
7646                                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7647                                                 act = NULL;
7648                                         }
7649                                         free(ip);
7650                                         if (dp != NULL) {
7651                                                 act = MolActionNew(gMolActionAssignBondOrders, n2, dp, del_g);
7652                                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7653                                                 act = NULL;
7654                                                 free(dp);
7655                                         }
7656                                 }
7657                                 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
7658                                         goto panic;
7659                                 (*nitems) -= n2;
7660                         }
7661                 }
7662                 /*  Renumber the entries  */
7663                 if (moveFlag) {
7664                         for (j = 0; j < *nitems * nsize; j++) {
7665                                 (*items)[j] = old2new[(*items)[j]];
7666                         }
7667                 }
7668                 if (items_dst != NULL) {
7669                         for (j = 0; j < *nitems_dst * nsize; j++) {
7670                                 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
7671                         }
7672                 }
7673                 free(counts);
7674                 IntGroupClear(move_g);
7675                 IntGroupRelease(del_g);
7676         }
7677         IntGroupRelease(move_g);
7678         
7679         /*  Copy the residues  */
7680         if (dst != NULL) {
7681                 /*  src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset)  */
7682                 n1 = src->nresidues - resSeqOffset;  /*  This will be dst->nresidues (if >0)  */
7683                 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
7684                         goto panic;
7685                 if (n1 > 1) {
7686                         memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
7687                 }
7688         }
7689
7690         /*  Copy the parameters to dst */
7691         if (dst != NULL && dst_par_g != NULL && (n2 = IntGroupGetCount(dst_par_g)) > 0) {
7692                 IntGroup *dst_new_g = IntGroupNew();
7693                 Int dst_par_count[kLastParType - kFirstParType + 1];
7694                 if (dst_new_g == NULL)
7695                         goto panic;
7696                 for (i = 0; i <= kLastParType - kFirstParType; i++)
7697                         dst_par_count[i] = 0;
7698                 up = (UnionPar *)calloc(sizeof(UnionPar), n2);
7699                 if (up == NULL)
7700                         goto panic;
7701                 if (ParameterCopy(src->par, kFirstParType, up, dst_par_g) < n2)
7702                         goto panic;
7703                 /*  Renumber the explicit atom indices  */
7704                 for (i = 0; i < nsrc; i++)
7705                         old2new[i] -= nsrcnew;  /*  new indices for atoms in dst; otherwise negative numbers  */
7706                 for (i = 0; i < n2; i++) {
7707                         /*  Renumber the indices, and count the number of parameters for each type  */
7708                         n1 = kFirstParType + IntGroupGetNthPoint(dst_par_g, i) / kParameterIndexOffset;
7709                         dst_par_count[n1 - kFirstParType]++;
7710                         ParameterRenumberAtoms(n1, up + i, nsrc, old2new);
7711                 }
7712                 for (i = 0; i < nsrc; i++)
7713                         old2new[i] += nsrcnew;
7714                 if (dst->par == NULL)
7715                         dst->par = ParameterNew();
7716                 for (i = 0; i <= kLastParType - kFirstParType; i++) {
7717                         if (dst_par_count[i] > 0)
7718                                 IntGroupAdd(dst_new_g, i * kParameterIndexOffset, dst_par_count[i]);
7719                 }
7720                 if (ParameterInsert(dst->par, kFirstParType, up, dst_new_g) < n2)
7721                         goto panic;
7722                 free(up);
7723                 IntGroupRelease(dst_new_g);
7724         }
7725         IntGroupRelease(dst_par_g);
7726
7727         /*  Remove the unused parameter. Note: the parameters that are in remove_par_g and not in 
7728             dst_par_g will disappear. To support undo, these parameters should be taken care separately.  */
7729         if (forUndo == 0 && remove_par_g != NULL && (n2 = IntGroupGetCount(remove_par_g)) > 0) {
7730                 UnionPar *up = (UnionPar *)malloc(sizeof(UnionPar) * n2);
7731                 ParameterDelete(src->par, kFirstParType, up, remove_par_g);
7732                 if (nactions != NULL) {
7733                         act = MolActionNew(gMolActionAddParameters, kFirstParType, remove_par_g, n2, up);
7734                         AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7735                         act = NULL;
7736                 }
7737                 free(up);
7738         }
7739         IntGroupRelease(remove_par_g);
7740         
7741         /*  Renumber the parameter records remaining in the src  */
7742         if (moveFlag) {
7743                 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7744                         n2 = ParameterGetCountForType(src->par, n1);
7745                         for (i = 0; i < n2; i++) {
7746                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7747                                 ParameterRenumberAtoms(n1, up, nsrc, old2new);
7748                         }
7749                 }
7750         }
7751
7752         /*  Clean up  */
7753         IntGroupRelease(remain_g);
7754         MoleculeCleanUpResidueTable(src);
7755         if (dst != NULL)
7756                 MoleculeCleanUpResidueTable(dst);
7757         free(new2old);
7758
7759         src->nframes = -1;  /*  Should be recalculated later  */
7760         if (dst != NULL)
7761                 dst->nframes = -1;  /*  Should be recalculated later  */
7762
7763         
7764         if (dstp != NULL)
7765                 *dstp = dst;
7766
7767         MoleculeIncrementModifyCount(src);
7768         src->needsMDRebuild = 1;
7769         __MoleculeUnlock(src);
7770         
7771         return 0;
7772
7773   panic:
7774         __MoleculeUnlock(src);
7775 /*    Panic("Low memory while removing atoms"); */
7776         return -1;
7777 }
7778
7779 /*  Separate molecule into two parts. The atoms specified by 'where' are moved
7780     from src to a new molecule, which is returned as *dstp. Dstp can be NULL, 
7781         in which case the moved atoms are discarded.  */
7782 int
7783 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7784 {
7785         return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1, nactions, actions, forUndo);
7786 }
7787
7788 /*  Extract atoms from a given molecule into two parts. The atoms specified by 
7789         'where' are copied from src to a new molecule, which is returned as *dstp.
7790     If dummyFlag is non-zero, then the atoms that are not included in the group 
7791         but are connected to any atoms in the group are converted to "dummy" atoms 
7792         (i.e. with element "Du" and names beginning with an underscore) and included 
7793         in the new molecule object.  */
7794 int
7795 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
7796 {
7797         int retval;
7798
7799         /*  Extract the fragment  */
7800         retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0, NULL, NULL, 0);
7801         if (retval != 0)
7802                 return retval;
7803
7804         if (dummyFlag) {
7805
7806                 /*  Search bonds crossing the molecule border  */
7807                 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
7808                 if (ig != NULL) {
7809                         IntGroupIterator iter;
7810                         Int i, idx;
7811                         idx = 1;
7812                         IntGroupIteratorInit(ig, &iter);
7813                         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7814                                 /*  The atoms at the border  */
7815                                 Int n1, n2, nn[3];
7816                                 Atom a, *ap;
7817                                 n1 = src->bonds[i*2];
7818                                 n2 = src->bonds[i*2+1];
7819                                 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
7820                                         int w = n1;
7821                                         n1 = n2;
7822                                         n2 = w;
7823                                         if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
7824                                                 continue;  /*  Actually this is an internal error  */
7825                                 }
7826                                 /*  n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule  */
7827                                 /*  Create a new dummy atom with the same segment/residue info with n1
7828                                     and the same position as n2  */
7829                                 ap = ATOM_AT_INDEX(src->atoms, n1);
7830                                 memset(&a, 0, gSizeOfAtomRecord);
7831                                 a.segSeq = ap->segSeq;
7832                                 memmove(a.segName, ap->segName, 4);
7833                                 a.resSeq = ap->resSeq;
7834                                 memmove(a.resName, ap->resName, 4);
7835                                 ElementToString(0, a.element);  /*  "Du"  */
7836                                 snprintf(a.aname, 4, "_%d", idx++);
7837                                 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
7838                                 /*  Add the dummy atom to the new molecule; nn[1] is the index
7839                                     of the new dummy atom in the new molecule  */
7840                                 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
7841                                 /*  Connect nn1 and nn2  */
7842                                 nn[2] = kInvalidIndex;
7843                                 MoleculeAddBonds(*dstp, 1, nn, NULL, 1);
7844                         }
7845                         IntGroupIteratorRelease(&iter);
7846                         IntGroupRelease(ig);
7847                 }
7848         }
7849         
7850         return 0;
7851 }
7852
7853 int
7854 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds, IntGroup *where, Int autoGenerate)
7855 {
7856         Int nangles, ndihedrals;
7857         Int *angles, *dihedrals;
7858         Int i, j, k, kk, n1, n2, cn1, cn2;
7859         Int *cp1, *cp2;
7860         Int temp[4];
7861         Atom *ap1, *ap2, *ap3;
7862         
7863         if (mp == NULL || bonds == NULL || nbonds <= 0)
7864                 return 0;
7865         if (mp->noModifyTopology)
7866                 return -4;  /*  Prohibited operation  */
7867
7868         /*  Note: Duplicates and validity are not checked (the caller must do that)  */
7869
7870         __MoleculeLock(mp);
7871
7872         n1 = mp->nbonds;
7873         if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, n1 + nbonds - 1, NULL) == NULL
7874                 || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nbonds, sizeof(Int) * 2, where) != 0) {
7875                 __MoleculeUnlock(mp);
7876                 return -4;  /*  Out of memory  */
7877         }
7878         if (mp->bondOrders != NULL) {
7879                 /*  Expand the bond order info (all new entries are zero)  */
7880                 Double *dp = (Double *)calloc(sizeof(Double), nbonds);
7881                 if (dp == NULL)
7882                         return -4;
7883                 if (AssignArray(&(mp->bondOrders), &(mp->nbondOrders), sizeof(Double), n1 + nbonds - 1, NULL) == NULL
7884                         || sInsertElementsToArrayAtPositions(mp->bondOrders, n1, dp, nbonds, sizeof(Double), where) != 0) {
7885                         __MoleculeUnlock(mp);
7886                         free(dp);
7887                         return -4;
7888                 }
7889                 free(dp);
7890         }
7891         
7892         angles = dihedrals = NULL;
7893         nangles = ndihedrals = 0;
7894         
7895         /*  Add connects[], and angles/dihedrals (if autoGenerate is true)  */
7896         for (i = 0; i < nbonds; i++) {
7897                 
7898                 /*  One entry at time  */
7899                 /*  (Otherwise, duplicate entries of angles and dihedrals result)  */
7900                 n1 = bonds[i * 2];
7901                 n2 = bonds[i * 2 + 1];
7902                 
7903                 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
7904                 AtomConnectInsertEntry(&ap1->connect, -1, n2);
7905                 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
7906                 AtomConnectInsertEntry(&ap2->connect, -1, n1);
7907         
7908                 /*  Add angles and dihedrals  */
7909                 if (autoGenerate) {
7910                         AtomConnect *ac1, *ac2;
7911                         if (ap1->anchor == NULL || ap2->anchor == NULL) {
7912                                 /*  N1-N2-{XY} or N2-N1-{XY} angles (X: connected atom, Y: constitute atom of pi-anchor)  */
7913                                 for (j = 0; j < 4; j++) {
7914                                         switch (j) {
7915                                                 case 0: temp[0] = n1; temp[1] = n2; ac1 = &ap2->connect; break;  /* N1-N2-X */
7916                                                 case 1: if (ap2->anchor == NULL) continue; else ac1 = &ap2->anchor->connect; break; /* N1-N2-Y */
7917                                                 case 2: temp[0] = n2; temp[1] = n1; ac1 = &ap1->connect; break;  /* N2-N1-X */
7918                                                 case 3: if (ap1->anchor == NULL) continue; else ac1 = &ap1->anchor->connect; break; /* N2-N1-Y */
7919                                         }
7920                                         cp1 = AtomConnectData(ac1);
7921                                         cn1 = ac1->count;
7922                                         for (k = 0; k < cn1; k++) {
7923                                                 temp[2] = cp1[k];
7924                                                 if (temp[2] == temp[0])
7925                                                         continue;
7926                                                 ap3 = ATOM_AT_INDEX(mp->atoms, temp[2]);
7927                                                 if (ap3->anchor != NULL) {
7928                                                         /*  Avoid X-anchor-anchor angle (anchor-X-anchor is allowed)  */
7929                                                         if ((j < 2 && ap2->anchor != NULL) || (j >= 2 && ap1->anchor != NULL))
7930                                                                 continue;
7931                                                 }
7932                                                 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7933                                                         goto panic;
7934                                                 /*  Dihedrals N1-N2-X-{XY} or N2-N1-X-{XY}  */
7935                                                 if (j == 1 || j == 3)
7936                                                         continue;
7937                                                 cp2 = AtomConnectData(&ap3->connect);
7938                                                 for (kk = 0; kk < ap3->connect.count; kk++) {
7939                                                         temp[3] = cp2[kk];
7940                                                         if (temp[3] == temp[0] || temp[3] == temp[1])
7941                                                                 continue;
7942                                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7943                                                                 goto panic;
7944                                                 }
7945                                                 if (ap3->anchor != NULL) {
7946                                                         /*  N1-N2-X-Y or N2-N1-X-Y  */
7947                                                         /*  for Y, only the first constitute atom is considered  */
7948                                                         cp2 = AtomConnectData(&ap3->anchor->connect);
7949                                                         temp[3] = cp2[0];
7950                                                         if (temp[3] == temp[0] || temp[3] == temp[1])
7951                                                                 continue;
7952                                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7953                                                                 goto panic;
7954                                                 }
7955                                         }
7956                                 }
7957                         }
7958                         /*  X-N1-N2-X dihedrals  */
7959                         /*  Y-N1-N2-anchor is allowed, but the force may be zero if the angle N1-N2-anchor is */
7960                         /*  close to 180 deg (e.g. in ferrocene, C-anchor-Fe-anchor dihedral should be k=0)  */
7961                         if (ap1->anchor == NULL) {
7962                                 ac1 = &ap1->connect;
7963                                 cn1 = ac1->count;
7964                         } else {
7965                                 ac1 = &ap1->anchor->connect;
7966                                 cn1 = 1;  /*  Only the first constitute atom of pi-anchor is considered  */
7967                         }
7968                         if (ap2->anchor == NULL) {
7969                                 ac2 = &ap2->connect;
7970                                 cn2 = ac2->count;
7971                         } else {
7972                                 ac2 = &ap2->anchor->connect;
7973                                 cn2 = 1;  /*  Only the first constitute atom of pi-anchor is considered  */
7974                         }
7975                         temp[1] = n1;
7976                         temp[2] = n2;
7977                         cp1 = AtomConnectData(ac1);
7978                         cp2 = AtomConnectData(ac2);
7979                         for (j = 0; j < cn1; j++) {
7980                                 temp[0] = cp1[j];
7981                                 if (temp[0] == temp[2])
7982                                         continue;
7983                                 for (k = 0; k < cn2; k++) {
7984                                         temp[3] = cp2[k];
7985                                         if (temp[3] == temp[0] || temp[3] == temp[1])
7986                                                 continue;
7987                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7988                                                 goto panic;
7989                                 }
7990                         }
7991                 }
7992         }
7993         
7994         if (angles != NULL) {
7995                 temp[0] = kInvalidIndex;
7996                 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7997                         goto panic;
7998                 MoleculeAddAngles(mp, angles, NULL);
7999                 free(angles);
8000         }
8001         if (dihedrals != NULL) {
8002                 temp[0] = kInvalidIndex;
8003                 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8004                         goto panic;
8005                 MoleculeAddDihedrals(mp, dihedrals, NULL);
8006                 free(dihedrals);
8007         }
8008
8009         MoleculeIncrementModifyCount(mp);
8010         mp->needsMDRebuild = 1;
8011         __MoleculeUnlock(mp);
8012
8013         return nbonds;
8014
8015   panic:
8016         __MoleculeUnlock(mp);
8017         Panic("Low memory while adding bonds");
8018         return -1;  /*  Not reached  */
8019 }
8020
8021 /*  Delete bonds  */
8022 /*  The deleted angles and dihedrals are stored in outRemoval.  */
8023 /*  (*outRemoval) is an array of integers, containing:
8024       [0..na*3-1]: the angle indices
8025       [na*3..na*3+nd*4-1]: the dihedral indices
8026           [na*3+nd*4..na*3+nd*4+ni*4-1]: the improper indices
8027     *outRemovedPos is an intgroup denoting the positions of the removed angles/dihedrals/impropers.
8028           the angle indices are included as they are,
8029       the dihedral indices are offset by ATOMS_MAX_NUMBER,
8030       the improper indices are offset by ATOMS_MAX_NUMBER*2.
8031     Note: the removed bond indices are not returned, because the caller should already know them.  */
8032 int
8033 MoleculeDeleteBonds(Molecule *mp, Int *bonds, IntGroup *where, Int **outRemoved, IntGroup **outRemovedPos)
8034 {
8035         Int i, j, n1, n2, nw;
8036         Int *ip, *jp, na, nd, ni;
8037         IntGroup *ag, *dg, *ig;
8038         Atom *ap;
8039         IntGroupIterator iter;
8040
8041         if (mp == NULL)
8042                 return 0;
8043         if (mp->noModifyTopology)
8044                 return -4;  /*  Prohibited operation  */
8045
8046         __MoleculeLock(mp);
8047
8048         /*  Update connects[]  */
8049         IntGroupIteratorInit(where, &iter);
8050         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8051                 n1 = mp->bonds[i * 2];
8052                 n2 = mp->bonds[i * 2 + 1];
8053                 ap = ATOM_AT_INDEX(mp->atoms, n1);
8054                 ip = AtomConnectData(&ap->connect);
8055                 for (j = 0; j < ap->connect.count; j++) {
8056                         if (ip[j] == n2) {
8057                                 AtomConnectDeleteEntry(&ap->connect, j);
8058                                 break;
8059                         }
8060                 }
8061                 ap = ATOM_AT_INDEX(mp->atoms, n2);
8062                 ip = AtomConnectData(&ap->connect);
8063                 for (j = 0; j < ap->connect.count; j++) {
8064                         if (ip[j] == n1) {
8065                                 AtomConnectDeleteEntry(&ap->connect, j);
8066                                 break;
8067                         }
8068                 }
8069         }
8070         
8071         /*  Remove bonds, angles, dihedrals, impropers  */
8072         ag = IntGroupNew();
8073         dg = ig = NULL;
8074         na = nd = ni = 0;
8075         
8076         nw = IntGroupGetCount(where);
8077         jp = (Int *)malloc(sizeof(Int) * nw * 2);
8078         j = 0;
8079         IntGroupIteratorReset(&iter);
8080         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8081                 jp[j++] = mp->bonds[i * 2];
8082                 jp[j++] = mp->bonds[i * 2 + 1];
8083         }
8084         IntGroupIteratorRelease(&iter);
8085
8086         for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8087                 for (j = 0; j < nw; j++) {
8088                         n1 = jp[j * 2];
8089                         n2 = jp[j * 2 + 1];
8090                         if ((ip[0] == n1 && ip[1] == n2)
8091                                 || (ip[1] == n1 && ip[0] == n2)
8092                                 || (ip[1] == n1 && ip[2] == n2)
8093                                 || (ip[2] == n1 && ip[1] == n2)) {
8094                                 if (IntGroupAdd(ag, i, 1) != 0)
8095                                         goto panic;
8096                                 na++;
8097                                 break;
8098                         }
8099                 }
8100         }
8101         for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8102                 for (j = 0; j < nw; j++) {
8103                         n1 = jp[j * 2];
8104                         n2 = jp[j * 2 + 1];
8105                         if ((ip[0] == n1 && ip[1] == n2)
8106                          || (ip[1] == n1 && ip[0] == n2)
8107                          || (ip[1] == n1 && ip[2] == n2)
8108                          || (ip[2] == n1 && ip[1] == n2)
8109                          || (ip[2] == n1 && ip[3] == n2)
8110                          || (ip[3] == n1 && ip[2] == n2)) {
8111                                 if (dg == NULL)
8112                                         dg = IntGroupNew();
8113                                 if (IntGroupAdd(dg, i, 1) != 0)
8114                                         goto panic;
8115                                 nd++;
8116                                 break;
8117                         }
8118                 }
8119         }
8120         for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8121                 for (j = 0; j < nw; j++) {
8122                         n1 = jp[j * 2];
8123                         n2 = jp[j * 2 + 1];
8124                         if ((ip[0] == n1 && ip[2] == n2)
8125                          || (ip[1] == n1 && ip[2] == n2)
8126                          || (ip[3] == n1 && ip[2] == n2)
8127                          || (ip[0] == n2 && ip[2] == n1)
8128                          || (ip[1] == n2 && ip[2] == n1)
8129                          || (ip[3] == n2 && ip[2] == n1)) {
8130                                 if (ig == NULL)
8131                                         ig = IntGroupNew();
8132                                 if (IntGroupAdd(ig, i, 1) != 0)
8133                                         goto panic;
8134                                 ni++;
8135                                 break;
8136                         }
8137                 }
8138         }
8139         free(jp);
8140         
8141         if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, bonds, sizeof(Int) * 2, where) != 0)
8142                 goto panic;
8143         mp->nbonds -= IntGroupGetCount(where);
8144         if (mp->nbonds == 0) {
8145                 free(mp->bonds);
8146                 mp->bonds = NULL;
8147         }
8148         if (mp->bondOrders != NULL) {
8149                 if (sRemoveElementsFromArrayAtPositions(mp->bondOrders, mp->nbondOrders, NULL, sizeof(Double), where) != 0)
8150                         goto panic;
8151                 mp->nbondOrders -= IntGroupGetCount(where);
8152                 if (mp->nbondOrders == 0) {
8153                         free(mp->bondOrders);
8154                         mp->bondOrders = NULL;
8155                 }
8156         }
8157         if (na == 0 && nd == 0 && ni == 0)
8158                 ip = NULL;
8159         else
8160                 ip = (Int *)malloc(sizeof(Int) * (na * 3 + nd * 4 + ni * 4));
8161         if (na > 0)
8162                 MoleculeDeleteAngles(mp, ip, ag);
8163         if (nd > 0)
8164                 MoleculeDeleteDihedrals(mp, ip + na * 3, dg);
8165         if (ni > 0)
8166                 MoleculeDeleteImpropers(mp, ip + na * 3 + nd * 4, ig);
8167         if (ip != NULL) {
8168                 IntGroupOffset(dg, ATOMS_MAX_NUMBER);
8169                 IntGroupOffset(ig, ATOMS_MAX_NUMBER * 2);
8170                 IntGroupAddIntGroup(ag, dg);
8171                 IntGroupAddIntGroup(ag, ig);
8172                 IntGroupRelease(dg);
8173                 IntGroupRelease(ig);
8174         }
8175
8176         if (IntGroupGetCount(ag) == 0) {
8177                 IntGroupRelease(ag);
8178                 ag = NULL;
8179         }
8180         
8181         *outRemoved = ip;
8182         *outRemovedPos = ag;
8183
8184         MoleculeIncrementModifyCount(mp);
8185         mp->needsMDRebuild = 1;
8186         __MoleculeUnlock(mp);
8187
8188         return na * 3 + nd * 4 + ni * 4;
8189
8190   panic:
8191         __MoleculeUnlock(mp);
8192         Panic("Low memory while removing bonds");
8193         return -1;  /*  Not reached  */
8194 }
8195
8196 int
8197 MoleculeAssignBondOrders(Molecule *mp, const Double *orders, IntGroup *where)
8198 {
8199         Int i, j;
8200         IntGroupIterator iter;
8201         if (mp == NULL || orders == NULL || mp->nbonds == 0)
8202                 return 0;
8203         if (mp->noModifyTopology)
8204                 return -4;  /*  Prohibited operation  */
8205         if (mp->bondOrders == NULL) {
8206                 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbonds - 1, NULL);
8207                 memset(mp->bondOrders, 0, sizeof(Double) * mp->nbondOrders);
8208         }
8209         IntGroupIteratorInit(where, &iter);
8210         j = 0;
8211         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8212                 if (i >= mp->nbondOrders)
8213                         break;
8214                 mp->bondOrders[i] = orders[j++];
8215         }
8216         IntGroupIteratorRelease(&iter);
8217         return 0;
8218 }
8219
8220 int
8221 MoleculeGetBondOrders(Molecule *mp, Double *outOrders, IntGroup *where)
8222 {
8223         Int i, j;
8224         IntGroupIterator iter;
8225         if (mp == NULL || mp->nbonds == 0)
8226                 return 0;
8227         if (mp->bondOrders == NULL) {
8228                 /*  Returns all zero  */
8229                 i = IntGroupGetCount(where);
8230                 for (j = 0; j < i; j++)
8231                         outOrders[j] = 0.0;
8232         } else {
8233                 IntGroupIteratorInit(where, &iter);
8234                 j = 0;
8235                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8236                         if (i < mp->nbondOrders)
8237                                 outOrders[j] = mp->bondOrders[i];
8238                         else outOrders[j] = 0.0;
8239                         j++;
8240                 }
8241         }
8242         return 0;
8243 }
8244
8245 int
8246 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
8247 {
8248         int n1, nc;
8249         if (mp == NULL || angles == NULL)
8250                 return 0;
8251         if (mp->noModifyTopology)
8252                 return -4;  /*  Prohibited operation  */
8253
8254         __MoleculeLock(mp);
8255         if (where != NULL)
8256                 nc = IntGroupGetCount(where);
8257         else {
8258                 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
8259                         ;
8260                 nc = n1;
8261         }
8262         if (nc > 0) {
8263                 n1 = mp->nangles;
8264                 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
8265                         || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
8266                         __MoleculeUnlock(mp);
8267                         Panic("Low memory while adding angles");
8268                 }
8269         }
8270         mp->needsMDRebuild = 1;
8271         __MoleculeUnlock(mp);
8272         return nc;
8273 }
8274
8275 int
8276 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
8277 {
8278         int nc;
8279         if (mp == NULL || where == NULL)
8280                 return 0;
8281         if (mp->noModifyTopology)
8282                 return -4;  /*  Prohibited operation  */
8283         __MoleculeLock(mp);
8284         if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
8285                 __MoleculeUnlock(mp);
8286                 Panic("Bad argument while deleting angles");
8287         }
8288         mp->nangles -= (nc = IntGroupGetCount(where));
8289         if (mp->nangles == 0) {
8290                 free(mp->angles);
8291                 mp->angles = NULL;
8292         }
8293         mp->needsMDRebuild = 1;
8294         __MoleculeUnlock(mp);
8295         return nc;
8296 }
8297
8298 int
8299 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
8300 {
8301         int n1, nc;
8302         if (mp == NULL || dihedrals == NULL)
8303                 return 0;
8304         if (mp->noModifyTopology)
8305                 return -4;  /*  Prohibited operation  */
8306         if (where != NULL)
8307                 nc = IntGroupGetCount(where);
8308         else {
8309                 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
8310                         ;
8311                 nc = n1;
8312         }
8313         if (nc <= 0)
8314                 return 0;
8315         n1 = mp->ndihedrals;
8316         __MoleculeLock(mp);
8317         if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8318         || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
8319                 __MoleculeUnlock(mp);
8320                 Panic("Low memory while adding dihedrals");
8321         }
8322         mp->needsMDRebuild = 1;
8323         __MoleculeUnlock(mp);
8324         return nc;
8325 }
8326
8327 int
8328 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
8329 {       
8330         int nc;
8331         if (mp == NULL || where == NULL)
8332                 return 0;
8333         if (mp->noModifyTopology)
8334                 return -4;  /*  Prohibited operation  */
8335         __MoleculeLock(mp);
8336         if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
8337                 __MoleculeUnlock(mp);
8338                 Panic("Internal error: bad argument while deleting dihedrals");
8339         }
8340         mp->ndihedrals -= (nc = IntGroupGetCount(where));
8341         if (mp->ndihedrals == 0) {
8342                 free(mp->dihedrals);
8343                 mp->dihedrals = NULL;
8344         }
8345         mp->needsMDRebuild = 1;
8346         __MoleculeUnlock(mp);
8347         return nc;
8348 }
8349
8350 int
8351 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
8352 {
8353         int n1, nc;
8354         if (mp == NULL || impropers == NULL)
8355                 return 0;
8356         if (mp->noModifyTopology)
8357                 return -4;  /*  Prohibited operation  */
8358         if (where != NULL)
8359                 nc = IntGroupGetCount(where);
8360         else {
8361                 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
8362                         ;
8363                 nc = n1;
8364         }
8365         if (nc <= 0)
8366                 return 0;
8367         n1 = mp->nimpropers;
8368         __MoleculeLock(mp);
8369         if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8370         || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
8371                 __MoleculeUnlock(mp);
8372                 Panic("Low memory while adding impropers");
8373         }
8374         mp->needsMDRebuild = 1;
8375         __MoleculeUnlock(mp);
8376         return nc;
8377 }
8378
8379 int
8380 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
8381 {
8382         int nc;
8383         if (mp == NULL || where == NULL)
8384                 return 0;
8385         if (mp->noModifyTopology)
8386                 return -4;  /*  Prohibited operation  */
8387         __MoleculeLock(mp);
8388         if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
8389                 __MoleculeUnlock(mp);
8390                 Panic("Internal error: bad argument while deleting impropers");
8391         }
8392         mp->nimpropers -= (nc = IntGroupGetCount(where));
8393         if (mp->impropers == NULL) {
8394                 free(mp->impropers);
8395                 mp->impropers = NULL;
8396         }
8397         __MoleculeUnlock(mp);
8398         return nc;
8399 }
8400
8401 int
8402 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
8403 {
8404         Int i, *ip;
8405         if (mp == NULL || mp->bonds == NULL)
8406                 return -1;
8407         for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
8408                 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
8409                         return i;
8410         }
8411         return -1;
8412 }
8413
8414 int
8415 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
8416 {
8417         Int i, *ip;
8418         if (mp == NULL || mp->angles == NULL)
8419                 return -1;
8420         for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8421                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
8422                         (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
8423                         return i;
8424         }
8425         return -1;
8426 }
8427
8428 int
8429 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8430 {
8431         Int i, *ip;
8432         if (mp == NULL || mp->dihedrals == NULL)
8433                 return -1;
8434         for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8435                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
8436                         (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
8437                         return i;
8438         }
8439         return -1;
8440 }
8441
8442 int
8443 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8444 {
8445         Int i, *ip;
8446         if (mp == NULL || mp->impropers == NULL)
8447                 return -1;
8448         for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8449                 if (n3 != ip[2])
8450                         continue;
8451                 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
8452                         (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
8453                         (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
8454                         return i;
8455         }
8456         return -1;
8457 }
8458
8459 /*  Remove the bond at bondIndex and create two dummy atoms instead.
8460     The dummy atoms are placed at the end of atoms[], and the residue
8461         numbers are the same as the root atoms (i.e. the atoms to which
8462         the dummy atoms are connected). The indices are returned in
8463         dummyIndices[0,1].  */
8464 int
8465 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
8466 {
8467         Int roots[3], newBonds[5];
8468         Vector dr;
8469         Atom *rootp[2];
8470         Atom na[2], *nap;
8471         int i, natoms;
8472         IntGroup *ig;
8473         if (mp == NULL || mp->noModifyTopology)
8474                 return 0;
8475         if (bondIndex < 0 || bondIndex >= mp->nbonds)
8476                 return -1;
8477         roots[0] = mp->bonds[bondIndex * 2];
8478         roots[1] = mp->bonds[bondIndex * 2 + 1];
8479         roots[2] = kInvalidIndex;
8480         rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
8481         rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
8482         VecSub(dr, rootp[0]->r, rootp[1]->r);
8483         for (i = 0; i < 2; i++) {
8484                 float w;
8485                 nap = &na[i];
8486                 memmove(nap, rootp[i], sizeof(na));
8487                 nap->aname[0] = '*';
8488                 strcpy(nap->element, "Du");
8489                 nap->type = 0;
8490                 nap->charge = nap->weight = 0.0;
8491                 nap->atomicNumber = 0;
8492                 nap->connect.count = 0;
8493                 w = (i == 0 ? 0.4 : -0.4);
8494                 VecScaleInc(nap->r, dr, w);
8495                 VecZero(nap->v);
8496                 VecZero(nap->f);
8497                 nap->intCharge = 0;
8498                 nap->exflags = 0;
8499         }
8500
8501         /*  Expand atoms array and append the dummy atoms at the end  */
8502         __MoleculeLock(mp);
8503         natoms = mp->natoms;
8504         if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
8505                 goto panic;
8506         memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
8507         dummyIndices[0] = natoms;
8508         dummyIndices[1] = natoms + 1;
8509
8510         /*  Remove the old bond and create new bonds  */
8511         ig = IntGroupNewWithPoints(bondIndex, 1, -1);
8512         if (ig == NULL)
8513                 goto panic;
8514         MoleculeDeleteBonds(mp, NULL, ig, NULL, NULL);
8515         IntGroupRelease(ig);
8516         newBonds[0] = roots[0];
8517         newBonds[1] = dummyIndices[0];
8518         newBonds[2] = roots[1];
8519         newBonds[3] = dummyIndices[1];
8520         newBonds[4] = kInvalidIndex;
8521         
8522         i = (MoleculeAddBonds(mp, 2, newBonds, NULL, 1) < 0 ? -1 : 0);
8523         mp->needsMDRebuild = 1;
8524         __MoleculeUnlock(mp);
8525         return i;
8526
8527 panic:
8528         __MoleculeUnlock(mp);
8529         Panic("Low memory during creating dummy atoms");
8530         return 1;
8531 }
8532
8533 /*  Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
8534     a bond between the two root atoms. The value bondIndex is used as a
8535         hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
8536         the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
8537         is ignored and the new bond is stored at the end of bonds[].  */
8538 int
8539 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
8540 {
8541         return 0;
8542 }
8543
8544 /*
8545 Int
8546 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
8547 {
8548         Int n1, *np1;
8549         if (mol == NULL || mol->noModifyTopology)
8550                 return -1;
8551         n1 = mol->nangles;
8552         np1 = mol->angles;
8553         mol->nangles = 0;
8554         mol->angles = NULL;
8555         if (nangles > 0) {
8556                 __MoleculeLock(mol);
8557                 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
8558                 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
8559                 mol->needsMDRebuild = 1;
8560                 __MoleculeUnlock(mol);
8561         }
8562         *outAngles = np1;
8563         return n1;
8564 }
8565                                                 
8566 Int
8567 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
8568 {
8569         Int n1, *np1;
8570         if (mol == NULL || mol->noModifyTopology)
8571                 return -1;
8572         n1 = mol->ndihedrals;
8573         np1 = mol->dihedrals;
8574         mol->ndihedrals = 0;
8575         mol->dihedrals = NULL;
8576         if (ndihedrals > 0) {
8577                 __MoleculeLock(mol);
8578                 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
8579                 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
8580                 mol->needsMDRebuild = 1;
8581                 __MoleculeUnlock(mol);
8582         }
8583         *outDihedrals = np1;
8584         return n1;
8585 }
8586
8587 Int
8588 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
8589 {
8590         Int n1, *np1;
8591         if (mol == NULL || mol->noModifyTopology)
8592                 return -1;
8593         n1 = mol->nimpropers;
8594         np1 = mol->impropers;
8595         mol->nimpropers = 0;
8596         mol->impropers = NULL;
8597         if (nimpropers > 0) {
8598                 __MoleculeLock(mol);
8599                 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
8600                 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
8601                 mol->needsMDRebuild = 1;
8602                 __MoleculeUnlock(mol);
8603         }
8604         *outImpropers = np1;
8605         return n1;
8606 }
8607 */
8608
8609 Int
8610 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
8611 {
8612         Int i, j, k, *ip;
8613         Atom *ap;
8614         Int nangles;
8615         Int *angles;
8616         
8617         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8618                 return 0;  /*  molecule is empty  */
8619         if (mol->noModifyTopology)
8620                 return -1;
8621         nangles = 0;
8622         angles = NULL;
8623         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
8624                 Int *cp = AtomConnectData(&ap->connect);
8625                 if (ap->anchor != NULL)
8626                         continue;
8627                 for (j = 0; j < ap->connect.count; j++) {
8628                         Int j0 = cp[j];
8629                         if (ATOM_AT_INDEX(mol->atoms, j0)->anchor != NULL)
8630                                 continue;
8631                         for (k = j + 1; k < ap->connect.count; k++) {
8632                                 Int k0 = cp[k];
8633                                 if (ATOM_AT_INDEX(mol->atoms, k0)->anchor != NULL)
8634                                         continue;
8635                                 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
8636                                         ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
8637                                         ip[0] = j0;
8638                                         ip[1] = i;
8639                                         ip[2] = k0;
8640                                 }
8641                         }
8642                 }
8643         }
8644         if (nangles > 0) {
8645                 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
8646                 ip[0] = -1;
8647                 nangles--;
8648         }
8649         if (outAngles != NULL)
8650                 *outAngles = angles;
8651         return nangles;
8652 }
8653
8654 Int
8655 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
8656 {
8657         Int n1, n2, n3, n4, *ip, *cp2, *cp3;
8658         Atom *ap2, *ap3;
8659         Int ndihedrals;
8660         Int *dihedrals;
8661         
8662         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8663                 return 0;  /*  molecule is empty  */
8664         ndihedrals = 0;
8665         dihedrals = NULL;
8666         for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
8667                 Int i1, i3, i4, *ip;
8668                 if (ap2->anchor != NULL)
8669                         continue;
8670                 cp2 = AtomConnectData(&ap2->connect);
8671                 for (i3 = 0; i3 < ap2->connect.count; i3++) {
8672                         n3 = cp2[i3];
8673                         if (n2 > n3)
8674                                 continue;
8675                         ap3 = ATOM_AT_INDEX(mol->atoms, n3);
8676                         if (ap3->anchor != NULL)
8677                                 continue;
8678                         cp3 = AtomConnectData(&ap3->connect);
8679                         for (i1 = 0; i1 < ap2->connect.count; i1++) {
8680                                 n1 = cp2[i1];
8681                                 if (n1 == n3)
8682                                         continue;
8683                                 if (ATOM_AT_INDEX(mol->atoms, n1)->anchor != NULL)
8684                                         continue;
8685                                 for (i4 = 0; i4 < ap3->connect.count; i4++) {
8686                                         n4 = cp3[i4];
8687                                         if (n2 == n4 || n1 == n4)
8688                                                 continue;
8689                                         if (ATOM_AT_INDEX(mol->atoms, n4)->anchor != NULL)
8690                                                 continue;
8691                                         if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
8692                                                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
8693                                                 ip[0] = n1;
8694                                                 ip[1] = n2;
8695                                                 ip[2] = n3;
8696                                                 ip[3] = n4;
8697                                         }
8698                                 }
8699                         }
8700                 }
8701         }
8702         if (ndihedrals > 0) {
8703                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
8704                 ip[0] = -1;
8705                 ndihedrals--;
8706         }
8707         if (outDihedrals != NULL)
8708                 *outDihedrals = dihedrals;
8709         return ndihedrals;
8710 }
8711
8712 Int
8713 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
8714 {
8715         Int n1, n2, n3, n4, t1, t2, t3, t4, *ip, *cp;
8716         Parameter *par = mol->par;
8717         Atom *ap, *ap3;
8718         Int nimpropers;
8719         Int *impropers;
8720         
8721         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8722                 return 0;  /*  molecule is empty  */
8723         if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
8724                 return 0;  /*  No improper parameters are defined  */
8725         nimpropers = 0;
8726         impropers = NULL;
8727         ap = mol->atoms;
8728         for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
8729                 Int i1, i2, i4, found, *ip;
8730                 t3 = ap3->type;
8731                 cp = AtomConnectData(&ap3->connect);
8732                 for (i1 = 0; i1 < ap3->connect.count; i1++) {
8733                         n1 = cp[i1];
8734                         t1 = ATOM_AT_INDEX(ap, n1)->type;
8735                         for (i2 = i1 + 1; i2 < ap3->connect.count; i2++) {
8736                                 n2 = cp[i2];
8737                                 t2 = ATOM_AT_INDEX(ap, n2)->type;
8738                                 for (i4 = i2 + 1; i4 < ap3->connect.count; i4++) {
8739                                         n4 = cp[i4];
8740                                         t4 = ATOM_AT_INDEX(ap, n4)->type;
8741                                         found = 0;
8742                                         if (ParameterLookupImproperPar(par, t1, t2, t3, t4, n1, n2, n3, n4, 0) != NULL)
8743                                                 found = 1;
8744                                         else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, -1, -1, -1, -1, 0) != NULL)
8745                                                 found = 1;
8746                                         if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
8747                                                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8748                                                 ip[0] = n1;
8749                                                 ip[1] = n2;
8750                                                 ip[2] = n3;
8751                                                 ip[3] = n4;
8752                                         }
8753                                 }
8754                         }
8755                 }
8756         }
8757         if (nimpropers > 0) {
8758                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8759                 ip[0] = -1;
8760                 nimpropers--;
8761         }
8762         if (outImpropers != NULL)
8763                 *outImpropers = impropers;
8764         return nimpropers;
8765 }
8766
8767 #pragma mark ====== Residues ======
8768
8769 void
8770 MoleculeCleanUpResidueTable(Molecule *mp)
8771 {
8772         int i, maxres;
8773         Atom *ap;
8774         if (mp == NULL || mp->natoms == 0)
8775                 return;
8776         maxres = 0;
8777         __MoleculeLock(mp);
8778         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8779                 if (ap->resSeq >= maxres)
8780                         maxres = ap->resSeq + 1;
8781                 if (ap->resSeq < mp->nresidues) {
8782                         if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
8783                                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8784                 } else {
8785                         AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
8786                 }
8787         }
8788         if (maxres < mp->nresidues)
8789                 mp->nresidues = maxres;
8790         __MoleculeUnlock(mp);
8791 }
8792
8793 /*  Change the number of residues. If nresidues is greater than the current value,
8794     then the array mp->residues is expanded with null names. If nresidues is smaller
8795         than the current value, mp->nresidues is set to the smallest possible value
8796         that is no smaller than nresidues and larger than any of the resSeq values.  */
8797 int
8798 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
8799 {
8800         int n;
8801         if (mp == NULL)
8802                 return 0;
8803         if (mp->nresidues == nresidues)
8804                 return nresidues;
8805         else if (mp->nresidues < nresidues) {
8806                 __MoleculeLock(mp);
8807                 n = mp->nresidues;
8808                 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
8809                 while (n < nresidues)
8810                         mp->residues[n++][0] = 0;
8811                 __MoleculeUnlock(mp);
8812                 return nresidues;
8813         } else {
8814                 int i;
8815                 Atom *ap;
8816                 n = nresidues;
8817                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8818                         if (ap->resSeq >= n)
8819                                 n = ap->resSeq + 1;
8820                 }
8821                 mp->nresidues = n;
8822                 return n;
8823         }
8824 }
8825
8826 int
8827 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
8828 {
8829         IntGroupIterator iter;
8830         int withArray, resSeq, maxSeq;
8831         int i, j;
8832         Atom *ap;
8833         
8834         /*  If LSB of resSeqs is 1, then a constant value is used for all specified atoms  */
8835         if (((int)resSeqs & 1) == 0) {
8836                 withArray = 1;
8837                 resSeq = 0;
8838         } else {
8839                 withArray = 0;
8840                 resSeq = ((int)resSeqs - 1) / 2;
8841         }
8842         
8843         IntGroupIteratorInit(group, &iter);
8844
8845         /*  Change resSeqs  */
8846         maxSeq = 0;
8847         j = 0;
8848         __MoleculeLock(mp);
8849         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8850                 ap = ATOM_AT_INDEX(mp->atoms, i);
8851                 if (withArray)
8852                         resSeq = resSeqs[j++];
8853                 if (resSeq > maxSeq)
8854                         maxSeq = resSeq;
8855                 ap->resSeq = resSeq;
8856         }
8857         __MoleculeUnlock(mp);
8858
8859         /*  Expand array if necessary  */
8860         if (maxSeq >= mp->nresidues)
8861                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8862
8863         /*  Synchronize resName and residues[]  */
8864         __MoleculeLock(mp);
8865         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8866                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8867                         continue;
8868                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8869         }
8870         IntGroupIteratorRelease(&iter);
8871         __MoleculeUnlock(mp);
8872         
8873         MoleculeIncrementModifyCount(mp);
8874         
8875         return 0;
8876 }
8877
8878 int
8879 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
8880 {
8881         return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(resSeq * 2 + 1));
8882 }
8883
8884 /*  Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
8885     specifies the mp->nresidues after modifying the residue numbers.
8886         If all atoms are modified, then the table of residue names is also shifted. Otherwise,
8887         the table of residue names is not touched. */
8888 int
8889 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
8890 {
8891         int i, maxSeq, nmodatoms;
8892         Atom *ap;
8893         IntGroupIterator iter;
8894         IntGroupIteratorInit(group, &iter);
8895         maxSeq = 0;
8896         if (nresidues < 0)
8897                 nresidues = mp->nresidues;
8898         nmodatoms = 0;
8899         __MoleculeLock(mp);
8900         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8901                 ap = ATOM_AT_INDEX(mp->atoms, i);
8902                 ap->resSeq += offset;
8903                 if (ap->resSeq < 0) {
8904                         /*  Bad argument; undo change and returns this index + 1  */
8905                         int bad_index = i;
8906                         ap->resSeq -= offset;
8907                         while ((i = IntGroupIteratorLast(&iter)) >= 0) {
8908                                 ap = ATOM_AT_INDEX(mp->atoms, i);
8909                                 ap->resSeq -= offset;
8910                         }
8911                         IntGroupIteratorRelease(&iter);
8912                         return bad_index + 1;
8913                 }
8914                 if (ap->resSeq > maxSeq)
8915                         maxSeq = ap->resSeq;
8916                 nmodatoms++;
8917         }
8918         if (maxSeq >= nresidues)
8919                 nresidues = maxSeq + 1;
8920         if (offset < 0 && nmodatoms == mp->natoms) {
8921                 /*  Shift the residue names downward  */
8922                 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
8923         }
8924         __MoleculeUnlock(mp);
8925         MoleculeChangeNumberOfResidues(mp, nresidues);
8926         if (offset > 0 && nmodatoms == mp->natoms) {
8927                 /*  Shift the residue names upward  */
8928                 __MoleculeLock(mp);
8929                 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
8930                 __MoleculeUnlock(mp);
8931         }
8932         IntGroupIteratorRelease(&iter);
8933
8934         MoleculeIncrementModifyCount(mp);
8935         
8936         return 0;
8937 }
8938
8939 /*  Change residue names for the specified residue numbers. Names is an array of
8940     chars containing argc*4 characters, and every 4 characters represent a
8941         residue name; characters '\x01'-'\x1f' are converted to '\0', which allow 
8942         names to be handled as a C string.  */
8943 int
8944 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
8945 {
8946         int i, maxSeq;
8947         Atom *ap;
8948         maxSeq = 0;
8949         for (i = 0; i < argc; i++) {
8950                 if (maxSeq < resSeqs[i])
8951                         maxSeq = resSeqs[i];
8952         }
8953         if (maxSeq >= mp->nresidues)
8954                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8955         __MoleculeLock(mp);
8956         for (i = 0; i < argc; i++) {
8957                 char *p = mp->residues[resSeqs[i]];
8958                 int j;
8959                 strncpy(p, names + i * 4, 4);
8960                 for (j = 0; j < 4; j++) {
8961                         if (p[j] >= 0 && p[j] < 0x20)
8962                                 p[j] = 0;
8963                 }
8964         }
8965         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8966                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8967         }
8968         __MoleculeUnlock(mp);
8969
8970         MoleculeIncrementModifyCount(mp);
8971         
8972         return 0;
8973 }
8974
8975 /*  Returns the maximum residue number actually used  */
8976 int
8977 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
8978 {
8979         int i, maxSeq;
8980         Atom *ap;
8981         maxSeq = -1;
8982         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8983                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8984                         continue;
8985                 if (ap->resSeq > maxSeq)
8986                         maxSeq = ap->resSeq;
8987         }
8988         return maxSeq;
8989 }
8990
8991 /*  Returns the minimum residue number actually used  */
8992 int
8993 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
8994 {
8995         int i, minSeq;
8996         Atom *ap;
8997         minSeq = ATOMS_MAX_NUMBER;
8998         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8999                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9000                         continue;
9001                 if (ap->resSeq < minSeq)
9002                         minSeq = ap->resSeq;
9003         }
9004         return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
9005 }
9006
9007 #pragma mark ====== Sort by Residues ======
9008
9009 static int
9010 sAtomSortComparator(const void *a, const void *b)
9011 {
9012         const Atom *ap, *bp;
9013         ap = *((const Atom **)a);
9014         bp = *((const Atom **)b);
9015         if (ap->resSeq == bp->resSeq) {
9016                 /*  Retain the original order (i.e. atom with larger pointer address is larger)  */
9017                 if (ap < bp)
9018                         return -1;
9019                 else if (ap > bp)
9020                         return 1;
9021                 else return 0;
9022         } else {
9023                 /*  Compare the residue sequence. However, residue sequence 0 is always larger.  */
9024                 if (ap->resSeq == 0)
9025                         return 1;
9026                 else if (bp->resSeq == 0)
9027                         return -1;
9028                 else if (ap->resSeq < bp->resSeq)
9029                         return -1;
9030                 else if (ap->resSeq > bp->resSeq)
9031                         return 1;
9032                 else return 0;
9033         }
9034 }
9035
9036 static void
9037 sMoleculeReorder(Molecule *mp)
9038 {
9039         int i, res, prevRes;
9040         Atom **apArray;
9041         Int *old2new;
9042         Atom *newAtoms;
9043         if (mp == NULL || mp->natoms <= 1)
9044                 return;
9045
9046         /*  Sort the atoms, bonds, etc. */
9047         apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
9048         old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9049         newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9050         if (apArray == NULL || old2new == NULL || newAtoms == NULL)
9051                 Panic("Low memory during reordering atoms");
9052         for (i = 0; i < mp->natoms; i++)
9053                 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
9054
9055         /*  Sort the atoms. Note: apArray is an array of "Pointer to Atom"  */
9056         qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
9057         
9058         /*  Make a table of 'which atom becomes which'  */
9059         for (i = 0; i < mp->natoms; i++) {
9060                 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
9061                 old2new[j] = i;
9062         }
9063         
9064         /*  Renumber the bonds, etc.  */
9065         for (i = 0; i < mp->nbonds * 2; i++) {
9066                 mp->bonds[i] = old2new[mp->bonds[i]];
9067         }
9068         for (i = 0; i < mp->nangles * 3; i++) {
9069                 mp->angles[i] = old2new[mp->angles[i]];
9070         }
9071         for (i = 0; i < mp->ndihedrals * 4; i++) {
9072                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9073         }
9074         for (i = 0; i < mp->nimpropers * 4; i++) {
9075                 mp->impropers[i] = old2new[mp->impropers[i]];
9076         }
9077         for (i = 0; i < mp->natoms; i++) {
9078                 Int *ip, j;
9079                 ip = AtomConnectData(&(apArray[i]->connect));
9080                 for (j = 0; j < apArray[i]->connect.count; j++, ip++)
9081                         *ip = old2new[*ip];
9082         }
9083         
9084         /*  Renumber the residues so that the residue numbers are contiguous  */
9085         res = prevRes = 0;
9086         for (i = 0; i < mp->natoms; i++) {
9087                 if (apArray[i]->resSeq == 0)
9088                         break;
9089                 if (apArray[i]->resSeq != prevRes) {
9090                         res++;
9091                         prevRes = apArray[i]->resSeq;
9092                         if (prevRes != res) {
9093                                 strncpy(mp->residues[res], mp->residues[prevRes], 4);
9094                         }
9095                 }
9096                 apArray[i]->resSeq = res;
9097         }
9098         mp->nresidues = res + 1;
9099
9100         /*  Sort the atoms and copy back to atoms[] */
9101         for (i = 0; i < mp->natoms; i++) {
9102                 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
9103         }
9104         memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
9105         
9106         /*  Free the locally allocated storage  */
9107         free(newAtoms);
9108         free(old2new);
9109         free(apArray);
9110 }
9111
9112 /*  Renumber atoms  */
9113 int
9114 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
9115 {
9116         Int *old2new, i, j, retval;
9117         Atom *saveAtoms;
9118         if (mp == NULL)
9119                 return 0;
9120         if (mp->noModifyTopology)
9121                 return -1;
9122         if (old2new_out != NULL)
9123                 old2new = old2new_out;
9124         else
9125                 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9126         saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9127         if (old2new == NULL || saveAtoms == NULL)
9128                 Panic("Low memory during reordering atoms");
9129         memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
9130         __MoleculeLock(mp);
9131         for (i = 0; i < mp->natoms; i++)
9132                 old2new[i] = -1;
9133         for (i = 0; i < isize && i < mp->natoms; i++) {
9134                 j = new2old[i];
9135                 if (j < 0 || j >= mp->natoms) {
9136                         retval = 1; /* Out of range */
9137                         goto end;
9138                 }
9139                 if (old2new[j] != -1) {
9140                         retval = 2;  /*  Duplicate entry  */
9141                         goto end;
9142                 }
9143                 old2new[j] = i;
9144         }
9145         if (i < mp->natoms) {
9146                 for (j = 0; j < mp->natoms; j++) {
9147                         if (old2new[j] != -1)
9148                                 continue;
9149                         old2new[j] = i++;
9150                 }
9151         }
9152         if (i != mp->natoms) {
9153                 retval = 3;  /*  Internal inconsistency  */
9154                 goto end;
9155         }
9156
9157         /*  Renumber the bonds, etc.  */
9158         for (i = 0; i < mp->nbonds * 2; i++) {
9159                 mp->bonds[i] = old2new[mp->bonds[i]];
9160         }
9161         for (i = 0; i < mp->nangles * 3; i++) {
9162                 mp->angles[i] = old2new[mp->angles[i]];
9163         }
9164         for (i = 0; i < mp->ndihedrals * 4; i++) {
9165                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9166         }
9167         for (i = 0; i < mp->nimpropers * 4; i++) {
9168                 mp->impropers[i] = old2new[mp->impropers[i]];
9169         }
9170         /*  Renumber the connection table and pi anchor table  */
9171         for (i = 0; i < mp->natoms; i++) {
9172                 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
9173                 Int *ip = AtomConnectData(&ap->connect);
9174                 for (j = 0; j < ap->connect.count; j++, ip++)
9175                         *ip = old2new[*ip];
9176                 if (ap->anchor != NULL) {
9177                         ip = AtomConnectData(&ap->anchor->connect);
9178                         for (j = 0; j < ap->anchor->connect.count; j++, ip++)
9179                                 *ip = old2new[*ip];
9180                 }
9181         }
9182         
9183         if (mp->par != NULL) {
9184                 /*  Renumber the parameters  */
9185                 int n;
9186                 for (j = kFirstParType; j <= kLastParType; j++) {
9187                         n = ParameterGetCountForType(mp->par, j);
9188                         for (i = 0; i < n; i++) {
9189                                 UnionPar *up = ParameterGetUnionParFromTypeAndIndex(mp->par, j, i);
9190                                 if (up != NULL)
9191                                         ParameterRenumberAtoms(j, up, mp->natoms, old2new);
9192                         }
9193                 }
9194         }
9195         
9196         /*  Renumber the atoms  */
9197         for (i = 0; i < mp->natoms; i++)
9198                 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
9199         retval = 0;
9200         
9201         MoleculeIncrementModifyCount(mp);
9202         mp->needsMDRebuild = 1;
9203
9204   end:
9205         __MoleculeUnlock(mp);
9206         free(saveAtoms);
9207         if (old2new_out == NULL)
9208                 free(old2new);
9209         return retval;
9210 }
9211
9212 #pragma mark ====== Coordinate Transform ======
9213
9214 void
9215 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
9216 {
9217         int i;
9218         Atom *ap;
9219         Symop new_symop;
9220         Transform rtr, symtr;
9221         if (mp == NULL || tr == NULL)
9222                 return;
9223         TransformInvert(rtr, tr);
9224         __MoleculeLock(mp);
9225         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9226                 if (group == NULL || IntGroupLookup(group, i, NULL) != 0) {
9227                         TransformVec(&ap->r, tr, &ap->r);
9228                         if (!SYMOP_ALIVE(ap->symop))
9229                                 continue;
9230                         /*  Transform symop  */
9231                         if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9232                                 continue;
9233                         TransformMul(symtr, tr, symtr);
9234                         if (group == NULL || IntGroupLookup(group, ap->symbase, NULL) != 0)
9235                                 TransformMul(symtr, symtr, rtr);
9236                 } else {
9237                         if (!SYMOP_ALIVE(ap->symop))
9238                                 continue;
9239                         /*  Transform symop if the base atom is transformed  */
9240                         if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
9241                                 continue;
9242                         if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9243                                 continue;
9244                         TransformMul(symtr, symtr, rtr);
9245                 }
9246                 if (MoleculeGetSymopForTransform(mp, symtr, &new_symop, 1) != 0)
9247                         continue;
9248                 ap->symop = new_symop;
9249         }
9250         mp->needsMDCopyCoordinates = 1;
9251         __MoleculeUnlock(mp);
9252         sMoleculeNotifyChangeAppearance(mp);
9253 }
9254
9255 /*
9256 void
9257 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
9258 {
9259         int i;
9260         Atom *ap;
9261         if (mp == NULL || tr == NULL)
9262                 return;
9263         __MoleculeLock(mp);
9264         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9265                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9266                         continue;
9267                 TransformVec(&ap->r, tr, &ap->r);
9268         }
9269         mp->needsMDCopyCoordinates = 1;
9270         __MoleculeUnlock(mp);
9271         sMoleculeNotifyChangeAppearance(mp);
9272 }
9273 */
9274
9275 void
9276 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
9277 {
9278         Transform tr;
9279         if (mp == NULL || vp == NULL)
9280                 return;
9281         memset(tr, 0, sizeof(tr));
9282         tr[0] = tr[4] = tr[8] = 1.0;
9283         tr[9] = vp->x;
9284         tr[10] = vp->y;
9285         tr[11] = vp->z;
9286         MoleculeTransform(mp, tr, group);
9287 }
9288
9289 void
9290 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
9291 {
9292         Transform tr;
9293         TransformForRotation(tr, axis, angle, center);
9294         MoleculeTransform(mp, tr, group);
9295 }
9296
9297 int
9298 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
9299 {
9300         int i;
9301         Atom *ap;
9302         Double w;
9303         if (mp == NULL || center == NULL)
9304                 return 1;
9305         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9306                 return 2;   /*  Empty molecule  */
9307         w = 0.0;
9308         center->x = center->y = center->z = 0.0;
9309         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9310                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9311                         continue;
9312                 VecScaleInc(*center, ap->r, ap->weight);
9313                 w += ap->weight;
9314         }
9315         if (w < 1e-7)
9316                 return 3;  /*  Atomic weights are not defined?  */
9317         w = 1.0 / w;
9318         VecScaleSelf(*center, w);
9319         return 0;
9320 }
9321
9322 int
9323 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
9324 {
9325         Vector vmin, vmax;
9326         int i;
9327         Atom *ap;
9328         if (mp == NULL)
9329                 return 1;
9330         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9331                 return 2;   /*  Empty molecule  */
9332         vmin.x = vmin.y = vmin.z = 1e50;
9333         vmax.x = vmax.y = vmax.z = -1e50;
9334         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9335                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9336                         continue;
9337                 if (vmin.x > ap->r.x)
9338                         vmin.x = ap->r.x;
9339                 if (vmin.y > ap->r.y)
9340                         vmin.y = ap->r.y;
9341                 if (vmin.z > ap->r.z)
9342                         vmin.z = ap->r.z;
9343                 if (vmax.x < ap->r.x)
9344                         vmax.x = ap->r.x;
9345                 if (vmax.y < ap->r.y)
9346                         vmax.y = ap->r.y;
9347                 if (vmax.z < ap->r.z)
9348                         vmax.z = ap->r.z;
9349         }
9350         if (min != NULL)
9351                 *min = vmin;
9352         if (max != NULL)
9353                 *max = vmax;
9354         return 0;       
9355 }
9356
9357 #pragma mark ====== Measurements ======
9358
9359 Double
9360 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
9361 {
9362         Vector r1, r2;
9363 /*      if (mp->is_xtal_coord) {
9364                 TransformVec(&r1, mp->cell->tr, vp1);
9365                 TransformVec(&r2, mp->cell->tr, vp2);
9366         } else */ {
9367                 r1 = *vp1;
9368                 r2 = *vp2;
9369         }
9370         VecDec(r1, r2);
9371         return VecLength(r1);
9372 }
9373
9374 Double
9375 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
9376 {
9377         Vector r1, r2, r3;
9378         double w;
9379 /*      if (mp->is_xtal_coord) {
9380                 TransformVec(&r1, mp->cell->tr, vp1);
9381                 TransformVec(&r2, mp->cell->tr, vp2);
9382                 TransformVec(&r3, mp->cell->tr, vp3);
9383         } else */ {
9384                 r1 = *vp1;
9385                 r2 = *vp2;
9386                 r3 = *vp3;
9387         }
9388         VecDec(r1, r2);
9389         VecDec(r3, r2);
9390         w = VecLength(r1) * VecLength(r3);
9391         if (w < 1e-20)
9392                 return NAN;
9393         return acos(VecDot(r1, r3) / w) * kRad2Deg;
9394 }
9395
9396 Double
9397 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
9398 {
9399         Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
9400         double w1, w2, w3;
9401 /*      if (mp->is_xtal_coord) {
9402                 TransformVec(&r1, mp->cell->tr, vp1);
9403                 TransformVec(&r2, mp->cell->tr, vp2);
9404                 TransformVec(&r3, mp->cell->tr, vp3);
9405                 TransformVec(&r4, mp->cell->tr, vp4);
9406         } else */ {
9407                 r1 = *vp1;
9408                 r2 = *vp2;
9409                 r3 = *vp3;
9410                 r4 = *vp4;
9411         }
9412         VecSub(r21, r1, r2);
9413         VecSub(r32, r2, r3);
9414         VecSub(r43, r3, r4);
9415         VecCross(v1, r21, r32);
9416         VecCross(v2, r32, r43);
9417         VecCross(v3, r32, v1);
9418         w1 = VecLength(v1);
9419         w2 = VecLength(v2);
9420         w3 = VecLength(v3);
9421         if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
9422                 return NAN;
9423         } else {
9424                 w1 = 1.0 / w1;
9425                 w2 = 1.0 / w2;
9426                 w3 = 1.0 / w3;
9427                 VecScaleSelf(v1, w1);
9428                 VecScaleSelf(v2, w2);
9429                 VecScaleSelf(v3, w3);
9430                 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
9431         }
9432 }
9433
9434 #pragma mark ====== XtalCell Parameters ======
9435
9436 void
9437 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
9438 {
9439         if (mp->cell != NULL) {
9440                 TransformVec(dst, mp->cell->tr, src);
9441         } else *dst = *src;
9442 }
9443
9444 void
9445 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
9446 {
9447         if (mp->cell != NULL) {
9448                 TransformVec(dst, mp->cell->rtr, src);
9449         } else *dst = *src;
9450 }
9451
9452 int
9453 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
9454 {
9455         static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
9456         int n1, n2, n3;
9457         Vector *vp1, *vp2, *vp3;
9458         Vector v1, v2;
9459
9460         if (cp == NULL)
9461                 return 0;
9462         for (n1 = 0; n1 < 3; n1++) {
9463                 if (cp->flags[n1] != 0)
9464                         break;
9465         }
9466         if (n1 == 3) {
9467                 /*  All directions are non-periodic  */
9468                 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
9469                 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
9470         } else {
9471                 n2 = (n1 + 1) % 3;
9472                 n3 = (n1 + 2) % 3;
9473                 vp1 = &(cp->axes[n1]);
9474                 vp2 = &(cp->axes[n2]);
9475                 vp3 = &(cp->axes[n3]);
9476                 cp->tr[n1*3] = vp1->x;
9477                 cp->tr[n1*3+1] = vp1->y;
9478                 cp->tr[n1*3+2] = vp1->z;
9479                 cp->tr[9] = cp->origin.x;
9480                 cp->tr[10] = cp->origin.y;
9481                 cp->tr[11] = cp->origin.z;
9482                 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
9483                         /*  1-dimensional or 2-dimensional system  */
9484                         /*  Create "dummy" axes, so that transforms between internal and cartesian coordinates are
9485                          possible with a single matrix  */
9486                         if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
9487                                 /*  1-dimensional  */
9488                                 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
9489                                 VecCross(v1, *vp1, xvec);
9490                                 VecCross(v2, *vp1, yvec);
9491                                 if (VecLength2(v1) < VecLength2(v2))
9492                                         v1 = v2;
9493                                 VecCross(v2, *vp1, v1);
9494                                 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
9495                                         return -1;   /*  Non-regular transform  */
9496                         } else if (cp->flags[n2] == 0) {
9497                                 v2 = *vp3;
9498                                 VecCross(v1, v2, *vp1);
9499                                 if (NormalizeVec(&v1, &v1))
9500                                         return -1;  /*  Non-regular transform  */
9501                         } else {
9502                                 v1 = *vp2;
9503                                 VecCross(v2, *vp1, v1);
9504                                 if (NormalizeVec(&v2, &v2))
9505                                         return -1;  /*  Non-regular transform  */
9506                         }
9507                         cp->tr[n2*3] = v1.x;
9508                         cp->tr[n2*3+1] = v1.y;
9509                         cp->tr[n2*3+2] = v1.z;
9510                         cp->tr[n3*3] = v2.x;
9511                         cp->tr[n3*3+1] = v2.y;
9512                         cp->tr[n3*3+2] = v2.z;
9513                 } else {
9514                         VecCross(v1, *vp1, *vp2);
9515                         if (fabs(VecDot(v1, *vp3)) < 1e-7)
9516                                 return -1;  /*  Non-regular transform  */
9517                         cp->tr[n2*3] = vp2->x;
9518                         cp->tr[n2*3+1] = vp2->y;
9519                         cp->tr[n2*3+2] = vp2->z;
9520                         cp->tr[n3*3] = vp3->x;
9521                         cp->tr[n3*3+1] = vp3->y;
9522                         cp->tr[n3*3+2] = vp3->z;
9523                 }
9524         }
9525         if (TransformInvert(cp->rtr, cp->tr))
9526                 return -1;  /*  Non-regular transform  */
9527
9528         /*  Calculate the reciprocal cell parameters  */
9529         cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[3] * cp->rtr[3] + cp->rtr[6] * cp->rtr[6]);
9530         cp->rcell[1] = sqrt(cp->rtr[1] * cp->rtr[1] + cp->rtr[4] * cp->rtr[4] + cp->rtr[7] * cp->rtr[7]);
9531         cp->rcell[2] = sqrt(cp->rtr[2] * cp->rtr[2] + cp->rtr[5] * cp->rtr[5] + cp->rtr[8] * cp->rtr[8]);
9532         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;
9533         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;
9534         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;
9535         
9536         if (calc_abc) {
9537                 /*  Calculate a, b, c, alpha, beta, gamma  */
9538                 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[1] * cp->tr[1] + cp->tr[2] * cp->tr[2]);
9539                 cp->cell[1] = sqrt(cp->tr[3] * cp->tr[3] + cp->tr[4] * cp->tr[4] + cp->tr[5] * cp->tr[5]);
9540                 cp->cell[2] = sqrt(cp->tr[6] * cp->tr[6] + cp->tr[7] * cp->tr[7] + cp->tr[8] * cp->tr[8]);
9541                 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;
9542                 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;
9543                 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;
9544         }
9545         return 0;
9546 }
9547
9548 void
9549 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
9550 {
9551         XtalCell *cp;
9552         int i;
9553         Atom *ap;
9554         Transform cmat;
9555         if (mp == NULL)
9556                 return;
9557         __MoleculeLock(mp);
9558         memset(&cmat, 0, sizeof(Transform));
9559         if (mp->cell != NULL)
9560                 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
9561         else
9562                 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
9563         if (a == 0.0) {
9564                 if (mp->cell != NULL) {
9565                         free(mp->cell);
9566                         mp->needsMDRebuild = 1;
9567                 }
9568                 mp->cell = NULL;
9569         } else {
9570                 cp = mp->cell;
9571                 if (cp == NULL) {
9572                         cp = (XtalCell *)calloc(sizeof(XtalCell), 1);
9573                         if (cp == NULL)
9574                                 Panic("Low memory during setting cell parameters");
9575                         mp->cell = cp;
9576                         mp->needsMDRebuild = 1;
9577                 }
9578                 /*  alpha, beta, gamma are in degree  */
9579                 cp->cell[0] = a;
9580                 cp->cell[1] = b;
9581                 cp->cell[2] = c;
9582                 cp->cell[3] = alpha;
9583                 cp->cell[4] = beta;
9584                 cp->cell[5] = gamma;
9585                 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
9586                         /*  c unique (hexagonal etc.)  */
9587                         Double cosa, cosb, sinb, cosg;
9588                         cosa = cos(alpha * kDeg2Rad);
9589                         cosb = cos(beta * kDeg2Rad);
9590                         sinb = sin(beta * kDeg2Rad);
9591                         cosg = cos(gamma * kDeg2Rad);
9592                         cp->axes[0].x = a * sinb;
9593                         cp->axes[0].y = 0;
9594                         cp->axes[0].z = a * cosb;
9595                         cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
9596                         cp->axes[1].z = b * cosa;
9597                         cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
9598                         cp->axes[2].x = 0;
9599                         cp->axes[2].y = 0;
9600                         cp->axes[2].z = c;
9601                 } else {
9602                         /*  b unique  */
9603                         Double cosg, sing, cosa, cosb;
9604                         cosa = cos(alpha * kDeg2Rad);
9605                         cosb = cos(beta * kDeg2Rad);
9606                         cosg = cos(gamma * kDeg2Rad);
9607                         sing = sin(gamma * kDeg2Rad);
9608                         cp->axes[0].x = a * sing;
9609                         cp->axes[0].y = a * cosg;
9610                         cp->axes[0].z = 0;
9611                         cp->axes[1].x = 0;
9612                         cp->axes[1].y = b;
9613                         cp->axes[1].z = 0;
9614                         cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
9615                         cp->axes[2].y = c * cosa;
9616                         cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
9617                 }
9618                 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
9619                 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
9620                 MoleculeCalculateCellFromAxes(cp, 0);
9621                 TransformMul(cmat, cp->tr, cmat);
9622         }
9623         
9624         /*  Update the coordinates (if requested)  */
9625         if (convertCoordinates) {
9626                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9627                         TransformVec(&(ap->r), cmat, &(ap->r));
9628                 }
9629         }
9630         
9631         /*  Update the anisotropic parameters  */
9632         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9633                 Aniso *anp = ap->aniso;
9634                 if (anp != NULL) {
9635                         MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
9636                 }
9637         }
9638         __MoleculeUnlock(mp);
9639         sMoleculeNotifyChangeAppearance(mp);
9640 }
9641
9642 void
9643 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x13, Double x23, const Double *sigmaptr)
9644 {
9645         Double d, dx;
9646         int u = 0;
9647         const Double log2 = 0.693147180559945;
9648         const Double pi22 = 19.7392088021787;  /* 2*pi**2 */
9649         Transform m1, m2;
9650         Aniso *anp;
9651         XtalCell *cp;
9652         Vector axis[3];
9653         Double val[3];
9654         if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
9655                 return;
9656         anp = mp->atoms[n1].aniso;
9657         __MoleculeLock(mp);
9658         if (anp == NULL) {
9659                 anp = (Aniso *)calloc(sizeof(Aniso), 1);
9660                 if (anp == NULL) {
9661                         __MoleculeUnlock(mp);
9662                         Panic("Low memory during setting anisotropic atom parameters");
9663                 }
9664                 mp->atoms[n1].aniso = anp;
9665         }
9666         switch (type) {
9667                 case 1: d = 1; dx = 0.5; break;
9668                 case 2: d = log2; dx = log2; break;
9669                 case 3: d = log2; dx = log2 * 0.5; break;
9670                 case 4: u = 1; d = 0.25; dx = 0.25; break;
9671                 case 5: u = 1; d = 0.25; dx = 0.125; break;
9672                 case 8: u = 1; d = pi22; dx = pi22; break;
9673                 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
9674                 case 10: d = pi22; dx = pi22; break;
9675                 default: d = dx = 1; break;
9676         }
9677         anp->bij[0] = x11 * d;
9678         anp->bij[1] = x22 * d;
9679         anp->bij[2] = x33 * d;
9680         anp->bij[3] = x12 * dx;
9681         anp->bij[4] = x13 * dx;
9682         anp->bij[5] = x23 * dx;
9683         if (sigmaptr != NULL) {
9684                 anp->has_bsig = 1;
9685                 anp->bsig[0] = sigmaptr[0] * d;
9686                 anp->bsig[1] = sigmaptr[1] * d;
9687                 anp->bsig[2] = sigmaptr[2] * d;
9688                 anp->bsig[3] = sigmaptr[3] * dx;
9689                 anp->bsig[4] = sigmaptr[4] * dx;
9690                 anp->bsig[5] = sigmaptr[5] * dx;
9691         } else {
9692                 anp->has_bsig = 0;
9693                 anp->bsig[0] = anp->bsig[1] = anp->bsig[2] = anp->bsig[3] = anp->bsig[4] = anp->bsig[5] = 0.0;
9694         }
9695         cp = mp->cell;
9696         if (cp != NULL && u == 1) {
9697                 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
9698                 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
9699                 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
9700                 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
9701                 anp->bij[4] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[3] * kDeg2Rad); */
9702                 anp->bij[5] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[4] * kDeg2Rad); */
9703                 if (sigmaptr != NULL) {
9704                         anp->bsig[0] *= cp->rcell[0] * cp->rcell[0];
9705                         anp->bsig[1] *= cp->rcell[1] * cp->rcell[1];
9706                         anp->bsig[2] *= cp->rcell[2] * cp->rcell[2];
9707                         anp->bsig[3] *= cp->rcell[0] * cp->rcell[1];
9708                         anp->bsig[4] *= cp->rcell[2] * cp->rcell[0];
9709                         anp->bsig[5] *= cp->rcell[1] * cp->rcell[2];
9710                 }
9711         }
9712         
9713         /*  Calculate the principal axes (in Cartesian coordinates)  */
9714         /*  The principal axes are the eigenvectors of matrix At(B^-1)A, where
9715                 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
9716                 in which x and z are the crystal-space and cartesian coordinates. */
9717         m1[0] = anp->bij[0] / pi22;
9718         m1[4] = anp->bij[1] / pi22;
9719         m1[8] = anp->bij[2] / pi22;
9720         m1[1] = m1[3] = anp->bij[3] / pi22;
9721         m1[2] = m1[6] = anp->bij[4] / pi22;
9722         m1[5] = m1[7] = anp->bij[5] / pi22;
9723         MatrixInvert(m1, m1);
9724         if (cp != NULL) {
9725                 memmove(m2, cp->rtr, sizeof(Mat33));
9726                 MatrixMul(m1, m1, m2);
9727                 MatrixTranspose(m2, m2);
9728                 MatrixMul(m1, m2, m1);
9729         }
9730         MatrixSymDiagonalize(m1, val, axis);
9731         for (u = 0; u < 3; u++) {
9732                 if (val[u] < 0) {
9733                         fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
9734                         val[u] = 0.001;
9735                 } else {
9736                         val[u] = 1 / sqrt(val[u]);
9737                 }
9738                 anp->pmat[u*3] = axis[u].x * val[u];
9739                 anp->pmat[u*3+1] = axis[u].y * val[u];
9740                 anp->pmat[u*3+2] = axis[u].z * val[u];
9741         }
9742         __MoleculeUnlock(mp);
9743 }
9744
9745 /*  Set the anisotropic parameter for atom idx according to the symop. If symop is not alive, nothing is done. */
9746 void
9747 MoleculeSetAnisoBySymop(Molecule *mp, int idx)
9748 {
9749         Atom *ap, *ap2;
9750         Transform t1, t2;
9751         if (mp == NULL || idx < 0 || idx >= mp->natoms)
9752                 return;
9753         ap = ATOM_AT_INDEX(mp->atoms, idx);
9754         if (!SYMOP_ALIVE(ap->symop))
9755                 return;
9756         ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
9757         if (ap2->aniso == NULL) {
9758                 if (ap->aniso != NULL) {
9759                         free(ap->aniso);
9760                         ap->aniso = NULL;
9761                 }
9762                 return;
9763         }
9764         if (ap->aniso == NULL)
9765                 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
9766         if (ap->symop.sym == 0 || ap->symop.sym >= mp->nsyms) {
9767                 /*  Just copy the aniso parameters  */
9768                 memmove(ap->aniso, ap2->aniso, sizeof(Aniso));
9769                 return;
9770         }
9771         memmove(t1, SYMMETRY_AT_INDEX(mp->syms, ap->symop.sym), sizeof(Transform));
9772         t1[9] = t1[10] = t1[11] = 0.0;
9773         memset(t2, 0, sizeof(Transform));
9774         t2[0] = ap2->aniso->bij[0];
9775         t2[4] = ap2->aniso->bij[1];
9776         t2[8] = ap2->aniso->bij[2];
9777         t2[1] = t2[3] = ap2->aniso->bij[3];
9778         t2[2] = t2[6] = ap2->aniso->bij[4];
9779         t2[5] = t2[7] = ap2->aniso->bij[5];
9780         TransformMul(t2, t1, t2);
9781         TransformInvert(t1, t1);
9782         TransformMul(t2, t2, t1);
9783         MoleculeSetAniso(mp, idx, 0, t2[0], t2[4], t2[8], t2[1], t2[2], t2[5], (ap2->aniso->has_bsig ? ap2->aniso->bsig : NULL));
9784 }
9785
9786 int
9787 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic, int convertCoordinates)
9788 {
9789         static Vector zeroVec = {0, 0, 0};
9790         XtalCell b;
9791         Transform cmat;
9792         int i, n;
9793         Atom *ap;
9794         if (mp == NULL)
9795                 return 0;
9796         if (mp->cell != NULL)
9797                 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
9798         else
9799                 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
9800         if (ax == NULL) {
9801                 if (mp->cell != NULL) {
9802                         free(mp->cell);
9803                         mp->needsMDRebuild = 1;
9804                 }
9805                 mp->cell = NULL;
9806                 return 0;
9807         }       
9808         memset(&b, 0, sizeof(b));
9809         b.axes[0] = (ax != NULL ? *ax : zeroVec);
9810         b.axes[1] = (ay != NULL ? *ay : zeroVec);
9811         b.axes[2] = (az != NULL ? *az : zeroVec);
9812         b.origin = *ao;
9813         memmove(b.flags, periodic, 3);
9814         if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
9815                 return -1;
9816         __MoleculeLock(mp);
9817         if (mp->cell == NULL) {
9818                 mp->needsMDRebuild = 1;
9819         } else {
9820                 if (mp->cell->has_sigma) {
9821                         /*  Keep the sigma  */
9822                         b.has_sigma = 1;
9823                         memmove(b.cellsigma, mp->cell->cellsigma, sizeof(mp->cell->cellsigma));
9824                 }
9825                 if ((b.flags[0] != mp->cell->flags[0]) || (b.flags[1] != mp->cell->flags[1]) || (b.flags[2] != mp->cell->flags[2])) {
9826                         mp->needsMDRebuild = 1;
9827                 }
9828                 free(mp->cell);
9829         }
9830         mp->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
9831         if (mp->cell != NULL) {
9832                 memmove(mp->cell, &b, sizeof(XtalCell));
9833                 TransformMul(cmat, b.tr, cmat);
9834                 /*  Update the coordinates (if requested)  */
9835                 if (convertCoordinates) {
9836                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9837                                 TransformVec(&(ap->r), cmat, &(ap->r));
9838                         }
9839                 }
9840                 
9841                 /*  Update the anisotropic parameters  */
9842                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9843                         Aniso *anp = ap->aniso;
9844                         if (anp != NULL) {
9845                                 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
9846                         }
9847                 }
9848                 n = 0;
9849         } else n = -2;  /*  Out of memory  */
9850         __MoleculeUnlock(mp);
9851         sMoleculeNotifyChangeAppearance(mp);
9852         return n;
9853 }
9854
9855 #pragma mark ====== Fragment manipulation ======
9856
9857 static void
9858 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
9859 {
9860         Atom *ap;
9861         Int i, *cp, idx2;
9862         if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
9863                 return;
9864         IntGroupAdd(result, idx, 1);
9865         ap = ATOM_AT_INDEX(mp->atoms, idx);
9866         cp = AtomConnectData(&ap->connect);
9867         for (i = 0; i < ap->connect.count; i++) {
9868                 idx2 = cp[i];
9869                 if (IntGroupLookup(result, idx2, NULL))
9870                         continue;
9871                 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, idx2)->anchor != NULL)
9872                         continue;  /*  bond between two pi_anchors is ignored  */
9873                 sMoleculeFragmentSub(mp, idx2, result, exatoms);
9874         }
9875         if (ap->anchor != NULL) {
9876                 cp = AtomConnectData(&ap->anchor->connect);
9877                 for (i = 0; i < ap->anchor->connect.count; i++) {
9878                         idx2 = cp[i];
9879                         if (IntGroupLookup(result, idx2, NULL))
9880                                 continue;
9881                         sMoleculeFragmentSub(mp, idx2, result, exatoms);
9882                 }
9883         }
9884 }
9885
9886 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
9887     not containing the atoms in exatoms  */
9888 IntGroup *
9889 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
9890 {
9891         IntGroup *result;
9892         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9893                 return NULL;
9894         result = IntGroupNew();
9895         sMoleculeFragmentSub(mp, n1, result, exatoms);
9896         return result;
9897 }
9898
9899 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
9900     not containing the atoms n2, n3, ... (terminated by -1)  */
9901 IntGroup *
9902 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
9903 {
9904         int i;
9905         IntGroup *exatoms, *result;
9906         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9907                 return NULL;
9908         exatoms = IntGroupNew();
9909         for (i = 0; i < argc; i++)
9910                 IntGroupAdd(exatoms, argv[i], 1);
9911         result = IntGroupNew();
9912         sMoleculeFragmentSub(mp, n1, result, exatoms);
9913         IntGroupRelease(exatoms);
9914         return result;
9915 }
9916
9917 /*  The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
9918     not containing the atoms in exatoms  */
9919 IntGroup *
9920 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
9921 {
9922         IntGroupIterator iter;
9923         IntGroup *result;
9924         int i;
9925         if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
9926                 return NULL;
9927         IntGroupIteratorInit(inatoms, &iter);
9928         result = IntGroupNew();
9929         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9930                 sMoleculeFragmentSub(mp, i, result, exatoms);
9931         }
9932         IntGroupIteratorRelease(&iter);
9933         return result;
9934 }
9935
9936 /*  Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
9937     group is bound to the rest of the molecule via only one bond.
9938         If the result is true, then the atoms belonging to the (only) bond are returned
9939         in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
9940         and n2 can be NULL, if those informations are not needed.  */
9941 int
9942 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
9943 {
9944         Int i, i1, i2, j, k, bond_count, nval1, nval2, *cp;
9945         Atom *ap;
9946         if (mp == NULL || mp->natoms == 0 || group == NULL)
9947                 return 0;  /*  Invalid arguments  */
9948         bond_count = 0;
9949         for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
9950                 i2 = IntGroupGetEndPoint(group, i);
9951                 for (j = i1; j < i2; j++) {
9952                         if (j < 0 || j >= mp->natoms)
9953                                 return 0;  /*  Invalid atom group  */
9954                         ap = ATOM_AT_INDEX(mp->atoms, j);
9955                         cp = AtomConnectData(&ap->connect);
9956                         for (k = 0; k < ap->connect.count; k++) {
9957                                 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, cp[k])->anchor != NULL)
9958                                         continue;  /*  Ignore bond between two pi_anchors  */
9959                                 if (IntGroupLookup(group, cp[k], NULL) == 0) {
9960                                         bond_count++;
9961                                         nval1 = j;
9962                                         nval2 = cp[k];
9963                                         if (bond_count > 1)
9964                                                 return 0;  /*  Too many bonds  */
9965                                 }
9966                         }
9967                         if (ap->anchor != NULL) {
9968                                 cp = AtomConnectData(&ap->anchor->connect);
9969                                 for (k = 0; k < ap->anchor->connect.count; k++) {
9970                                         if (IntGroupLookup(group, cp[k], NULL) == 0) {
9971                                                 bond_count++;
9972                                                 nval1 = j;
9973                                                 nval2 = cp[k];
9974                                                 if (bond_count > 1)
9975                                                         return 0;  /*  Too many bonds  */
9976                                         }
9977                                 }                                       
9978                         }
9979                 }
9980         }
9981         if (bond_count == 1) {
9982                 if (n1 != NULL)
9983                         *n1 = nval1;
9984                 if (n2 != NULL)
9985                         *n2 = nval2;
9986                 return 1;
9987         } else {
9988                 return 0;
9989         }       
9990 }
9991
9992 /*  Returns non-zero if the given group is 'rotatable' in the molecule. The group
9993     is said to be 'rotatable' when either of the following conditions are met; (1)
9994         the group is detachable, or (2) the group consists of two bonded atoms that define
9995         a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
9996         (either a new IntGroup or 'group' with incremented reference count; thus the caller
9997         is responsible for releasing the returned value).  */
9998 int
9999 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
10000 {
10001         int i1, i2;
10002         if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
10003                 if (rotGroup != NULL) {
10004                         IntGroupRetain(group);
10005                         *rotGroup = group;
10006                 }
10007                 return 1;
10008         }
10009         if (group != NULL && IntGroupGetCount(group) == 2) {
10010                 i1 = IntGroupGetNthPoint(group, 0);
10011                 i2 = IntGroupGetNthPoint(group, 1);
10012                 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
10013                         IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
10014                         if (frag == NULL)
10015                                 return 0;
10016                         i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
10017                         if (i1 == 0) {
10018                                 IntGroupRelease(frag);
10019                                 if (rotGroup != NULL)
10020                                         *rotGroup = NULL;
10021                                 return 0;
10022                         }
10023                         if (rotGroup != NULL)
10024                                 *rotGroup = frag;
10025                         else if (frag != NULL)
10026                                 IntGroupRelease(frag);
10027                         return i1;
10028                 }
10029         }
10030         return 0;
10031 }
10032
10033 #pragma mark ====== Multiple frame ======
10034
10035 int
10036 MoleculeGetNumberOfFrames(Molecule *mp)
10037 {
10038         if (mp == NULL)
10039                 return 0;
10040         if (mp->nframes <= 0) {
10041                 /*  Recalculate  */
10042                 int i, n;
10043                 Atom *ap;
10044                 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10045                         if (ap->nframes > n)
10046                                 n = ap->nframes;
10047                 }
10048                 if (n == 0)
10049                         n = 1;
10050                 mp->nframes = n;
10051         }
10052         return mp->nframes;
10053 }
10054
10055 int
10056 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
10057 {
10058         int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
10059         Vector *tempv, *vp;
10060         Atom *ap;
10061         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
10062                 return -1;
10063
10064         n_old = MoleculeGetNumberOfFrames(mp);
10065         n_new = n_old + count;
10066         last_inserted = IntGroupGetNthPoint(group, count - 1);
10067         if (n_new <= last_inserted) {
10068                 exframes = last_inserted - n_new + 1;  /*  number of extra frames that will be silently inserted  */
10069                 n_new += exframes;
10070         } else exframes = 0;
10071
10072         tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4);  /*  "*4" for handling cells  */
10073         if (tempv == NULL)
10074                 return -1;
10075
10076         __MoleculeLock(mp);
10077
10078         /*  Copy back the current coordinates  */
10079         /*  No change in the current coordinates, but the frame buffer is updated  */
10080         MoleculeSelectFrame(mp, mp->cframe, 1); 
10081         
10082         /*  Expand ap->frames for all atoms  */
10083         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10084                 if (ap->frames == NULL)
10085                         vp = (Vector *)calloc(sizeof(Vector), n_new);
10086                 else
10087                         vp = (Vector *)realloc(ap->frames, sizeof(Vector) * n_new);
10088                 if (vp == NULL) {
10089                         __MoleculeUnlock(mp);
10090                         return -1;
10091                 }
10092                 for (j = ap->nframes; j < n_new; j++)
10093                         vp[j] = ap->r;
10094                 ap->frames = vp;
10095         }
10096         if (mp->cell != NULL) {
10097                 j = mp->nframe_cells;
10098                 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, n_new - 1, NULL);
10099                 for (i = j; i < n_new; i++) {
10100                         /*  Set the current cell parameters to the expanded frames  */
10101                         mp->frame_cells[i * 4] = mp->cell->axes[0];
10102                         mp->frame_cells[i * 4 + 1] = mp->cell->axes[1];
10103                         mp->frame_cells[i * 4 + 2] = mp->cell->axes[2];
10104                         mp->frame_cells[i * 4 + 3] = mp->cell->origin;
10105                 }
10106         }
10107         
10108         /*  group = [n0..n1-1, n2..n3-1, ...]  */
10109         /*  s = t = 0,  */
10110         /*  tempv[0..n0-1] <- ap[0..n0-1], s += n0,
10111             tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
10112                 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
10113                 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
10114                 ...
10115                 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
10116                 At last, s will become n_old and t will become count.  */
10117         for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10118                 int s, t, ns, ne, mult;
10119                 Vector cr;
10120                 ne = s = t = 0;
10121                 if (i == mp->natoms) {
10122                         if (mp->cell == NULL || mp->frame_cells == NULL)
10123                                 break;
10124                         vp = mp->frame_cells;
10125                         mult = 4;
10126                 } else {
10127                         cr = ap->r;
10128                         vp = ap->frames;
10129                         mult = 1;
10130                 }
10131                 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10132                         if (ns > ne) {
10133                                 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
10134                                 s += ns - ne;
10135                         }
10136                         ne = IntGroupGetEndPoint(group, j);
10137                         while (ns < ne) {
10138                                 if (i == mp->natoms) {
10139                                         if (inFrameCell != NULL) {
10140                                                 tempv[ns * 4] = inFrameCell[t * 4];
10141                                                 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
10142                                                 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
10143                                                 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
10144                                         } else {
10145                                                 tempv[ns * 4] = mp->cell->axes[0];
10146                                                 tempv[ns * 4 + 1] = mp->cell->axes[1];
10147                                                 tempv[ns * 4 + 2] = mp->cell->axes[2];
10148                                                 tempv[ns * 4 + 3] = mp->cell->origin;
10149                                         }
10150                                 } else {
10151                                         if (inFrame != NULL)
10152                                                 tempv[ns] = inFrame[natoms * t + i];
10153                                         else
10154                                                 tempv[ns] = cr;
10155                                 }
10156                                 t++;
10157                                 ns++;
10158                         }
10159                 }
10160                 if (n_new > ne) {
10161                         memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
10162                         s += n_new - ne;
10163                 }
10164                 if (i < mp->natoms)
10165                         ap->nframes = n_new;
10166                 memmove(vp, tempv, sizeof(Vector) * mult * n_new);
10167         }
10168         free(tempv);
10169         mp->nframes = n_new;
10170         MoleculeSelectFrame(mp, last_inserted, 0);
10171         MoleculeIncrementModifyCount(mp);
10172         __MoleculeUnlock(mp);
10173         return count;
10174 }
10175
10176 int
10177 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
10178 {
10179         int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
10180         Vector *tempv, *vp;
10181         Atom *ap;
10182         IntGroup *group, *group2;
10183
10184         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
10185                 return -1;
10186
10187         /*  outFrame[] should have enough size for Vector * natoms * group.count  */
10188         memset(outFrame, 0, sizeof(Vector) * natoms * count);
10189         if (mp->cell != NULL && mp->frame_cells != NULL)
10190                 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
10191
10192         n_old = MoleculeGetNumberOfFrames(mp);
10193         if (n_old == 1)
10194                 return -2;  /*  Cannot delete last frame  */
10195
10196         group = IntGroupNew();
10197         group2 = IntGroupNewWithPoints(0, n_old, -1);
10198         IntGroupIntersect(inGroup, group2, group);
10199         IntGroupRelease(group2);
10200         count = IntGroupGetCount(group);
10201         n_new = n_old - count;
10202         if (n_new < 1) {
10203                 IntGroupRelease(group);
10204                 return -2;  /*  Trying to delete too many frames  */
10205         }
10206         tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4);  /*  "*4" for handling cells  */
10207         if (tempv == NULL) {
10208                 IntGroupRelease(group);
10209                 return -1;
10210         }
10211
10212         __MoleculeLock(mp);
10213
10214         /*  Copy back the current coordinates  */
10215         /*  No change in the current coordinates, but the frame buffer is updated  */
10216         MoleculeSelectFrame(mp, mp->cframe, 1); 
10217
10218         /*  Determine which frame should be selected after removal is completed  */
10219         {
10220                 int n1;
10221                 if (IntGroupLookup(group, mp->cframe, &i)) {
10222                         /*  cframe will be removed  */
10223                         n1 = IntGroupGetStartPoint(group, i) - 1;
10224                         if (n1 < 0)
10225                                 n1 = IntGroupGetEndPoint(group, i);
10226                 } else n1 = mp->cframe;
10227                 /*  Change to that frame  */
10228                 MoleculeSelectFrame(mp, n1, 0);
10229                 group2 = IntGroupNewFromIntGroup(group);
10230                 IntGroupReverse(group2, 0, n_old);
10231                 new_cframe = IntGroupLookupPoint(group2, n1);
10232                 if (new_cframe < 0)
10233                         return -3;  /*  This cannot happen  */
10234                 IntGroupRelease(group2);
10235         }
10236
10237         /*  group = [n0..n1-1, n2..n3-1, ...]  */
10238         /*  s = t = 0, */
10239         /*  tempv[0..n0-1] -> ap[0..n0-1], s += n0,
10240             tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
10241                 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
10242                 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
10243                 ...
10244                 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
10245                 At last, s will become n_new and t will become count.  */
10246         nframes = 0;
10247         for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10248                 int s, t, j, ns, ne;
10249                 int mult;
10250                 /*  if i == mp->natoms, mp->frame_cells is handled  */
10251                 if (i == mp->natoms) {
10252                         if (mp->cell == NULL || mp->frame_cells == NULL)
10253                                 break;
10254                         mult = 4;
10255                         vp = mp->frame_cells;
10256                         old_count = n_old;
10257                 } else {
10258                         mult = 1;
10259                         vp = ap->frames;
10260                         if (vp == NULL) {
10261                                 ap->frames = vp = (Vector *)calloc(sizeof(Vector), n_old);
10262                                 if (vp == NULL) {
10263                                         __MoleculeUnlock(mp);
10264                                         return -1;
10265                                 }
10266                         }
10267                         old_count = ap->nframes;
10268                 }
10269
10270                 /*  Copy vp to tempv  */
10271                 memset(tempv, 0, sizeof(Vector) * mult * n_old);
10272                 memmove(tempv, vp, sizeof(Vector) * mult * (old_count > n_old ? n_old : old_count));
10273                 ne = ns = s = t = 0;
10274                 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10275                         if (ns > n_old)
10276                                 ns = n_old;
10277                         if (ns > ne) {
10278                                 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (ns - ne));
10279                                 s += ns - ne;
10280                         }
10281                         ne = IntGroupGetEndPoint(group, j);
10282                         if (ne > n_old)
10283                                 ne = n_old;
10284                         while (ns < ne) {
10285                                 if (i < mp->natoms)
10286                                         outFrame[natoms * t + i] = tempv[ns];
10287                                 else if (outFrameCell != NULL) {
10288                                         outFrameCell[t * 4] = tempv[ns * 4];
10289                                         outFrameCell[t * 4 + 1] = tempv[ns * 4 + 1];
10290                                         outFrameCell[t * 4 + 2] = tempv[ns * 4 + 2];
10291                                         outFrameCell[t * 4 + 3] = tempv[ns * 4 + 3];
10292                                 }
10293                                 t++;
10294                                 ns++;
10295                         }
10296                 }
10297                 if (n_old > ne) {
10298                         memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (n_old - ne));
10299                         s += n_old - ne;
10300                 }
10301                 if (i < mp->natoms)
10302                         ap->nframes = s;
10303                 if (nframes < s)
10304                         nframes = s;
10305                 if (s <= 1) {
10306                         if (i < mp->natoms) {
10307                                 free(ap->frames);
10308                                 ap->frames = NULL;
10309                                 ap->nframes = 0;
10310                         } else {
10311                                 free(mp->frame_cells);
10312                                 mp->frame_cells = NULL;
10313                                 mp->nframe_cells = 0;
10314                         }
10315                 } else {
10316                         if (i < mp->natoms)
10317                                 ap->frames = (Vector *)realloc(ap->frames, sizeof(Vector) * s);
10318                         else {
10319                                 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, s - 1, NULL);
10320                                 mp->nframe_cells = s;
10321                         }
10322                 }
10323         }
10324         free(tempv);
10325         mp->nframes = nframes;
10326         
10327         /*  Select the "last" frame; do not "copy back" the coordinates to the frame table  */
10328 /*      i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
10329         MoleculeSelectFrame(mp, new_cframe, 0);
10330
10331         IntGroupRelease(group);
10332
10333         MoleculeIncrementModifyCount(mp);
10334         __MoleculeUnlock(mp);
10335         return count;
10336 }
10337
10338 int
10339 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
10340 {
10341         int i, cframe, nframes, modified;
10342         Atom *ap;
10343         cframe = mp->cframe;
10344         nframes = MoleculeGetNumberOfFrames(mp);
10345         if (frame == -1)
10346                 frame = mp->cframe;
10347         if (mp == NULL || mp->natoms == 0 || frame < 0 || frame >= nframes)
10348                 return -1;
10349         modified = 0;
10350         __MoleculeLock(mp);
10351         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10352                 if (copyback && cframe >= 0 && cframe < ap->nframes) {
10353                         /*  Write the current coordinate back to the frame array  */
10354                         ap->frames[cframe] = ap->r;
10355                 }
10356                 if (frame != cframe && frame >= 0 && frame < ap->nframes) {
10357                         /*  Read the coordinate from the frame array  */
10358                         ap->r = ap->frames[frame];
10359                         modified = 1;
10360                 }
10361         }
10362
10363         if (mp->cell != NULL && mp->frame_cells != NULL) {
10364                 /*  Write the current cell back to the frame_cells array  */
10365                 if (copyback && cframe >= 0) {
10366                         Vector *vp = (Vector *)AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, cframe, NULL);
10367                         vp[0] = mp->cell->axes[0];
10368                         vp[1] = mp->cell->axes[1];
10369                         vp[2] = mp->cell->axes[2];
10370                         vp[3] = mp->cell->origin;
10371                 }
10372                 /*  Set the cell from the frame array  */
10373                 if (frame != cframe && frame >= 0 && frame < mp->nframe_cells) {
10374                         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);
10375                         modified = 1;
10376                         MoleculeAmendBySymmetry(mp, NULL, NULL, NULL);
10377                 }
10378         }
10379         mp->cframe = frame;
10380         if (modified)
10381                 mp->needsMDCopyCoordinates = 1;
10382         __MoleculeUnlock(mp);
10383         sMoleculeNotifyChangeAppearance(mp);
10384         return frame;
10385 }
10386
10387 /*  If molecule is multi-frame, then flush the current information to the frame buffer.
10388     Returns the number of frames.  */
10389 int
10390 MoleculeFlushFrames(Molecule *mp)
10391 {
10392         int nframes = MoleculeGetNumberOfFrames(mp);
10393         if (nframes > 1)
10394                 MoleculeSelectFrame(mp, mp->cframe, 1);
10395         return nframes;
10396 }
10397
10398 #pragma mark ====== Pi Atoms ======
10399
10400 static inline void
10401 sMoleculeCalculatePiAnchorPosition(Atom *ap, Atom *atoms)
10402 {
10403         Int *cp, j, n;
10404         Atom *ap2;
10405         cp = AtomConnectData(&ap->anchor->connect);
10406         n = ap->anchor->connect.count;
10407         VecZero(ap->r);
10408         for (j = 0; j < n; j++) {
10409                 Double w = ap->anchor->coeffs[j];
10410                 ap2 = ATOM_AT_INDEX(atoms, cp[j]);
10411                 VecScaleInc(ap->r, ap2->r, w);
10412         }       
10413 }
10414
10415 void
10416 MoleculeUpdatePiAnchorPositions(Molecule *mol)
10417 {
10418         Int i;
10419         Atom *ap;
10420         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
10421                 if (ap->anchor == NULL)
10422                         continue;
10423                 sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
10424         }
10425 }
10426
10427 void
10428 MoleculeCalculatePiAnchorPosition(Molecule *mol, int idx)
10429 {
10430         Atom *ap;
10431         if (mol == NULL || idx < 0 || idx >= mol->natoms)
10432                 return;
10433         ap = ATOM_AT_INDEX(mol->atoms, idx);
10434         if (ap->anchor == NULL)
10435                 return;
10436         sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
10437 }
10438
10439 int
10440 MoleculeSetPiAnchorList(Molecule *mol, Int idx, Int nentries, Int *entries, Double *weights, Int *nUndoActions, struct MolAction ***undoActions)
10441 {
10442         Atom *ap;
10443         Int *ip, i, j, n, *np;
10444         Double d;
10445         if (mol == NULL || idx < 0 || idx >= mol->natoms || nentries <= 1)
10446                 return -1;  /*  Invalid argument  */
10447         if (weights != NULL) {
10448                 d = 0.0;
10449                 for (i = 0; i < nentries; i++) {
10450                         if (weights[i] <= 0.0) {
10451                                 return 10;  /*  Weights must be positive  */
10452                         }
10453                         d += weights[i];
10454                 }
10455                 d = 1.0 / d;
10456         } else d = 1.0 / nentries;
10457         ap = ATOM_AT_INDEX(mol->atoms, idx);
10458         if (ap->anchor != NULL) {
10459                 /*  Already an anchor: check if bonds/angles/dihedrals have entries related to this anchor  */
10460                 IntGroup *bg, *ag, *dg, *ig;
10461                 Int *ibuf, ibufsize;
10462                 MolAction *act;
10463                 bg = ag = dg = ig = NULL;
10464                 ip = AtomConnectData(&ap->anchor->connect);
10465                 for (i = 0; i < ap->anchor->connect.count; i++) {
10466                         n = ip[i];
10467                         for (j = 0; j < nentries; j++) {
10468                                 if (n == entries[j])
10469                                         break;
10470                         }
10471                         if (j == nentries) {
10472                                 /*  This entry will disappear: if any bond/angle/dihedral has idx-n pair, that should be removed.  */
10473                                 for (j = 0, np = mol->bonds; j < mol->nbonds; j++, np += 2) {
10474                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[0])) {
10475                                                 if (bg == NULL)
10476                                                         bg = IntGroupNew();
10477                                                 IntGroupAdd(bg, j, 1);
10478                                         }
10479                                 }
10480                                 for (j = 0, np = mol->angles; j < mol->nangles; j++, np += 3) {
10481                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) ||
10482                                                 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1])) {
10483                                                 if (ag == NULL)
10484                                                         ag = IntGroupNew();
10485                                                 IntGroupAdd(ag, j, 1);
10486                                         }
10487                                 }
10488                                 for (j = 0, np = mol->dihedrals; j < mol->ndihedrals; j++, np += 4) {
10489                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) || (idx == np[2] && n == np[3]) ||
10490                                                 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[3] && n == np[2])) {
10491                                                 if (dg == NULL)
10492                                                         dg = IntGroupNew();
10493                                                 IntGroupAdd(dg, j, 1);
10494                                         }
10495                                 }
10496                                 for (j = 0, np = mol->impropers; j < mol->nimpropers; j++, np += 4) {
10497                                         if ((idx == np[0] && n == np[2]) || (idx == np[1] && n == np[2]) || (idx == np[3] && n == np[2]) ||
10498                                                 (idx == np[2] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[2] && n == np[3])) {
10499                                                 if (ig == NULL)
10500                                                         ig = IntGroupNew();
10501                                                 IntGroupAdd(ig, j, 1);
10502                                         }
10503                                 }
10504                         }
10505                 }
10506                 ibuf = NULL;
10507                 ibufsize = 0;
10508                 if (ig != NULL) {
10509                         /*  Delete impropers (with undo info) */
10510                         i = IntGroupGetCount(ig);
10511                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
10512                         MoleculeDeleteImpropers(mol, ibuf, ig);
10513                         if (nUndoActions != NULL && undoActions != NULL) {
10514                                 act = MolActionNew(gMolActionAddImpropers, i * 4, ibuf, ig);
10515                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10516                         }
10517                         IntGroupRelease(ig);
10518                 }
10519                 if (dg != NULL) {
10520                         /*  Delete dihedrals (with undo info)  */
10521                         i = IntGroupGetCount(dg);
10522                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
10523                         MoleculeDeleteDihedrals(mol, ibuf, dg);
10524                         if (nUndoActions != NULL && undoActions != NULL) {
10525                                 act = MolActionNew(gMolActionAddDihedrals, i * 4, ibuf, dg);
10526                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10527                         }
10528                         IntGroupRelease(dg);
10529                 }
10530                 if (ag != NULL) {
10531                         /*  Delete angles (with undo info) */
10532                         i = IntGroupGetCount(ag);
10533                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 3 - 1, NULL);
10534                         MoleculeDeleteAngles(mol, ibuf, ag);
10535                         if (nUndoActions != NULL && undoActions != NULL) {
10536                                 act = MolActionNew(gMolActionAddAngles, i * 3, ibuf, ag);
10537                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10538                         }
10539                         IntGroupRelease(ag);
10540                 }
10541                 if (bg != NULL) {
10542                         /*  Delete bonds (with undo info) */
10543                         i = IntGroupGetCount(bg);
10544                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 2 - 1, NULL);
10545                         MoleculeDeleteBonds(mol, ibuf, bg, NULL, NULL);
10546                         if (nUndoActions != NULL && undoActions != NULL) {
10547                                 act = MolActionNew(gMolActionAddBondsForUndo, i * 2, ibuf, bg);
10548                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10549                         }
10550                         IntGroupRelease(bg);
10551                 }
10552         } else {
10553                 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
10554         }
10555         AtomConnectResize(&ap->anchor->connect, nentries);
10556         memmove(AtomConnectData(&ap->anchor->connect), entries, sizeof(Int) * nentries);
10557         AssignArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), nentries - 1, NULL);
10558         if (weights != NULL) {
10559                 memmove(ap->anchor->coeffs, weights, sizeof(Double) * nentries);
10560                 for (i = 0; i < nentries; i++)
10561                         ap->anchor->coeffs[i] *= d;   /*  Normalize weight  */
10562         } else {
10563                 for (i = 0; i < nentries; i++)
10564                         ap->anchor->coeffs[i] = d;
10565         }
10566         MoleculeCalculatePiAnchorPosition(mol, idx);
10567         return 0;
10568 }
10569
10570 #pragma mark ====== MO calculation ======
10571
10572 /*  Calculate an MO value for a single point.  */
10573 /*  Index is the MO number (1-based)  */
10574 /*  tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom.  */
10575 static Double
10576 sCalcMOPoint(Molecule *mp, const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
10577 {
10578         ShellInfo *sp;
10579         PrimInfo *pp;
10580         Double val, tval, *cnp, *tmpp, *mobasep, *mop;
10581         Int i, j;
10582         /*  Cache dr and |dr|^2  */
10583         for (i = 0; i < mp->natoms; i++) {
10584                 Vector r;
10585                 r = ATOM_AT_INDEX(mp->atoms, i)->r;
10586                 tmp[i * 4] = r.x = (vp->x - r.x) * kAngstrom2Bohr;
10587                 tmp[i * 4 + 1] = r.y = (vp->y - r.y) * kAngstrom2Bohr;
10588                 tmp[i * 4 + 2] = r.z = (vp->z - r.z) * kAngstrom2Bohr;
10589                 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
10590         }
10591         /*  Iterate over all shells  */
10592         val = 0.0;
10593         mobasep = bset->mo + (index - 1) * bset->ncomps;
10594         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
10595                 pp = bset->priminfos + sp->p_idx;
10596                 cnp = bset->cns + sp->cn_idx;
10597                 if (sp->a_idx >= mp->natoms)
10598                         return 0.0; /*  This may happen when molecule is edited after setting up MO info  */
10599                 tmpp = tmp + sp->a_idx * 4;
10600                 mop = mobasep + sp->m_idx;
10601                 switch (sp->sym) {
10602                         case kGTOType_S: {
10603                                 tval = 0;
10604                                 for (j = 0; j < sp->nprim; j++) {
10605                                         tval += *cnp++ * exp(-pp->A * tmpp[3]);
10606                                         pp++;
10607                                 }
10608                                 val += mop[0] * tval;
10609                                 break;
10610                         }
10611                         case kGTOType_P: {
10612                                 Double x, y, z;
10613                                 x = y = z = 0;
10614                                 for (j = 0; j < sp->nprim; j++) {
10615                                         tval = exp(-pp->A * tmpp[3]);
10616                                         x += *cnp++ * tval;
10617                                         y += *cnp++ * tval;
10618                                         z += *cnp++ * tval;
10619                                         pp++;
10620                                 }
10621                                 x *= mop[0] * tmpp[0];
10622                                 y *= mop[1] * tmpp[1];
10623                                 z *= mop[2] * tmpp[2];
10624                                 val += x + y + z;
10625                                 break;
10626                         }
10627                         case kGTOType_SP: {
10628                                 Double t, x, y, z;
10629                                 t = x = y = z = 0;
10630                                 for (j = 0; j < sp->nprim; j++) {
10631                                         tval = exp(-pp->A * tmpp[3]);
10632                                         t += *cnp++ * tval;
10633                                         x += *cnp++ * tval;
10634                                         y += *cnp++ * tval;
10635                                         z += *cnp++ * tval;
10636                                         pp++;
10637                                 }
10638                                 t *= mop[0];
10639                                 x *= mop[1] * tmpp[0];
10640                                 y *= mop[2] * tmpp[1];
10641                                 z *= mop[3] * tmpp[2];
10642                                 val += t + x + y + z;
10643                                 break;
10644                         }
10645                         case kGTOType_D: {
10646                                 Double xx, yy, zz, xy, xz, yz;
10647                                 xx = yy = zz = xy = xz = yz = 0;
10648                                 for (j = 0; j < sp->nprim; j++) {
10649                                         tval = exp(-pp->A * tmpp[3]);
10650                                         xx += *cnp++ * tval;
10651                                         yy += *cnp++ * tval;
10652                                         zz += *cnp++ * tval;
10653                                         xy += *cnp++ * tval;
10654                                         xz += *cnp++ * tval;
10655                                         yz += *cnp++ * tval;
10656                                         pp++;
10657                                 }
10658                                 xx *= mop[0] * tmpp[0] * tmpp[0];
10659                                 yy *= mop[1] * tmpp[1] * tmpp[1];
10660                                 zz *= mop[2] * tmpp[2] * tmpp[2];
10661                                 xy *= mop[3] * tmpp[0] * tmpp[1];
10662                                 xz *= mop[4] * tmpp[0] * tmpp[2];
10663                                 yz *= mop[5] * tmpp[1] * tmpp[2];
10664                                 val += xx + yy + zz + xy + xz + yz;
10665                                 break;
10666                         }
10667                         case kGTOType_D5: {
10668                                 Double d0, d1p, d1n, d2p, d2n;
10669                                 d0 = d1p = d1n = d2p = d2n = 0;
10670                                 for (j = 0; j < sp->nprim; j++) {
10671                                         tval = exp(-pp->A * tmpp[3]);
10672                                         d0 += *cnp++ * tval;
10673                                         d1p += *cnp++ * tval;
10674                                         d1n += *cnp++ * tval;
10675                                         d2p += *cnp++ * tval;
10676                                         d2n += *cnp++ * tval;
10677                                         pp++;
10678                                 }
10679                                 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
10680                                 d1p *= mop[1] * tmpp[0] * tmpp[2];
10681                                 d1n *= mop[2] * tmpp[1] * tmpp[2];
10682                                 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
10683                                 d2n *= mop[4] * tmpp[0] * tmpp[1];
10684                                 val += d0 + d1p + d1n + d2p + d2n;
10685                                 break;
10686                         }
10687                         /*  TODO: Support F/F7 and G/G9 type orbitals  */
10688                 }
10689         }
10690         return val;
10691 }
10692
10693 /*  Calculate one MO. The input vectors are angstrom unit (changed from bohr unit: 20140520)  */
10694 /*  mono is the MO number (1-based)  */
10695 int
10696 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)
10697 {
10698         int ix, iy, iz, n, nn;
10699         Cube *cp;
10700         Double *tmp;
10701         if (mp == NULL || mp->bset == NULL)
10702                 return -1;
10703         if (mp->bset->cns == NULL) {
10704                 if (sSetupGaussianCoefficients(mp->bset) != 0)
10705                         return -1;
10706         }
10707         if (mp->bset->natoms_bs > mp->natoms)
10708                 return -3;  /*  Number of atoms is smaller than expected (internal error)  */
10709         
10710         cp = (Cube *)calloc(sizeof(Cube), 1);
10711         if (cp == NULL) {
10712                 return -1;
10713         }
10714         cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
10715         if (cp->dp == NULL) {
10716                 free(cp);
10717                 return -1;
10718         }
10719         cp->idn = mono;
10720         cp->origin = *op;
10721         cp->dx = *dxp;
10722         cp->dy = *dyp;
10723         cp->dz = *dzp;
10724         cp->nx = nx;
10725         cp->ny = ny;
10726         cp->nz = nz;
10727         
10728         /*  TODO: use multithread  */
10729         tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms_bs * 4);
10730         if (tmp == NULL) {
10731                 free(cp->dp);
10732                 free(cp);
10733                 return -1;
10734         }
10735         n = nn = 0;
10736         for (ix = 0; ix < nx; ix++) {
10737                 Vector p;
10738                 for (iy = 0; iy < ny; iy++) {
10739                         for (iz = 0; iz < nz; iz++) {
10740                                 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
10741                                 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
10742                                 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
10743                                 cp->dp[n++] = sCalcMOPoint(mp, mp->bset, mono, &p, tmp);
10744                         }
10745                         if (callback != NULL && n - nn > 100) {
10746                                 nn = n;
10747                                 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
10748                                         free(cp->dp);
10749                                         free(cp);
10750                                         free(tmp);
10751                                         return -2;  /*  User interrupt  */
10752                                 }
10753                         }
10754                 }
10755         }
10756         free(tmp);
10757
10758         AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
10759         return mp->bset->ncubes - 1;
10760 }
10761
10762 /*  Output values are in angstrom unit (changed from bohr unit: 20140520)  */
10763 int
10764 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
10765 {
10766         int i;
10767         Vector rmin, rmax, r;
10768         Double dr, dx, dy, dz;
10769         Atom *ap;
10770         if (mp == NULL || mp->bset == NULL)
10771                 return -1;
10772         if (npoints <= 0)
10773                 npoints = 1000000;
10774         rmin.x = rmin.y = rmin.z = 1e10;
10775         rmax.x = rmax.y = rmax.z = -1e10;
10776         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10777                 dr = RadiusForAtomicNumber(ap->atomicNumber);
10778                 r = ap->r;
10779                 if (dr == 0.0)
10780                         dr = 1.0;
10781                 dr = dr * 3.0 + 2.0;
10782                 if (rmin.x > r.x - dr)
10783                         rmin.x = r.x - dr;
10784                 if (rmin.y > r.y - dr)
10785                         rmin.y = r.y - dr;
10786                 if (rmin.z > r.z - dr)
10787                         rmin.z = r.z - dr;
10788                 if (rmax.x < r.x + dr)
10789                         rmax.x = r.x + dr;
10790                 if (rmax.y < r.y + dr)
10791                         rmax.y = r.y + dr;
10792                 if (rmax.z < r.z + dr)
10793                         rmax.z = r.z + dr;
10794         }
10795         dx = rmax.x - rmin.x;
10796         dy = rmax.y - rmin.y;
10797         dz = rmax.z - rmin.z;
10798         dr = pow(dx * dy * dz / npoints, 1.0/3.0);
10799         *nx = floor(dx / dr + 0.5);
10800         *ny = floor(dy / dr + 0.5);
10801         *nz = floor(dz / dr + 0.5);
10802         if (*nx == 0)
10803                 *nx = 1;
10804         if (*ny == 0)
10805                 *ny = 1;
10806         if (*nz == 0)
10807                 *nz = 1;
10808         *op = rmin;
10809         xp->x = yp->y = zp->z = dr;
10810         xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
10811         return 0;
10812 }
10813
10814 const Cube *
10815 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
10816 {
10817         if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
10818                 return NULL;
10819         return mp->bset->cubes[index];
10820 }
10821
10822 int
10823 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
10824 {
10825         int i;
10826         if (mp == NULL || mp->bset == NULL)
10827                 return -1;
10828         for (i = 0; i < mp->bset->ncubes; i++) {
10829                 if (mp->bset->cubes[i]->idn == mono)
10830                         return i;
10831         }
10832         return -1;
10833 }
10834
10835 int
10836 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
10837 {
10838         int n;
10839         if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
10840                 return -1;
10841         CubeRelease(mp->bset->cubes[index]);
10842         if (index < n - 1)
10843                 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
10844         if (--(mp->bset->ncubes) == 0) {
10845                 free(mp->bset->cubes);
10846                 mp->bset->cubes = NULL;
10847         }
10848         return mp->bset->ncubes;
10849 }
10850
10851 int
10852 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
10853 {
10854         const Cube *cp;
10855         int i, j, k, n;
10856         FILE *fp;
10857         if (mp == NULL || mp->bset == NULL)
10858                 return -1;  /*  Molecule or the basis set information is empty  */
10859         cp = MoleculeGetCubeAtIndex(mp, index);
10860         if (cp == NULL)
10861                 return -2;  /*  MO not yet calculated  */
10862         fp = fopen(fname, "wb");
10863         if (fp == NULL)
10864                 return -3;  /*  Cannot create file  */
10865
10866         /*  Comment lines  */
10867         fprintf(fp, "%s MO=%d\n", comment, cp->idn);
10868         fprintf(fp, " MO coefficients\n");
10869         
10870         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms_bs),
10871                         cp->origin.x * kAngstrom2Bohr, cp->origin.y * kAngstrom2Bohr, cp->origin.z * kAngstrom2Bohr);
10872         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx,
10873                         cp->dx.x * kAngstrom2Bohr, cp->dx.y * kAngstrom2Bohr, cp->dx.z * kAngstrom2Bohr);
10874         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny,
10875                         cp->dy.x * kAngstrom2Bohr, cp->dy.y * kAngstrom2Bohr, cp->dy.z * kAngstrom2Bohr);
10876         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz,
10877                         cp->dz.x * kAngstrom2Bohr, cp->dz.y * kAngstrom2Bohr, cp->dz.z * kAngstrom2Bohr);
10878         
10879         /*  Atomic information  */
10880         for (i = 0; i < mp->natoms; i++) {
10881                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
10882                 /*  The second number should actually be the effective charge  */
10883                 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber,
10884                                 ap->r.x * kAngstrom2Bohr, ap->r.y * kAngstrom2Bohr, ap->r.z * kAngstrom2Bohr);
10885         }
10886         fprintf(fp, "%5d%5d\n", 1, 1);
10887         
10888         /*  3D data  */
10889         for (i = n = 0; i < cp->nx; i++) {
10890                 for (j = 0; j < cp->ny; j++) {
10891                         for (k = 0; k < cp->nz; k++) {
10892                                 /*  On Windows, the "%e" format writes the exponent in 3 digits, but
10893                                     this is not standard. So we avoid using %e  */
10894                                 Double d = cp->dp[n++];
10895                                 int exponent = (int)floor(log10(fabs(d)));
10896                                 Double base = d * pow(10, -1.0 * exponent);
10897                                 fprintf(fp, " %8.5fe%+03d", base, exponent);
10898                         /*      fprintf(fp, " %12.5e", d); */
10899                                 if (k == cp->nz - 1 || k % 6 == 5)
10900                                         fprintf(fp, "\n");
10901                         }
10902                 }
10903         }
10904         fclose(fp);
10905         return 0;
10906 }
10907
10908 #pragma mark ====== Marching Cube (for isosurface) ======
10909
10910 MCube *
10911 MoleculeClearMCube(Molecule *mol, Int nx, Int ny, Int nz, const Vector *origin, Double dx, Double dy, Double dz)
10912 {
10913         MCube *mc = mol->mcube;
10914         int i;
10915         float rgba[8] = { 1, 1, 1, 0.6, 0, 0, 1, 0.6 };
10916         if (mc != NULL) {
10917                 free(mc->dp);
10918                 free(mc->radii);
10919                 free(mc->c[0].fp);
10920                 free(mc->c[0].cubepoints);
10921                 free(mc->c[0].triangles);
10922                 free(mc->c[1].fp);
10923                 free(mc->c[1].cubepoints);
10924                 free(mc->c[1].triangles);
10925                 memmove(rgba, mc->c[0].rgba, sizeof(float) * 4);
10926                 memmove(rgba + 4, mc->c[1].rgba, sizeof(float) * 4);
10927                 free(mc);
10928                 mol->mcube = NULL;
10929         }
10930         if (nx > 0 && ny > 0 && nz > 0) {
10931                 mc = (MCube *)calloc(sizeof(MCube), 1);
10932                 /*  round up to nearest 4N+1 integer  */
10933                 dx *= nx;
10934                 dy *= ny;
10935                 dz *= nz;
10936                 mc->nx = (nx + 2) / 4 * 4 + 1;
10937                 mc->ny = (ny + 2) / 4 * 4 + 1;
10938                 mc->nz = (nz + 2) / 4 * 4 + 1;
10939                 mc->dx = dx / mc->nx;
10940                 mc->dy = dy / mc->ny;
10941                 mc->dz = dz / mc->nz;
10942                 mc->origin = *origin;
10943                 mc->dp = (Double *)malloc(sizeof(Double) * mc->nx * mc->ny * mc->nz);
10944                 if (mc->dp == NULL) {
10945                         free(mc);
10946                         return NULL;
10947                 }
10948                 mc->c[0].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
10949                 mc->c[1].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
10950                 if (mc->c[0].fp == NULL || mc->c[1].fp == NULL) {
10951                         free(mc->c[0].fp);
10952                         free(mc->c[1].fp);
10953                         free(mc->dp);
10954                         free(mc);
10955                         return NULL;
10956                 }
10957                 for (i = 0; i < mc->nx * mc->ny * mc->nz; i++) {
10958                         mc->dp[i] = DBL_MAX;
10959                 }
10960                 memmove(mc->c[0].rgba, rgba, sizeof(float) * 4);
10961                 memmove(mc->c[1].rgba, rgba + 4, sizeof(float) * 4);
10962                 mol->mcube = mc;
10963         }
10964         MoleculeCallback_notifyModification(mol, 0);
10965         return mol->mcube;
10966 }
10967
10968 static int sMarchingCubeTable[256][16] = {
10969         {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10970         {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10971         {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10972         {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10973         {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10974         {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10975         {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10976         {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
10977         {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10978         {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10979         {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10980         {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
10981         {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10982         {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
10983         {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
10984         {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10985         {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10986         {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10987         {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10988         {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
10989         {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10990         {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
10991         {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
10992         {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
10993         {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
10994         {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
10995         {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
10996         {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
10997         {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
10998         {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
10999         {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
11000         {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
11001         {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11002         {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11003         {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11004         {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
11005         {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11006         {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
11007         {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
11008         {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
11009         {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11010         {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
11011         {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
11012         {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
11013         {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
11014         {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
11015         {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
11016         {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
11017         {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11018         {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
11019         {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
11020         {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11021         {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
11022         {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
11023         {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
11024         {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
11025         {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
11026         {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
11027         {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
11028         {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
11029         {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
11030         {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
11031         {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
11032         {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11033         {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11034         {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11035         {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11036         {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
11037         {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11038         {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
11039         {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
11040         {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
11041         {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11042         {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
11043         {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
11044         {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
11045         {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
11046         {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
11047         {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
11048         {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
11049         {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11050         {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
11051         {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
11052         {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
11053         {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
11054         {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
11055         {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
11056         {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
11057         {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
11058         {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
11059         {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
11060         {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
11061         {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
11062         {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
11063         {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
11064         {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
11065         {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11066         {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
11067         {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
11068         {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
11069         {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
11070         {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
11071         {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11072         {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
11073         {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
11074         {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
11075         {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
11076         {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
11077         {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
11078         {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
11079         {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
11080         {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11081         {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
11082         {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
11083         {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
11084         {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
11085         {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
11086         {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
11087         {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
11088         {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11089         {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
11090         {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
11091         {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
11092         {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
11093         {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
11094         {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11095         {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
11096         {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11097         {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11098         {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11099         {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11100         {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
11101         {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11102         {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
11103         {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
11104         {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
11105         {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11106         {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
11107         {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
11108         {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
11109         {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
11110         {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
11111         {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
11112         {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
11113         {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11114         {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
11115         {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
11116         {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
11117         {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
11118         {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
11119         {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
11120         {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
11121         {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
11122         {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11123         {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
11124         {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
11125         {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
11126         {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
11127         {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
11128         {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11129         {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11130         {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
11131         {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
11132         {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
11133         {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
11134         {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
11135         {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
11136         {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
11137         {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
11138         {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
11139         {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
11140         {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
11141         {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
11142         {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
11143         {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
11144         {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
11145         {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
11146         {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
11147         {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
11148         {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
11149         {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
11150         {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
11151         {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
11152         {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
11153         {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
11154         {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
11155         {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
11156         {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11157         {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
11158         {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
11159         {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11160         {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11161         {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11162         {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
11163         {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
11164         {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
11165         {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
11166         {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
11167         {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
11168         {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
11169         {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
11170         {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
11171         {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
11172         {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
11173         {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11174         {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
11175         {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
11176         {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11177         {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
11178         {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
11179         {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
11180         {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
11181         {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
11182         {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
11183         {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
11184         {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11185         {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
11186         {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
11187         {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
11188         {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
11189         {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
11190         {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11191         {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
11192         {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11193         {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
11194         {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
11195         {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
11196         {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
11197         {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
11198         {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
11199         {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
11200         {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
11201         {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
11202         {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
11203         {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
11204         {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11205         {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
11206         {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
11207         {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11208         {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11209         {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11210         {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
11211         {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
11212         {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11213         {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
11214         {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
11215         {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11216         {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11217         {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
11218         {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11219         {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
11220         {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11221         {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11222         {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11223         {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11224         {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
11225 };
11226
11227 /*  Recalculate the MCube  */
11228 /*  If idn < 0, then the current grid settings and values are unchanged, and */
11229 /*  only the marching cubes are regenerated.  */
11230 int
11231 MoleculeUpdateMCube(Molecule *mol, int idn)
11232 {
11233         Int flags, retval, step, hstep, sn;
11234         Int n, ix, iy, iz, nx, ny, nz;
11235         Int nn, iix, iiy, iiz;
11236         Int ncubepoints, c1, c2, c3;
11237         Int *ip;
11238         Double thres, *tmp, dd;
11239         Vector p;
11240         MCube *mc;
11241         MCubePoint *mcp;
11242         Atom *ap;
11243
11244         if (mol == NULL || mol->bset == NULL || mol->mcube == NULL)
11245                 return -1;
11246         if (mol->bset->cns == NULL) {
11247                 if (sSetupGaussianCoefficients(mol->bset) != 0)
11248                         return -1;
11249         }
11250         if (mol->bset->natoms_bs > mol->natoms)
11251                 return -1;  /*  Number of atoms is smaller than expected  */
11252
11253         mc = mol->mcube;
11254         if (idn > 0) {
11255                 ShellInfo *sp;
11256                 Double *mobasep, *mop, mopmax;
11257                 Double xmin, xmax, ymin, ymax, zmin, zmax;
11258                 /*  Clear mcube values  */
11259                 for (ix = 0; ix < mc->nx * mc->ny * mc->nz; ix++) {
11260                         mc->dp[ix] = DBL_MAX;
11261                         mc->c[0].fp[ix] = 0;
11262                         mc->c[1].fp[ix] = 0;
11263                 }
11264                 mc->idn = idn;
11265                 /*  Estimate the orbital sizes  */
11266                 mc->radii = (Double *)realloc(mc->radii, sizeof(Double) * mol->natoms);
11267                 if (mc->radii == NULL)
11268                         return -2;  /*  Out of memory  */
11269                 mc->nradii = mol->natoms;
11270                 memset(mc->radii, 0, sizeof(Double) * mc->nradii);
11271                 mobasep = mol->bset->mo + (mc->idn - 1) * mol->bset->ncomps;
11272                 mopmax = 0.0;
11273                 for (ix = 0, sp = mol->bset->shells; ix < mol->bset->nshells; ix++, sp++) {
11274                         if (sp->a_idx >= mol->natoms)
11275                                 continue;  /*  This may happen when molecule is edited after setting up MO info  */
11276                         mop = mobasep + sp->m_idx;
11277                         for (iy = 0; iy < sp->ncomp; iy++) {
11278                                 dd = fabs(mop[iy]);
11279                                 if (dd > mc->radii[sp->a_idx])
11280                                         mc->radii[sp->a_idx] = dd;
11281                                 if (dd > mopmax)
11282                                         mopmax = dd;
11283                         }
11284                 }
11285                 xmin = ymin = zmin = 1e10;
11286                 xmax = ymax = zmax = -1e10;
11287                 for (ix = 0, ap = mol->atoms; ix < mol->natoms; ix++, ap = ATOM_NEXT(ap)) {
11288                         dd = RadiusForAtomicNumber(ap->atomicNumber);
11289                         dd = (dd * 2.0 + 1.0) * (mc->radii[ix] / mopmax) * (mc->expand > 0.0 ? mc->expand : 1.0);
11290                         mc->radii[ix] = dd;
11291                         p = ap->r;
11292                         dd += 0.1;
11293                         if (p.x - dd < xmin)
11294                                 xmin = p.x - dd;
11295                         if (p.y - dd < ymin)
11296                                 ymin = p.y - dd;
11297                         if (p.z - dd < zmin)
11298                                 zmin = p.z - dd;
11299                         if (p.x + dd > xmax)
11300                                 xmax = p.x + dd;
11301                         if (p.y + dd > ymax)
11302                                 ymax = p.y + dd;
11303                         if (p.z + dd > zmax)
11304                                 zmax = p.z + dd;
11305                 }
11306                 mc->origin.x = xmin;
11307                 mc->origin.y = ymin;
11308                 mc->origin.z = zmin;
11309                 mc->dx = (xmax - xmin) / mc->nx;
11310                 mc->dy = (ymax - ymin) / mc->ny;
11311                 mc->dz = (zmax - zmin) / mc->nz;
11312         }
11313         
11314         /*  Temporary work area  */
11315         tmp = (Double *)calloc(sizeof(Double), mol->bset->natoms_bs * 4);
11316         if (tmp == NULL)
11317                 return -2;
11318         
11319         /*  TODO: use multithread  */
11320         nx = mc->nx;
11321         ny = mc->ny;
11322         nz = mc->nz;
11323         step = 4;
11324         
11325 #if 1
11326         /*  Calculate points within certain distances from atoms  */
11327         for (nn = 0, ap = mol->atoms; nn < mol->natoms; nn++, ap = ATOM_NEXT(ap)) {
11328         /*      dd = RadiusForAtomicNumber(ap->atomicNumber);
11329                 if (dd == 0.0)
11330                         dd = 1.0;
11331                 dd = dd * 1.5 + 1.0; */
11332                 dd = mc->radii[nn];
11333                 p.x = ap->r.x - dd - mc->origin.x;
11334                 p.y = ap->r.y - dd - mc->origin.y;
11335                 p.z = ap->r.z - dd - mc->origin.z;
11336                 c1 = p.x / mc->dx;
11337                 c2 = p.y / mc->dy;
11338                 c3 = p.z / mc->dz;
11339                 iix = c1 + ceil(dd * 2.0 / mc->dx);
11340                 iiy = c2 + ceil(dd * 2.0 / mc->dy);
11341                 iiz = c3 + ceil(dd * 2.0 / mc->dz);
11342                 if (c1 < 0)
11343                         c1 = 0;
11344                 if (c2 < 0)
11345                         c2 = 0;
11346                 if (c3 < 0)
11347                         c3 = 0;
11348                 if (iix >= nx)
11349                         iix = nx - 1;
11350                 if (iiy >= ny)
11351                         iiy = ny - 1;
11352                 if (iiz >= nz)
11353                         iiz = nz - 1;
11354                 for (ix = c1; ix <= iix; ix++) {
11355                         p.x = mc->origin.x + mc->dx * ix;
11356                         for (iy = c2; iy <= iiy; iy++) {
11357                                 p.y = mc->origin.y + mc->dy * iy;
11358                                 for (iz = c3; iz <= iiz; iz++) {
11359                                         n = (ix * ny + iy) * nz + iz;
11360                                         if (mc->dp[n] == DBL_MAX) {
11361                                                 p.z = mc->origin.z + mc->dz * iz;
11362                                                 mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
11363                                         }
11364                                 }
11365                         }
11366                 }
11367         }
11368         
11369 #else
11370         /*  (i * step, j * step, k * step)  */
11371         for (ix = 0; ix < nx; ix += step) {
11372                 for (iy = 0; iy < ny; iy += step) {
11373                         for (iz = 0; iz < nz; iz += step) {
11374                                 n = (ix * ny + iy) * nz + iz;
11375                                 if (mc->dp[n] == DBL_MAX) {
11376                                         p.x = mc->origin.x + mc->dx * ix;
11377                                         p.y = mc->origin.y + mc->dy * iy;
11378                                         p.z = mc->origin.z + mc->dz * iz;
11379                                         mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
11380                                 }
11381                                 n += step;
11382                         }
11383                 }
11384         }
11385         
11386         /*  Intermediate points  */
11387         for (step = 4; step > 1; step /= 2) {
11388                 hstep = step / 2;
11389                 for (sn = 0; sn <= 1; sn++) {
11390                         n = 0;
11391                         for (ix = 0; ix < nx - 1; ix += step) {
11392                                 for (iy = 0; iy < ny - 1; iy += step) {
11393                                         for (iz = 0; iz < nz - 1; iz += step) {
11394                                                 flags = 0;
11395                                                 thres = mc->thres * (sn == 0 ? 1 : -1);
11396                                                 n = (ix * ny + iy) * nz + iz;
11397                                                 if (mc->dp[n] == DBL_MAX || mc->dp[n + step * (nz * (ny + 1) + 1)] == DBL_MAX)
11398                                                         continue;
11399                                                 /*  (ix, iy, iz)  */
11400                                                 if (mc->dp[n] >= thres)
11401                                                         flags |= 1;
11402                                                 /*  (ix + step, iy, iz)  */
11403                                                 if (mc->dp[n + step * ny * nz] >= thres)
11404                                                         flags |= 2;
11405                                                 /*  (ix, iy + step, iz)  */
11406                                                 if (mc->dp[n + step * nz] >= thres)
11407                                                         flags |= 4;
11408                                                 /*  (ix + 4, iy + step, iz)  */
11409                                                 if (mc->dp[n + step * nz * (ny + 1)] >= thres)
11410                                                         flags |= 8;
11411                                                 /*  (ix, iy, iz + step)  */
11412                                                 if (mc->dp[n + step] >= thres)
11413                                                         flags |= 16;
11414                                                 if (mc->dp[n + step * (ny * nz + 1)] >= thres)
11415                                                         flags |= 32;
11416                                                 /*  (ix, iy + step, iz + step)  */
11417                                                 if (mc->dp[n + step * (nz + 1)] >= thres)
11418                                                         flags |= 64;
11419                                                 /*  (ix + step, iy + step, iz + step)  */
11420                                                 if (mc->dp[n + step * (nz * (ny + 1) + 1)] >= thres)
11421                                                         flags |= 128;
11422                                                 if (flags != 0 && flags != 255) {
11423                                                         /*  Calc the intermediate points  */
11424                                                         for (iix = 0; iix <= step; iix += hstep) {
11425                                                                 for (iiy = 0; iiy <= step; iiy += hstep) {
11426                                                                         for (iiz = 0; iiz <= step; iiz += hstep) {
11427                                                                                 if (iix % step == 0 && iiy % step == 0 && iiz % step == 0)
11428                                                                                         continue;
11429                                                                                 nn = n + (iix * ny + iiy) * nz + iiz;
11430                                                                                 if (mc->dp[nn] == DBL_MAX) {
11431                                                                                         p.x = mc->origin.x + mc->dx * (ix + iix);
11432                                                                                         p.y = mc->origin.y + mc->dy * (iy + iiy);
11433                                                                                         p.z = mc->origin.z + mc->dz * (iz + iiz);
11434                                                                                         mc->dp[nn] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
11435                                                                                 }
11436                                                                         }
11437                                                                 }
11438                                                         }
11439                                                 }
11440                                         }
11441                                 }
11442                         }
11443                 }
11444         }
11445         
11446 #endif
11447
11448         free(tmp);
11449         
11450         /*  Calculate vertex positions and normal vectors  */
11451         for (sn = 0; sn <= 1; sn++) {
11452                 n = 0;
11453                 thres = mc->thres * (sn == 0 ? 1 : -1);
11454                 VecZero(p);
11455                 for (ix = 0; ix < nx - 1; ix++) {
11456                         for (iy = 0; iy < ny - 1; iy++) {
11457                                 for (iz = 0; iz < nz - 1; iz++) {
11458                                         Double dd0, dd1;
11459                                         nn = (ix * ny + iy) * nz + iz;
11460                                         dd0 = mc->dp[nn];
11461                                         if (dd0 == DBL_MAX)
11462                                                 continue;
11463                                         if (0) {
11464                                                 dd1 = mc->dp[nn + ny * nz];
11465                                                 if (dd1 != DBL_MAX)
11466                                                         p.x = (dd1 - dd0) / mc->dx;
11467                                                 else if (ix > 0 && (dd1 = mc->dp[nn - ny * nz]) != DBL_MAX)
11468                                                         p.x = (dd0 - dd1) / mc->dx;
11469                                                 else continue;  /*  Cannot define gradient  */
11470                                                 dd1 = mc->dp[nn + nz];
11471                                                 if (dd1 != DBL_MAX)
11472                                                         p.y = (dd1 - dd0) / mc->dy;
11473                                                 else if (iy > 0 && (dd1 = mc->dp[nn - nz]) != DBL_MAX)
11474                                                         p.y = (dd0 - dd1) / mc->dy;
11475                                                 else continue;
11476                                                 dd1 = mc->dp[nn + 1];
11477                                                 if (dd1 != DBL_MAX)
11478                                                         p.z = (dd1 - dd0) / mc->dz;
11479                                                 else if (iz > 0 && (dd1 = mc->dp[nn - 1]) != DBL_MAX)
11480                                                         p.z = (dd0 - dd1) / mc->dz;
11481                                                 else continue;
11482                                                 NormalizeVec(&p, &p);
11483                                         }
11484                                         if (n + 3 >= mc->c[sn].ncubepoints) {
11485                                                 /*  Expand cubepoints[] array  */
11486                                                 mc->c[sn].cubepoints = (MCubePoint *)realloc(mc->c[sn].cubepoints, sizeof(MCubePoint) * (mc->c[sn].ncubepoints + 8192));
11487                                                 if (mc->c[sn].cubepoints == NULL) {
11488                                                         mc->c[sn].ncubepoints = 0;
11489                                                         retval = -3;
11490                                                         goto end;
11491                                                 }
11492                                                 mc->c[sn].ncubepoints += 8192;
11493                                         }
11494                                         mcp = mc->c[sn].cubepoints + n;
11495                                         iix = (dd0 >= thres ? 1 : -1);
11496                                         /*  (x, y, z)->(x + 1, y, z)  */
11497                                         dd1 = mc->dp[nn + ny * nz];
11498                                         if (dd1 != DBL_MAX) {
11499                                                 iiy = (dd1 >= thres ? 1 : -1);
11500                                                 if (iix != iiy) {
11501                                                         /*  Register  */
11502                                                         mcp->key = nn * 3;
11503                                                         mcp->d = (thres - dd0) / (dd1 - dd0);
11504                                                         mcp->pos[0] = mc->origin.x + mc->dx * (ix + mcp->d);
11505                                                         mcp->pos[1] = mc->origin.y + mc->dy * iy;
11506                                                         mcp->pos[2] = mc->origin.z + mc->dz * iz;
11507                                                         mcp->grad[0] = p.x;
11508                                                         mcp->grad[1] = p.y;
11509                                                         mcp->grad[2] = p.z;
11510                                                         mcp++;
11511                                                         n++;
11512                                                 }
11513                                         }
11514                                         /*  (x, y, z)->(x, y + 1, z)  */
11515                                         dd1 = mc->dp[nn + nz];
11516                                         if (dd1 != DBL_MAX) {
11517                                                 iiy = (dd1 >= thres ? 1 : -1);
11518                                                 if (iix != iiy) {
11519                                                         /*  Register  */
11520                                                         mcp->key = nn * 3 + 1;
11521                                                         mcp->d = (thres - dd0) / (dd1 - dd0);
11522                                                         mcp->pos[0] = mc->origin.x + mc->dx * ix;
11523                                                         mcp->pos[1] = mc->origin.y + mc->dy * (iy + mcp->d);
11524                                                         mcp->pos[2] = mc->origin.z + mc->dz * iz;
11525                                                         mcp->grad[0] = p.x;
11526                                                         mcp->grad[1] = p.y;
11527                                                         mcp->grad[2] = p.z;
11528                                                         mcp++;
11529                                                         n++;
11530                                                 }
11531                                         }
11532                                         /*  (x, y, z)->(x, y, z + 1)  */
11533                                         dd1 = mc->dp[nn + 1];
11534                                         if (dd1 != DBL_MAX) {
11535                                                 iiy = (dd1 >= thres ? 1 : -1);
11536                                                 if (iix != iiy) {
11537                                                         /*  Register  */
11538                                                         mcp->key = nn * 3 + 2;
11539                                                         mcp->d = (thres - dd0) / (dd1 - dd0);
11540                                                         mcp->pos[0] = mc->origin.x + mc->dx * ix;
11541                                                         mcp->pos[1] = mc->origin.y + mc->dy * iy;
11542                                                         mcp->pos[2] = mc->origin.z + mc->dz * (iz + mcp->d);
11543                                                         mcp->grad[0] = p.x;
11544                                                         mcp->grad[1] = p.y;
11545                                                         mcp->grad[2] = p.z;
11546                                                         mcp++;
11547                                                         n++;
11548                                                 }
11549                                         }
11550                                 }
11551                         }
11552                 }
11553                 if (n < mc->c[sn].ncubepoints)
11554                         mc->c[sn].cubepoints[n].key = -1;  /*  End mark  */
11555                 ncubepoints = n;
11556                 if (ncubepoints < 3) {
11557                         /*  Less than 3 points: no triangles  */
11558                         if (mc->c[sn].ntriangles > 0)
11559                                 mc->c[sn].triangles[0] = -1;  /*  End mark  */
11560                         retval = 0;
11561                         goto end;
11562                 }
11563                 
11564                 /*  Create triangle table  */
11565                 n = 0;
11566                 for (ix = 0; ix < nx - 1; ix++) {
11567                         for (iy = 0; iy < ny - 1; iy++) {
11568                                 for (iz = 0; iz < nz - 1; iz++) {
11569                                         nn = (ix * ny + iy) * nz + iz;
11570                                         iix = 0;
11571                                         if ((dd = mc->dp[nn]) == DBL_MAX)
11572                                                 continue;
11573                                         else if (dd >= thres)
11574                                                 iix |= 1;
11575                                         if ((dd = mc->dp[nn + ny * nz]) == DBL_MAX)
11576                                                 continue;
11577                                         else if (dd >= thres)
11578                                                 iix |= 2;
11579                                         if ((dd = mc->dp[nn + ny * nz + nz]) == DBL_MAX)
11580                                                 continue;
11581                                         else if (dd >= thres)
11582                                                 iix |= 4;
11583                                         if ((dd = mc->dp[nn + nz]) == DBL_MAX)
11584                                                 continue;
11585                                         else if (dd >= thres)
11586                                                 iix |= 8;
11587                                         if ((dd = mc->dp[nn + 1]) == DBL_MAX)
11588                                                 continue;
11589                                         else if (dd >= thres)
11590                                                 iix |= 16;
11591                                         if ((dd = mc->dp[nn + ny * nz + 1]) == DBL_MAX)
11592                                                 continue;
11593                                         else if (dd >= thres)
11594                                                 iix |= 32;
11595                                         if ((dd = mc->dp[nn + ny * nz + nz + 1]) == DBL_MAX)
11596                                                 continue;
11597                                         else if (dd >= thres)
11598                                                 iix |= 64;
11599                                         if ((dd = mc->dp[nn + nz + 1]) == DBL_MAX)
11600                                                 continue;
11601                                         else if (dd >= thres)
11602                                                 iix |= 128;
11603                                         for (iiy = 0; iiy < 15; iiy++) {
11604                                                 nn = sMarchingCubeTable[iix][iiy];
11605                                                 if (nn < 0)
11606                                                         break;
11607                                                 /*  key index for edges 0-11  */
11608                                                 switch (nn) {
11609                                                         case 0:  iiz = (( ix      * ny + iy    ) * nz + iz    ) * 3;     break;
11610                                                         case 1:  iiz = (((ix + 1) * ny + iy    ) * nz + iz    ) * 3 + 1; break;
11611                                                         case 2:  iiz = (( ix      * ny + iy + 1) * nz + iz    ) * 3;     break;
11612                                                         case 3:  iiz = (( ix      * ny + iy    ) * nz + iz    ) * 3 + 1; break;
11613                                                         case 4:  iiz = (( ix      * ny + iy    ) * nz + iz + 1) * 3;     break;
11614                                                         case 5:  iiz = (((ix + 1) * ny + iy    ) * nz + iz + 1) * 3 + 1; break;
11615                                                         case 6:  iiz = (( ix      * ny + iy + 1) * nz + iz + 1) * 3;     break;
11616                                                         case 7:  iiz = (( ix      * ny + iy    ) * nz + iz + 1) * 3 + 1; break;
11617                                                         case 8:  iiz = (( ix      * ny + iy    ) * nz + iz    ) * 3 + 2; break;
11618                                                         case 9:  iiz = (((ix + 1) * ny + iy    ) * nz + iz    ) * 3 + 2; break;
11619                                                         case 10: iiz = (((ix + 1) * ny + iy + 1) * nz + iz    ) * 3 + 2; break;
11620                                                         case 11: iiz = (( ix      * ny + iy + 1) * nz + iz    ) * 3 + 2; break;
11621                                                         default:
11622                                                                 /*  Skip this triangle  */
11623                                                                 iiy = (iiy - iiy % 3) + 2;
11624                                                                 n = n - n % 3;
11625                                                                 continue;
11626                                                 }
11627                                                 /*  Look for the key index in cubepoints  */
11628                                                 c1 = 0;
11629                                                 c3 = ncubepoints - 1;
11630                                                 mcp = mc->c[sn].cubepoints;
11631                                                 while (1) {
11632                                                         int w;
11633                                                         /*  c1 is always less than c3  */
11634                                                         if (c1 + 1 == c3) {
11635                                                                 /*  end of search  */
11636                                                                 if (mcp[c1].key == iiz) {
11637                                                                         c2 = c1;
11638                                                                 } else if (mcp[c3].key == iiz) {
11639                                                                         c2 = c3;
11640                                                                 } else {
11641                                                                         c2 = -1;
11642                                                                 }
11643                                                                 break;
11644                                                         }
11645                                                         c2 = (c1 + c3) / 2;
11646                                                         w = mcp[c2].key - iiz;
11647                                                         if (w == 0)
11648                                                                 break;
11649                                                         if (w < 0) {
11650                                                                 c1 = c2;
11651                                                         } else {
11652                                                                 c3 = c2;
11653                                                         }
11654                                                 }
11655                                                 if (c2 < 0) {
11656                                                         /*  Not found: skip this triangle  */
11657                                                         iiy = (iiy - iiy % 3) + 2;
11658                                                         n = n - n % 3;
11659                                                         continue;
11660                                                 }
11661                                                 if (n + 1 >= mc->c[sn].ntriangles) {
11662                                                         /*  Expand triangles[] array  */
11663                                                         mc->c[sn].triangles = (Int *)realloc(mc->c[sn].triangles, sizeof(Int) * (mc->c[sn].ntriangles + 8192));
11664                                                         if (mc->c[sn].triangles == NULL) {
11665                                                                 mc->c[sn].ntriangles = 0;
11666                                                                 retval = -4;
11667                                                                 goto end;
11668                                                         }
11669                                                         mc->c[sn].ntriangles += 8192;
11670                                                 }
11671                                                 mc->c[sn].triangles[n] = c2;
11672                                                 n++;
11673                                         }
11674                                 }
11675                         }
11676                 }
11677                 if (n < mc->c[sn].ntriangles)
11678                         mc->c[sn].triangles[n] = -1;  /*  End mark  */
11679                 
11680                 /*  Estimate the normal vector  */
11681                 for (n = 0, ip = mc->c[sn].triangles; ip[n] >= 0; n += 3) {
11682                         Vector v[3];
11683                         for (ix = 0; ix < 3; ix++) {
11684                                 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
11685                                 v[ix].x = mcp->pos[0];
11686                                 v[ix].y = mcp->pos[1];
11687                                 v[ix].z = mcp->pos[2];
11688                         }
11689                         VecDec(v[2], v[0]);
11690                         VecDec(v[1], v[0]);
11691                         VecCross(v[0], v[1], v[2]);
11692                         NormalizeVec(v, v);
11693                         for (ix = 0; ix < 3; ix++) {
11694                                 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
11695                                 mcp->grad[0] += v[0].x;
11696                                 mcp->grad[1] += v[0].y;
11697                                 mcp->grad[2] += v[0].z;
11698                         }
11699                 }
11700                 for (n = 0, mcp = mc->c[sn].cubepoints; mcp->key >= 0; mcp++) {
11701                         if (mcp->grad[0] != 0.0 || mcp->grad[1] != 0.0 || mcp->grad[2] != 0.0) {
11702                                 dd = 1.0 / sqrt(mcp->grad[0] * mcp->grad[0] + mcp->grad[1] * mcp->grad[1] + mcp->grad[2] * mcp->grad[2]);
11703                                 mcp->grad[0] *= dd;
11704                                 mcp->grad[1] *= dd;
11705                                 mcp->grad[2] *= dd;
11706                         }
11707                 }
11708         }
11709         retval = 0;
11710         MoleculeCallback_notifyModification(mol, 0);
11711 end:
11712         /*  For debug  */
11713         if (0) {
11714                 char *MyAppCallback_getDocumentHomeDir(void);
11715                 FILE *fp;
11716                 char *s;
11717                 Double dmax, dmin;
11718                 asprintf(&s, "%s/%s", MyAppCallback_getDocumentHomeDir(), "mcube_log.txt");
11719                 fp = fopen(s, "w");
11720                 dmax = -1e8;
11721                 dmin = 1e8;
11722                 for (n = 0; n < mc->nx * mc->ny * mc->nz; n++) {
11723                         if (mc->dp[n] == DBL_MAX)
11724                                 continue;
11725                         if (dmax < mc->dp[n])
11726                                 dmax = mc->dp[n];
11727                         if (dmin > mc->dp[n])
11728                                 dmin = mc->dp[n];
11729                 }
11730                 dmax = fabs(dmax);
11731                 dmin = fabs(dmin);
11732                 if (dmax < dmin)
11733                         dmax = dmin;
11734                 dmax = 1.001 * dmax;
11735                 fprintf(fp, "thres = %g = 100\n", mc->thres);
11736                 for (iz = 0; iz < mc->nz; iz++) {
11737                         fprintf(fp, "z = %d\n", iz);
11738                         for (iy = 0; iy < mc->ny; iy++) {
11739                                 for (ix = 0; ix < mc->nx; ix++) {
11740                                         n = (ix * ny + iy) * nz + iz;
11741                                         dd = mc->dp[n];
11742                                         if (dd == DBL_MAX)
11743                                                 fprintf(fp, " XXX ");
11744                                         else {
11745                                                 dd = dd * 100 / mc->thres;
11746                                                 if (dd > 999.0)
11747                                                         dd = 999.0;
11748                                                 else if (dd < -999.0)
11749                                                         dd = -999.0;
11750                                                 fprintf(fp, "%4d ", (int)(dd));
11751                                         }
11752                                 }
11753                                 fprintf(fp, "\n");
11754                         }
11755                         fprintf(fp, "\n");
11756                 }
11757                 
11758                 for (sn = 0; sn <= 1; sn++) {
11759                         for (n = 0; n < mc->c[sn].ncubepoints; n++) {
11760                                 MCubePoint *mcp = mc->c[sn].cubepoints + n;
11761                                 nn = mcp->key;
11762                                 if (nn == -1)
11763                                         break;
11764                                 iix = nn % 3;
11765                                 iz = nn / 3 % mc->nz;
11766                                 iy = nn / (3 * mc->nz) % mc->ny;
11767                                 ix = nn / (3 * mc->nz * mc->ny);
11768                                 fprintf(fp, "%c%d:[%d,%d,%d,%d] (%g,[%g,%g,%g],[%g,%g,%g])\n", (sn == 0 ? 'p' : 'P'),
11769                                                 n, ix, iy, iz, iix,
11770                                                 mcp->d, mcp->pos[0], mcp->pos[1], mcp->pos[2], mcp->grad[0], mcp->grad[1], mcp->grad[2]);
11771                         }
11772                         for (n = 0; n < mc->c[sn].ntriangles; n += 3) {
11773                                 if (mc->c[sn].triangles[n] < 0)
11774                                         break;
11775                                 fprintf(fp, "%c%d:(%d,%d,%d)\n", (sn == 0 ? 't' : 'T'), n / 3,
11776                                                 mc->c[sn].triangles[n], mc->c[sn].triangles[n + 1], mc->c[sn].triangles[n + 2]);
11777                         }
11778                 }
11779                 fclose(fp);
11780         }
11781         
11782         return retval;
11783 }