4 * Created by Toshi Nagata on 06/03/11.
5 * Copyright 2006 Toshi Nagata. All rights reserved.
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.
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.
25 #include "MD/MDCore.h"
26 #include "MD/MDPressure.h"
28 static Molecule *sMoleculeRoot = NULL;
29 static int sMoleculeUntitledCount = 0;
31 Int gSizeOfAtomRecord = sizeof(Atom);
33 /* These are the pasteboard data type. Since the internal representation of the
34 pasteboard data includes binary data that may be dependent on the software version,
35 the revision number is appended to these strings on startup (See MyApp::OnInit()) */
36 char *gMoleculePasteboardType = "Molecule";
37 char *gParameterPasteboardType = "Parameter";
39 #pragma mark ====== Utility function ======
42 strlen_limit(const char *s, int limit)
45 for (len = 0; *s != 0 && (limit < 0 || len < limit); s++, len++);
49 #pragma mark ====== Atom handling ======
52 s_AtomDuplicate(Atom *dst, const Atom *src, Int copy_frame)
55 dst = (Atom *)malloc(gSizeOfAtomRecord);
59 memmove(dst, src, gSizeOfAtomRecord);
60 if (src->aniso != NULL) {
61 dst->aniso = (Aniso *)malloc(sizeof(Aniso));
62 if (dst->aniso != NULL)
63 memmove(dst->aniso, src->aniso, sizeof(Aniso));
65 if (src->frames != NULL && copy_frame) {
66 dst->frames = (Vector *)malloc(sizeof(Vector) * src->nframes);
67 if (dst->frames != NULL) {
68 memmove(dst->frames, src->frames, sizeof(Vector) * src->nframes);
69 dst->nframes = src->nframes;
74 if (src->connect.count > ATOM_CONNECT_LIMIT) {
75 dst->connect.u.ptr = NULL;
76 dst->connect.count = 0;
77 NewArray(&(dst->connect.u.ptr), &(dst->connect.count), sizeof(Int), src->connect.count);
78 memmove(dst->connect.u.ptr, src->connect.u.ptr, sizeof(Int) * src->connect.count);
84 AtomDuplicate(Atom *dst, const Atom *src)
86 return s_AtomDuplicate(dst, src, 1);
90 AtomDuplicateNoFrame(Atom *dst, const Atom *src)
92 return s_AtomDuplicate(dst, src, 0);
98 if (ap->aniso != NULL) {
102 if (ap->frames != NULL) {
107 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
108 ap->connect.count = 0;
109 free(ap->connect.u.ptr);
110 ap->connect.u.ptr = NULL;
115 CubeRelease(Cube *cp)
125 BasisSetRelease(BasisSet *bset)
130 if (bset->shells != NULL)
132 if (bset->priminfos != NULL)
133 free(bset->priminfos);
134 if (bset->mo != NULL)
136 if (bset->cns != NULL)
138 if (bset->moenergies != NULL)
139 free(bset->moenergies);
140 if (bset->scfdensities != NULL)
141 free(bset->scfdensities);
142 if (bset->pos != NULL)
144 if (bset->nuccharges != NULL)
145 free(bset->nuccharges);
146 if (bset->cubes != NULL) {
147 for (i = 0; i < bset->ncubes; i++) {
148 CubeRelease(bset->cubes[i]);
156 AtomConnectData(AtomConnect *ac)
160 return ATOM_CONNECT_PTR(ac);
164 AtomConnectResize(AtomConnect *ac, Int nconnects)
169 if (nconnects <= ATOM_CONNECT_LIMIT) {
170 if (ac->count > ATOM_CONNECT_LIMIT) {
172 memmove(ac->u.data, p, sizeof(Int) * nconnects);
176 if (ac->count <= ATOM_CONNECT_LIMIT) {
179 NewArray(&p, &(ac->count), sizeof(Int), nconnects);
180 memmove(p, ac->u.data, sizeof(Int) * ac->count);
182 } else if (ac->count < nconnects) {
184 AssignArray(&(ac->u.ptr), &(ac->count), sizeof(Int), nconnects - 1, NULL);
187 ac->count = nconnects;
191 AtomConnectInsertEntry(AtomConnect *ac, Int idx, Int connect)
196 if (idx < 0 || idx >= ac->count)
198 AtomConnectResize(ac, ac->count + 1);
199 n = ac->count - idx - 1; /* Number of entries to be moved towards the bottom */
200 p = ATOM_CONNECT_PTR(ac);
202 memmove(p + idx + 1, p + idx, sizeof(Int) * n);
208 AtomConnectDeleteEntry(AtomConnect *ac, Int idx)
213 if (idx < 0 || idx >= ac->count)
215 n = ac->count - idx - 1; /* Number of entries to be moved towards the top */
216 p = ATOM_CONNECT_PTR(ac);
218 memmove(p + idx, p + idx + 1, sizeof(Int) * n);
220 AtomConnectResize(ac, ac->count - 1);
223 #pragma mark ====== Accessor types ======
226 MolEnumerableNew(Molecule *mol, int kind)
228 MolEnumerable *mseq = (MolEnumerable *)calloc(sizeof(MolEnumerable), 1);
230 mseq->mol = MoleculeRetain(mol);
237 MolEnumerableRelease(MolEnumerable *mseq)
240 MoleculeRelease(mseq->mol);
246 AtomRefNew(Molecule *mol, int idx)
248 AtomRef *aref = (AtomRef *)calloc(sizeof(AtomRef), 1);
250 aref->mol = MoleculeRetain(mol);
257 AtomRefRelease(AtomRef *aref)
260 MoleculeRelease(aref->mol);
265 #pragma mark ====== Creation of molecules ======
271 Molecule *mp = (Molecule *)calloc(sizeof(Molecule), 1);
273 Panic("Cannot allocate new molecule record");
274 snprintf(name, sizeof name, "Untitled %d", sMoleculeUntitledCount++);
275 ObjectInit((Object *)mp, (Object **)&sMoleculeRoot, name);
280 MoleculeNewWithName(const char *name)
282 Molecule *mp = MoleculeNew();
283 MoleculeSetName(mp, name);
288 MoleculeInitWithAtoms(Molecule *mp, const Atom *atoms, int natoms)
295 if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
296 Panic("Cannot allocate memory for atoms");
297 for (i = 0; i < natoms; i++)
298 AtomDuplicate(mp->atoms + i, atoms + i);
299 mp->nframes = -1; /* Should be recalculated later */
304 MoleculeInitWithMolecule(Molecule *mp2, const Molecule *mp)
306 MoleculeInitWithAtoms(mp2, mp->atoms, mp->natoms);
307 if (mp->nbonds > 0) {
308 if (NewArray(&mp2->bonds, &mp2->nbonds, sizeof(Int)*2, mp->nbonds) == NULL)
310 memmove(mp2->bonds, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
312 if (mp->nangles > 0) {
313 if (NewArray(&mp2->angles, &mp2->nangles, sizeof(Int)*3, mp->nangles) == NULL)
315 memmove(mp2->angles, mp->angles, sizeof(Int) * 3 * mp->nangles);
317 if (mp->ndihedrals > 0) {
318 if (NewArray(&mp2->dihedrals, &mp2->ndihedrals, sizeof(Int)*4, mp->ndihedrals) == NULL)
320 memmove(mp2->dihedrals, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
322 if (mp->nimpropers > 0) {
323 if (NewArray(&mp2->impropers, &mp2->nimpropers, sizeof(Int)*4, mp->nimpropers) == NULL)
325 memmove(mp2->impropers, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
327 if (mp->nresidues > 0) {
328 if (NewArray(&mp2->residues, &mp2->nresidues, sizeof(mp->residues[0]), mp->nresidues) == NULL)
330 memmove(mp2->residues, mp->residues, sizeof(mp->residues[0]) * mp->nresidues);
332 if (mp->cell != NULL) {
333 mp2->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
334 memmove(mp2->cell, mp->cell, sizeof(XtalCell));
337 NewArray(&(mp2->syms), &(mp2->nsyms), sizeof(Transform), mp->nsyms);
338 memmove(mp2->syms, mp->syms, sizeof(Transform) * mp2->nsyms);
340 mp2->useFlexibleCell = mp->useFlexibleCell;
341 if (mp->nframe_cells > 0) {
342 if (NewArray(&mp2->frame_cells, &mp2->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells) == NULL)
344 memmove(mp2->frame_cells, mp->frame_cells, sizeof(Vector) * 4 * mp->nframe_cells);
347 /* FIXME: should bset (basis set info) and elpot be duplicated or not? */
350 mp2->par = ParameterDuplicate(mp->par);
351 if (mp->arena != NULL) {
353 md_arena_init_from_arena(mp2->arena, mp->arena);
358 Panic("Cannot allocate memory for duplicate molecule");
359 return NULL; /* Not reached */
362 /* Assign a unique name to this parameter record */
364 MoleculeSetName(Molecule *mp, const char *name)
366 ObjectSetName((Object *)mp, name, (Object *)sMoleculeRoot);
370 MoleculeGetName(Molecule *mp)
372 return ObjectGetName((Object *)mp);
376 MoleculeWithName(const char *name)
378 return (Molecule *)ObjectWithName(name, (Object *)sMoleculeRoot);
382 MoleculeSetPath(Molecule *mol, const char *fname)
385 if (mol == NULL || fname == NULL)
387 if (fname[0] == '/' || (isalpha(fname[0]) && fname[1] == ':')) {
391 cwd = getcwd(NULL, 0);
392 asprintf(&buf, "%s/%s", cwd, fname);
395 if (mol->path != NULL) {
396 if (strcmp(mol->path, buf) == 0) {
401 free((void *)(mol->path));
404 if (mol->arena != NULL) {
405 md_close_output_files(mol->arena);
410 MoleculeGetPath(Molecule *mol)
418 MoleculeRetain(Molecule *mp)
420 ObjectIncrRefCount((Object *)mp);
421 MoleculeRetainExternalObj(mp);
426 MoleculeClear(Molecule *mp)
430 if (mp->arena != NULL) {
431 md_arena_set_molecule(mp->arena, NULL);
434 if (mp->par != NULL) {
435 ParameterRelease(mp->par);
438 if (mp->bset != NULL) {
439 BasisSetRelease(mp->bset);
442 if (mp->atoms != NULL) {
444 for (i = 0; i < mp->natoms; i++)
445 AtomClean(mp->atoms + i);
450 if (mp->bonds != NULL) {
455 if (mp->angles != NULL) {
460 if (mp->dihedrals != NULL) {
462 mp->dihedrals = NULL;
465 if (mp->impropers != NULL) {
467 mp->impropers = NULL;
470 if (mp->residues != NULL) {
475 if (mp->cell != NULL) {
479 if (mp->syms != NULL) {
484 if (mp->selection != NULL) {
485 IntGroupRelease(mp->selection);
486 mp->selection = NULL;
488 if (mp->frame_cells != NULL) {
489 free(mp->frame_cells);
490 mp->frame_cells = NULL;
491 mp->nframe_cells = 0;
493 if (mp->bset != NULL) {
494 BasisSetRelease(mp->bset);
497 if (mp->par != NULL) {
498 ParameterRelease(mp->par);
501 if (mp->elpots != NULL) {
506 if (mp->path != NULL) {
507 free((void *)mp->path);
513 MoleculeRelease(Molecule *mp)
517 MoleculeReleaseExternalObj(mp);
518 if (ObjectDecrRefCount((Object *)mp) == 0) {
520 ObjectDealloc((Object *)mp, (Object **)&sMoleculeRoot);
525 MoleculeExchange(Molecule *mp1, Molecule *mp2)
528 struct MainView *mview1, *mview2;
529 struct MDArena *arena1, *arena2;
530 /* mview and arena must be kept as they are */
535 /* 'natoms' is the first member to be copied */
536 int ofs = offsetof(Molecule, natoms);
537 memmove((char *)(&mp_temp) + ofs, (char *)mp1 + ofs, sizeof(Molecule) - ofs);
538 memmove((char *)mp1 + ofs, (char *)mp2 + ofs, sizeof(Molecule) - ofs);
539 memmove((char *)mp2 + ofs, (char *)(&mp_temp) + ofs, sizeof(Molecule) - ofs);
544 /* if (mp1->arena != NULL && mp1->arena->mol == mp2)
545 mp1->arena->mol = mp1;
546 if (mp1->arena != NULL && mp1->arena->xmol == mp2)
547 mp1->arena->xmol = mp1;
548 if (mp2->arena != NULL && mp2->arena->mol == mp1)
549 mp2->arena->mol = mp2;
550 if (mp2->arena != NULL && mp2->arena->xmol == mp1)
551 mp2->arena->xmol = mp2; */
554 #pragma mark ====== Mutex ======
557 MoleculeLock(Molecule *mol)
559 if (mol == NULL || mol->mutex == NULL)
561 MoleculeCallback_lockMutex(mol->mutex);
565 MoleculeUnlock(Molecule *mol)
567 if (mol == NULL || mol->mutex == NULL)
569 MoleculeCallback_unlockMutex(mol->mutex);
572 #pragma mark ====== Modify count ======
575 MoleculeIncrementModifyCount(Molecule *mp)
578 if (++(mp->modifyCount) == 1)
579 MoleculeCallback_notifyModification(mp, 0);
580 /* fprintf(stderr, "MoleculeIncrementModifyCount: %d\n", mp->modifyCount); */
585 MoleculeClearModifyCount(Molecule *mp)
589 /* fprintf(stderr, "MoleculeClearModifyCount: %d\n", mp->modifyCount); */
593 #pragma mark ====== File handling functions ======
596 guessMoleculeType(const char *fname)
600 const char *retval = NULL;
601 fp = fopen(fname, "rb");
603 memset(buf, 0, sizeof buf);
604 if (fread(buf, 1, sizeof buf - 1, fp) > 0) {
605 if (strncmp(buf, "PSF", 3) == 0)
607 else if (((p = strstr(buf, "ATOM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r'))
608 || ((p = strstr(buf, "HETATM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r')))
611 retval = "???"; /* unknown */
619 guessElement(Atom *ap)
621 int atomicNumber = -1;
622 if (ap->atomicNumber > 0)
623 atomicNumber = ap->atomicNumber;
625 atomicNumber = GuessAtomicNumber(ap->element, ap->weight);
626 if (atomicNumber <= 0 && ap->aname[0] != 0)
627 atomicNumber = GuessAtomicNumber(ap->aname, ap->weight);
629 if (atomicNumber >= 0) {
630 ap->atomicNumber = atomicNumber;
632 ap->weight = WeightForAtomicNumber(atomicNumber);
633 if (ap->element[0] == 0)
634 ElementToString(atomicNumber, ap->element);
640 sReadLineWithInterrupt(char *buf, int size, FILE *stream, int *lineNumber)
642 static int lastLineNumber = 0;
643 if (lineNumber != NULL) {
644 if (*lineNumber == 0)
646 else if (*lineNumber >= lastLineNumber + 1000) {
647 if (MyAppCallback_checkInterrupt() != 0)
648 return -1; /* User interrupt */
649 lastLineNumber = *lineNumber;
652 return ReadLine(buf, size, stream, lineNumber);
656 MoleculeLoadFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
659 if (ftype == NULL || *ftype == 0) {
661 cp = strrchr(fname, '.');
665 cp = guessMoleculeType(fname);
666 if (strcmp(cp, "???") != 0)
670 if (strcasecmp(ftype, "psf") == 0) {
671 retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
672 } else if (strcasecmp(ftype, "pdb") == 0) {
673 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
674 } else if (strcasecmp(ftype, "tep") == 0) {
675 retval = MoleculeLoadTepFile(mp, fname, errbuf, errbufsize);
676 } else if (strcasecmp(ftype, "res") == 0 || strcasecmp(ftype, "ins") == 0) {
677 retval = MoleculeLoadShelxFile(mp, fname, errbuf, errbufsize);
678 } else if (strcasecmp(ftype, "fchk") == 0 || strcasecmp(ftype, "fch") == 0) {
679 retval = MoleculeLoadGaussianFchkFile(mp, fname, errbuf, errbufsize);
681 snprintf(errbuf, errbufsize, "Unknown format %s", ftype);
684 /* if (retval != 0) {
685 retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
688 MoleculeSetPath(mp, fname);
693 MoleculeLoadMbsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
697 int i, j, k, n, err, fn, nframes;
706 char *bufp, *valp, *comp;
711 const int kUndefined = -10000000;
713 if (errbuf == NULL) {
718 if (mp->natoms != 0 || mp->par != NULL || mp->arena != NULL) {
719 snprintf(errbuf, errbufsize, "The molecule must be empty");
722 fp = fopen(fname, "rb");
724 snprintf(errbuf, errbufsize, "Cannot open file");
727 for (i = 0; i < 8; i++)
728 mview_fbuf[i] = kUndefined;
729 for (i = 0; i < 16; i++)
730 mview_ibuf[i] = kUndefined;
735 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
736 if (strncmp(buf, "!:", 2) != 0)
737 continue; /* Skip until section header is found */
739 strsep(&bufp, " \t\n");
740 if (strcmp(buf, "!:atoms") == 0) {
741 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
746 /* idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge */
747 if (sscanf(buf, "%d %6s %d %6s %6s %6s %lf %lf %6s %d %lf %lf %d", &ibuf[0], cbuf[0], &ibuf[1], cbuf[1], cbuf[2], cbuf[3], &dbuf[0], &dbuf[1], cbuf[4], &ibuf[2], &dbuf[2], &dbuf[3], &ibuf[3]) < 13) {
748 snprintf(errbuf, errbufsize, "line %d: coordinates cannot be read for atom %d", lineNumber, mp->natoms + 1);
751 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
752 strncpy(ap->segName, cbuf[0], 4);
753 ap->resSeq = ibuf[1];
754 strncpy(ap->resName, cbuf[1], 4);
755 strncpy(ap->aname, cbuf[2], 4);
756 ap->type = AtomTypeEncodeToUInt(cbuf[3]);
757 ap->charge = dbuf[0];
758 ap->weight = dbuf[1];
759 strncpy(ap->element, cbuf[4], 2);
760 ap->atomicNumber = ibuf[2];
761 ap->occupancy = dbuf[2];
762 ap->tempFactor = dbuf[3];
763 ap->intCharge = ibuf[3];
766 } else if (strcmp(buf, "!:atoms_symop") == 0) {
768 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
773 /* idx symop symbase */
774 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
775 snprintf(errbuf, errbufsize, "line %d: symmetry operations cannot be read for atom %d", lineNumber, i + 1);
778 if (i >= mp->natoms) {
779 snprintf(errbuf, errbufsize, "line %d: too many atomic symmetry info\n", lineNumber);
782 ap = ATOM_AT_INDEX(mp->atoms, i);
783 ap->symop.sym = ibuf[1] / 1000000;
784 ap->symop.dx = (ibuf[1] % 1000000) / 10000;
785 ap->symop.dy = (ibuf[1] % 10000) / 100;
786 ap->symop.dz = ibuf[1] % 100;
787 ap->symbase = ibuf[2];
791 } else if (strcmp(buf, "!:atoms_fix") == 0) {
793 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
798 /* idx fix_force fix_pos */
799 if (sscanf(buf, "%d %lf %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]) < 5) {
800 snprintf(errbuf, errbufsize, "line %d: fix atom info cannot be read for atom %d", lineNumber, i + 1);
803 if (i >= mp->natoms) {
804 snprintf(errbuf, errbufsize, "line %d: too many fix atom info\n", lineNumber);
807 ap = ATOM_AT_INDEX(mp->atoms, i);
808 ap->fix_force = dbuf[0];
809 ap->fix_pos.x = dbuf[1];
810 ap->fix_pos.y = dbuf[2];
811 ap->fix_pos.z = dbuf[3];
815 } else if (strcmp(buf, "!:mm_exclude") == 0) {
817 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
822 /* idx mm_exclude periodic_exclude */
823 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
824 snprintf(errbuf, errbufsize, "line %d: mm_exclude flags cannot be read for atom %d", lineNumber, i + 1);
827 if (i >= mp->natoms) {
828 snprintf(errbuf, errbufsize, "line %d: too many mm_exclude flags\n", lineNumber);
831 ap = ATOM_AT_INDEX(mp->atoms, i);
832 ap->mm_exclude = (ibuf[1] != 0);
833 ap->periodic_exclude = (ibuf[2] != 0);
837 } else if (strcmp(buf, "!:positions") == 0) {
839 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
845 if ((j = sscanf(buf, "%d %lf %lf %lf %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5])) < 4) {
846 snprintf(errbuf, errbufsize, "line %d: atom position cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
849 if (j > 4 && nframes != 0) {
850 snprintf(errbuf, errbufsize, "line %d: atom position sigma can only be given for frame 0", lineNumber);
853 if (j > 4 && j != 7) {
854 snprintf(errbuf, errbufsize, "line %d: atom position sigma cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
857 if (i >= mp->natoms) {
858 snprintf(errbuf, errbufsize, "line %d: too many atom position records\n", lineNumber);
864 ap = ATOM_AT_INDEX(mp->atoms, i);
866 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes, &v);
868 ap->frames[0] = ap->r;
872 ap->sigma.x = dbuf[3];
873 ap->sigma.y = dbuf[4];
874 ap->sigma.z = dbuf[5];
880 mp->nframes = nframes;
881 mp->cframe = nframes - 1;
883 mp->nframes = mp->cframe = 0;
886 } else if (strcmp(buf, "!:bonds") == 0) {
887 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
892 /* from1 to1 from2 to2 from3 to3 from4 to4 */
893 i = sscanf(buf, "%d %d %d %d %d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3], &ibuf[4], &ibuf[5], &ibuf[6], &ibuf[7]);
894 if (i < 2 || i % 2 != 0) {
895 snprintf(errbuf, errbufsize, "line %d: bad bond format", lineNumber);
898 for (j = 0; j < i; j += 2) {
900 iibuf[1] = ibuf[j + 1];
901 if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[0] == iibuf[1]) {
902 snprintf(errbuf, errbufsize, "line %d: bad bond format", lineNumber);
905 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, iibuf);
906 for (k = 0; k < 2; k++) {
908 ap = ATOM_AT_INDEX(mp->atoms, iibuf[k]);
909 cp = AtomConnectData(&ap->connect);
910 for (n = 0; n < ap->connect.count; n++, cp++) {
911 if (*cp == iibuf[1 - k])
914 if (n >= ap->connect.count) {
915 AtomConnectInsertEntry(&ap->connect, ap->connect.count, iibuf[1 - k]);
921 } else if (strcmp(buf, "!:angles") == 0) {
922 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
927 /* a1 b1 c1 a2 b2 c2 a3 b3 c3 */
928 i = sscanf(buf, "%d %d %d %d %d %d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3], &ibuf[4], &ibuf[5], &ibuf[6], &ibuf[7], &ibuf[8]);
929 if (i == 0 || i % 3 != 0) {
930 snprintf(errbuf, errbufsize, "line %d: bad angle format", lineNumber);
933 for (j = 0; j < i; j += 3) {
935 iibuf[1] = ibuf[j + 1];
936 iibuf[2] = ibuf[j + 2];
937 if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[2] < 0 || iibuf[2] >= mp->natoms || iibuf[0] == iibuf[1] || iibuf[1] == iibuf[2]) {
938 snprintf(errbuf, errbufsize, "line %d: bad angle format", lineNumber);
941 AssignArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, mp->nangles, iibuf);
945 } else if (strcmp(buf, "!:dihedrals") == 0) {
946 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
951 /* a1 b1 c1 d1 a2 b2 c2 d2 */
952 i = sscanf(buf, "%d %d %d %d %d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3], &ibuf[4], &ibuf[5], &ibuf[6], &ibuf[7]);
953 if (i == 0 || i % 4 != 0) {
954 snprintf(errbuf, errbufsize, "line %d: bad dihedral format", lineNumber);
957 for (j = 0; j < i; j += 4) {
959 iibuf[1] = ibuf[j + 1];
960 iibuf[2] = ibuf[j + 2];
961 iibuf[3] = ibuf[j + 3];
962 if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[2] < 0 || iibuf[2] >= mp->natoms || iibuf[3] < 0 || iibuf[3] >= mp->natoms || iibuf[0] == iibuf[1] || iibuf[1] == iibuf[2] || iibuf[2] == iibuf[3] || iibuf[0] == iibuf[2] || iibuf[1] == iibuf[3] || iibuf[0] == iibuf[3]) {
963 snprintf(errbuf, errbufsize, "line %d: bad dihedral format", lineNumber);
966 AssignArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, mp->ndihedrals, iibuf);
970 } else if (strcmp(buf, "!:impropers") == 0) {
971 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
976 /* a1 b1 c1 d1 a2 b2 c2 d2 */
977 i = sscanf(buf, "%d %d %d %d %d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3], &ibuf[4], &ibuf[5], &ibuf[6], &ibuf[7]);
978 if (i == 0 || i % 4 != 0) {
979 snprintf(errbuf, errbufsize, "line %d: bad improper format", lineNumber);
982 for (j = 0; j < i; j += 4) {
984 iibuf[1] = ibuf[j + 1];
985 iibuf[2] = ibuf[j + 2];
986 iibuf[3] = ibuf[j + 3];
987 if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[2] < 0 || iibuf[2] >= mp->natoms || iibuf[3] < 0 || iibuf[3] >= mp->natoms || iibuf[0] == iibuf[1] || iibuf[1] == iibuf[2] || iibuf[2] == iibuf[3] || iibuf[0] == iibuf[2] || iibuf[1] == iibuf[3] || iibuf[0] == iibuf[3]) {
988 snprintf(errbuf, errbufsize, "line %d: bad improper format", lineNumber);
991 AssignArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, mp->nimpropers, iibuf);
995 } else if (strcmp(buf, "!:xtalcell") == 0 && mp->cell == NULL) {
996 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1001 /* a b c alpha beta gamma [sigmaflag] */
1002 if ((j = sscanf(buf, "%lf %lf %lf %lf %lf %lf %d", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5], &ibuf[0])) < 6) {
1003 snprintf(errbuf, errbufsize, "line %d: bad xtalcell format", lineNumber);
1006 MoleculeSetCell(mp, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], 0);
1007 if (j == 7 && ibuf[0] != 0) {
1008 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1009 snprintf(errbuf, errbufsize, "line %d: sigma for xtalcell are missing", lineNumber);
1012 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1013 snprintf(errbuf, errbufsize, "line %d: bad xtalcell sigma format", lineNumber);
1016 if (mp->cell != NULL) {
1017 mp->cell->has_sigma = 1;
1018 for (i = 0; i < 6; i++) {
1019 mp->cell->cellsigma[i] = dbuf[i];
1022 snprintf(errbuf, errbufsize, "line %d: cell sigma are given while cell is not given", lineNumber);
1027 } else if (strcmp(buf, "!:symmetry_operations") == 0) {
1029 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1035 /* a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3 */
1036 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1037 snprintf(errbuf, errbufsize, "line %d: bad symmetry_operation format", lineNumber);
1042 tr[i + 3] = dbuf[1];
1043 tr[i + 6] = dbuf[2];
1051 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1056 } else if (strcmp(buf, "!:anisotropic_thermal_parameters") == 0) {
1058 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1063 /* b11 b22 b33 b12 b13 b23 [has_sigma] */
1064 if ((j = sscanf(buf, "%lf %lf %lf %lf %lf %lf %d", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5], &ibuf[0])) < 6) {
1065 snprintf(errbuf, errbufsize, "line %d: anisotropic thermal parameters cannot be read for atom %d", lineNumber, i + 1);
1068 if (i >= mp->natoms) {
1069 snprintf(errbuf, errbufsize, "line %d: too many anisotropic thermal parameters\n", lineNumber);
1072 if (dbuf[0] == 0.0 && dbuf[1] == 0.0 && dbuf[2] == 0.0 && dbuf[3] == 0.0 && dbuf[4] == 0.0 && dbuf[5] == 0.0) {
1075 MoleculeSetAniso(mp, i, 0, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], NULL);
1077 if (j == 7 && ibuf[0] != 0) {
1078 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1079 snprintf(errbuf, errbufsize, "line %d: anisotropic thermal parameters sigma missing", lineNumber);
1082 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1083 snprintf(errbuf, errbufsize, "line %d: anisotropic thermal parameters sigma cannot be read for atom %d", lineNumber, i + 1);
1086 ap = ATOM_AT_INDEX(mp->atoms, i);
1087 if (ap->aniso == NULL) {
1088 snprintf(errbuf, errbufsize, "line %d: anisotropic thermal parameters sigma are given while the parameters are not given", lineNumber);
1091 ap->aniso->has_bsig = 1;
1092 for (j = 0; j < 6; j++)
1093 ap->aniso->bsig[j] = dbuf[j];
1098 } else if (strcmp(buf, "!:periodic_box") == 0) {
1102 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1107 /* ax ay az; bx by bz; cx cy cz; ox oy oz; fx fy fz [sigma; sa sb sc s_alpha s_beta s_gamma] */
1109 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1110 snprintf(errbuf, errbufsize, "line %d: bad periodic_box format", lineNumber);
1119 if ((j = sscanf(buf, "%d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3])) < 3) {
1120 snprintf(errbuf, errbufsize, "line %d: bad periodic_box format", lineNumber);
1123 if (j == 4 && ibuf[3] != 0)
1125 cbuf[0][0] = ibuf[0];
1126 cbuf[0][1] = ibuf[1];
1127 cbuf[0][2] = ibuf[2];
1128 MoleculeSetPeriodicBox(mp, vs, vs + 1, vs + 2, vs + 3, cbuf[0], 0);
1130 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1131 snprintf(errbuf, errbufsize, "line %d: sigma for cell parameters are missing", lineNumber);
1134 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1135 snprintf(errbuf, errbufsize, "line %d: bad periodic_box sigma format", lineNumber);
1138 if (mp->cell != NULL) {
1139 mp->cell->has_sigma = 1;
1140 for (i = 0; i < 6; i++) {
1141 mp->cell->cellsigma[i] = dbuf[i];
1144 snprintf(errbuf, errbufsize, "line %d: cell sigma are given while cell is not given", lineNumber);
1150 } else if (strcmp(buf, "!:frame_periodic_boxes") == 0) {
1153 mp->useFlexibleCell = 1; /* The presence of this block causes asserting this flag */
1154 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1159 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1160 snprintf(errbuf, errbufsize, "line %d: bad frame_periodic_box format", lineNumber);
1168 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells, vs);
1172 if (mp->cframe < mp->nframe_cells) {
1173 /* mp->cframe should already have been set when positions are read */
1174 Vector *vp = &mp->frame_cells[mp->cframe * 4];
1175 static char defaultFlags[] = {1, 1, 1};
1176 char *flags = (mp->cell != NULL ? mp->cell->flags : defaultFlags);
1177 MoleculeSetPeriodicBox(mp, vp, vp + 1, vp + 2, vp + 3, flags, 0);
1180 } else if (strcmp(buf, "!:md_parameters") == 0) {
1182 if (mp->arena == NULL)
1183 mp->arena = md_arena_new(NULL);
1185 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1191 comp = strsep(&bufp, " \t");
1193 while (*bufp == ' ' || *bufp == '\t')
1195 valp = strsep(&bufp, "\n");
1197 if (strcmp(comp, "alchem_flags") == 0) {
1198 j = (valp == NULL ? 0 : atoi(valp));
1200 valp = (char *)malloc(j);
1202 while ((k = fgetc(fp)) >= 0) {
1204 if (k < '0' || k > '9') {
1205 snprintf(errbuf, errbufsize, "line %d: too few flags in alchem_flags block", lineNumber + 1);
1209 ReadLine(buf, sizeof buf, fp, &lineNumber);
1211 while (*bufp != 0) {
1212 if (*bufp >= '0' && *bufp <= '2') {
1214 snprintf(errbuf, errbufsize, "line %d: too many flags in alchem_flags block", lineNumber);
1218 valp[i++] = *bufp - '0';
1219 } else if (*bufp != ' ' && *bufp != '\t' && *bufp != '\n') {
1220 snprintf(errbuf, errbufsize, "line %d: strange character (0x%02x) in alchem_flags block", lineNumber, (int)*bufp);
1229 md_set_alchemical_flags(arena, j, valp);
1234 /* In the following, the redundant "!= NULL" is to suppress suprious warning */
1235 if ((strcmp(comp, "log_file") == 0 && (pp = &arena->log_result_name) != NULL)
1236 || (strcmp(comp, "coord_file") == 0 && (pp = &arena->coord_result_name) != NULL)
1237 || (strcmp(comp, "vel_file") == 0 && (pp = &arena->vel_result_name) != NULL)
1238 || (strcmp(comp, "force_file") == 0 && (pp = &arena->force_result_name) != NULL)
1239 || (strcmp(comp, "debug_file") == 0 && (pp = &arena->debug_result_name) != NULL)) {
1240 if (*valp == 0 || strstr(valp, "(null)") == valp)
1243 valp = strdup(valp);
1245 char *valp1 = strchr(valp, '\n');
1251 } else if ((strcmp(comp, "debug_output_level") == 0 && (ip = &arena->debug_output_level) != NULL)
1252 || (strcmp(comp, "coord_output_freq") == 0 && (ip = &arena->coord_output_freq) != NULL)
1253 || (strcmp(comp, "energy_output_freq") == 0 && (ip = &arena->energy_output_freq) != NULL)
1254 || (strcmp(comp, "coord_frame") == 0 && (ip = &arena->coord_result_frame) != NULL)
1255 || (strcmp(comp, "andersen_freq") == 0 && (ip = &arena->andersen_thermo_freq) != NULL)
1256 || (strcmp(comp, "random_seed") == 0 && (ip = &arena->random_seed) != NULL)
1257 || (strcmp(comp, "use_xplor_shift") == 0 && (ip = &arena->use_xplor_shift) != NULL)
1258 || (strcmp(comp, "relocate_center") == 0 && (ip = &arena->relocate_center) != NULL)
1259 || (strcmp(comp, "surface_potential_freq") == 0 && (ip = &arena->surface_potential_freq) != NULL)
1260 || (strcmp(comp, "use_graphite") == 0 && (ip = &arena->use_graphite) != NULL)) {
1261 *ip = (valp == NULL ? 0 : atoi(valp));
1262 } else if ((strcmp(comp, "timestep") == 0 && (dp = &arena->timestep) != NULL)
1263 || (strcmp(comp, "cutoff") == 0 && (dp = &arena->cutoff) != NULL)
1264 || (strcmp(comp, "electro_cutoff") == 0 && (dp = &arena->electro_cutoff) != NULL)
1265 || (strcmp(comp, "pairlist_distance") == 0 && (dp = &arena->pairlist_distance) != NULL)
1266 || (strcmp(comp, "temperature") == 0 && (dp = &arena->temperature) != NULL)
1267 || (strcmp(comp, "andersen_coupling") == 0 && (dp = &arena->andersen_thermo_coupling) != NULL)
1268 || (strcmp(comp, "dielectric") == 0 && (dp = &arena->dielectric) != NULL)
1269 || (strcmp(comp, "gradient_convergence") == 0 && (dp = &arena->gradient_convergence) != NULL)
1270 || (strcmp(comp, "coordinate_convergence") == 0 && (dp = &arena->coordinate_convergence) != NULL)
1271 || (strcmp(comp, "scale14_vdw") == 0 && (dp = &arena->scale14_vdw) != NULL)
1272 || (strcmp(comp, "scale14_elect") == 0 && (dp = &arena->scale14_elect) != NULL)
1273 || (strcmp(comp, "surface_probe_radius") == 0 && (dp = &arena->probe_radius) != NULL)
1274 || (strcmp(comp, "surface_tension") == 0 && (dp = &arena->surface_tension) != NULL)
1275 || (strcmp(comp, "alchemical_lambda") == 0 && (dp = &arena->alchem_lambda) != NULL)
1276 || (strcmp(comp, "alchemical_delta_lambda") == 0 && (dp = &arena->alchem_dlambda) != NULL)) {
1277 *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1281 } else if (strcmp(buf, "!:pressure_control_parameters") == 0) {
1282 MDPressureArena *pressure;
1283 if (mp->arena == NULL)
1284 mp->arena = md_arena_new(mp);
1285 if (mp->arena->pressure == NULL)
1286 mp->arena->pressure = pressure_new();
1287 pressure = mp->arena->pressure;
1288 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1294 comp = strsep(&bufp, " \t");
1296 while (*bufp == ' ' || *bufp == '\t')
1298 valp = strsep(&bufp, "\n");
1300 if (strcmp(comp, "pressure") == 0) {
1301 if (sscanf(valp, "%lf %lf %lf %lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5], &dbuf[6], &dbuf[7], &dbuf[8]) < 9) {
1302 snprintf(errbuf, errbufsize, "line %d: bad format", lineNumber);
1305 for (i = 0; i < 9; i++)
1306 pressure->apply[i] = dbuf[i];
1307 } else if (strcmp(comp, "pressure_cell_flexibility") == 0) {
1308 if (sscanf(valp, "%lf %lf %lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5], &dbuf[6], &dbuf[7]) < 8) {
1309 snprintf(errbuf, errbufsize, "line %d: bad format", lineNumber);
1312 for (i = 0; i < 8; i++)
1313 pressure->cell_flexibility[i] = dbuf[i];
1314 } else if ((strcmp(comp, "pressure_freq") == 0 && (ip = &pressure->freq) != NULL)) {
1315 *ip = (valp == NULL ? 0 : atoi(valp));
1316 } else if ((strcmp(comp, "pressure_coupling") == 0 && (dp = &pressure->coupling) != NULL)
1317 || (strcmp(comp, "pressure_fluctuate_cell_origin") == 0 && (dp = &pressure->fluctuate_cell_origin) != NULL)
1318 || (strcmp(comp, "pressure_fluctuate_cell_orientation") == 0 && (dp = &pressure->fluctuate_cell_orientation) != NULL)) {
1319 *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1323 } else if (strcmp(buf, "!:velocity") == 0) {
1325 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1331 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1332 snprintf(errbuf, errbufsize, "line %d: atom velocity cannot be read for atom %d", lineNumber, i + 1);
1335 if (i >= mp->natoms) {
1336 snprintf(errbuf, errbufsize, "line %d: too many atom velocity records\n", lineNumber);
1339 ap = ATOM_AT_INDEX(mp->atoms, i);
1346 } else if (strcmp(buf, "!:force") == 0) {
1348 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1354 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1355 snprintf(errbuf, errbufsize, "line %d: atom force cannot be read for atom %d", lineNumber, i + 1);
1358 if (i >= mp->natoms) {
1359 snprintf(errbuf, errbufsize, "line %d: too many atom force records\n", lineNumber);
1362 ap = ATOM_AT_INDEX(mp->atoms, i);
1369 } else if (strcmp(buf, "!:parameter") == 0 || strcmp(buf, "!:parameters") == 0) {
1370 Parameter *par = mp->par;
1372 mp->par = ParameterNew();
1377 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1382 j = ParameterReadFromString(par, buf, &bufp, fname, lineNumber, 0);
1384 snprintf(errbuf, errbufsize, "%s", bufp);
1390 MyAppCallback_setConsoleColor(1);
1391 MyAppCallback_showScriptMessage("%s", bufp);
1392 MyAppCallback_setConsoleColor(0);
1396 } else if (strcmp(buf, "!:trackball") == 0) {
1398 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1403 /* scale; trx try trz; theta_deg x y z */
1404 if ((i == 0 && sscanf(buf, "%f", &mview_fbuf[0]) < 1)
1405 || (i == 1 && sscanf(buf, "%f %f %f",
1406 &mview_fbuf[1], &mview_fbuf[2], &mview_fbuf[3]) < 3)
1407 || (i == 2 && sscanf(buf, "%f %f %f %f",
1408 &mview_fbuf[4], &mview_fbuf[5], &mview_fbuf[6], &mview_fbuf[7]) < 4)) {
1409 snprintf(errbuf, errbufsize, "line %d: bad trackball format", lineNumber);
1415 } else if (strcmp(buf, "!:view") == 0) {
1416 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1422 comp = strsep(&bufp, " \t");
1424 while (*bufp == ' ' || *bufp == '\t')
1426 valp = strsep(&bufp, "\n");
1428 /* In the following, the redundant "!= NULL" is to suppress suprious warning */
1429 if ((strcmp(comp, "show_unit_cell") == 0 && (i = 1))
1430 || (strcmp(comp, "show_periodic_box") == 0 && (i = 2))
1431 || (strcmp(comp, "show_expanded_atoms") == 0 && (i = 3))
1432 || (strcmp(comp, "show_ellipsoids") == 0 && (i = 4))
1433 || (strcmp(comp, "show_hydrogens") == 0 && (i = 5))
1434 || (strcmp(comp, "show_dummy_atoms") == 0 && (i = 6))
1435 || (strcmp(comp, "show_rotation_center") == 0 && (i = 7))
1436 || (strcmp(comp, "show_graphite_flag") == 0 && (i = 8))
1437 || (strcmp(comp, "show_periodic_image_flag") == 0 && (i = 9))
1438 || (strcmp(comp, "show_graphite") == 0 && (i = 10))) {
1439 mview_ibuf[i - 1] = atoi(valp);
1440 } else if (strcmp(comp, "show_periodic_image") == 0) {
1441 sscanf(valp, "%d %d %d %d %d %d",
1442 &mview_ibuf[10], &mview_ibuf[11], &mview_ibuf[12],
1443 &mview_ibuf[13], &mview_ibuf[14], &mview_ibuf[15]);
1448 /* Unknown sections are silently ignored */
1451 MoleculeCleanUpResidueTable(mp);
1452 if (mp->arena != NULL)
1453 md_arena_set_molecule(mp->arena, mp);
1457 if (errbuf[0] != 0) {
1458 /* The content of mp may be broken, so make it empty */
1462 MainView *mview = mp->mview;
1463 if (mview != NULL) {
1464 if (mview_ibuf[0] != kUndefined)
1465 mview->showUnitCell = mview_ibuf[0];
1466 if (mview_ibuf[1] != kUndefined)
1467 mview->showPeriodicBox = mview_ibuf[1];
1468 if (mview_ibuf[2] != kUndefined)
1469 mview->showExpandedAtoms = mview_ibuf[2];
1470 if (mview_ibuf[3] != kUndefined)
1471 mview->showEllipsoids = mview_ibuf[3];
1472 if (mview_ibuf[4] != kUndefined)
1473 mview->showHydrogens = mview_ibuf[4];
1474 if (mview_ibuf[5] != kUndefined)
1475 mview->showDummyAtoms = mview_ibuf[5];
1476 if (mview_ibuf[6] != kUndefined)
1477 mview->showRotationCenter = mview_ibuf[6];
1478 if (mview_ibuf[7] != kUndefined)
1479 mview->showGraphiteFlag = mview_ibuf[7];
1480 if (mview_ibuf[8] != kUndefined)
1481 mview->showPeriodicImageFlag = mview_ibuf[8];
1482 if (mview_ibuf[9] != kUndefined)
1483 mview->showGraphite = mview_ibuf[9];
1484 for (i = 0; i < 6; i++) {
1485 if (mview_ibuf[10 + i] != kUndefined)
1486 mview->showPeriodicImage[i] = mview_ibuf[10 + i];
1488 if (mview->track != NULL) {
1489 if (mview_fbuf[0] != kUndefined)
1490 TrackballSetScale(mview->track, mview_fbuf[0]);
1491 if (mview_fbuf[1] != kUndefined)
1492 TrackballSetTranslate(mview->track, mview_fbuf + 1);
1493 if (mview_fbuf[4] != kUndefined)
1494 TrackballSetRotate(mview->track, mview_fbuf + 4);
1502 MoleculeLoadPsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
1511 Vector *frames = NULL;
1514 if (errbuf == NULL) {
1521 fp = fopen(fname, "rb");
1523 snprintf(errbuf, errbufsize, "Cannot open file");
1526 /* flockfile(fp); */
1529 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1530 if (strncmp(buf, "PSF", 3) == 0) {
1534 for (p = buf; *p != 0 && isspace(*p); p++) {}
1540 if (strstr(buf, "!COORD") != NULL) {
1541 /* Extended psf file with coordinates */
1543 /* Allocate a temporary storage for frames */
1544 size_t size = sizeof(Vector) * mp->natoms * fn;
1546 frames = (Vector *)malloc(size);
1548 frames = (Vector *)realloc(frames, size);
1553 /* Copy the coordinates of the first frame */
1554 for (i = 0; i < mp->natoms; i++) {
1555 ap = ATOM_AT_INDEX(mp->atoms, i);
1559 /* Copy the coordinates of the last frame to the newly created frame */
1560 memmove(frames + sizeof(Vector) * mp->natoms * fn, frames + sizeof(Vector) * mp->natoms * (fn - 1), sizeof(Vector) * mp->natoms);
1563 /* Read coordinates */
1564 for (i = 0; i < mp->natoms; i++) {
1567 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1569 snprintf(errbuf, errbufsize, "line %d: premature end of file while reading coordinates (frame %d)", lineNumber, fn);
1572 if (sscanf(buf, "%lg %lg %lg", dval, dval + 1, dval + 2) != 3) {
1574 snprintf(errbuf, errbufsize, "line %d: coordinates cannot be read for atom %d", lineNumber, i + 1);
1581 ATOM_AT_INDEX(mp->atoms, i)->r = r;
1583 frames[mp->natoms * (fn - 1) + i] = r;
1592 ReadFormat(buf, "I8", &natoms);
1593 if (mp->atoms != NULL)
1595 if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
1598 for (i = 0; i < natoms; i++) {
1600 char segName[5], resName[4], atomName[5], atomType[3], element[3];
1603 memset(&w, 0, sizeof(w));
1604 ap = ATOM_AT_INDEX(mp->atoms, i);
1605 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1607 snprintf(errbuf, errbufsize, "line %d: premature end of file while reading atoms", lineNumber);
1610 ReadFormat(buf, "I8 x1 S4 I5 x1 S3 x2 S4 x1 S4 F16 F10",
1611 &w.serial, w.segName, &ap->resSeq, w.resName, w.atomName,
1612 w.atomType, &ap->charge, &ap->weight);
1613 strncpy(ap->segName, w.segName, 4);
1614 strncpy(ap->resName, w.resName, 3);
1615 strncpy(ap->aname, w.atomName, 4);
1616 ap->type = AtomTypeEncodeToUInt(w.atomType);
1617 /* $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl */
1618 ap->atomicNumber = GuessAtomicNumber(w.atomName, ap->weight);
1619 ElementToString(ap->atomicNumber, w.element);
1620 strncpy(ap->element, w.element, 2);
1621 /* w.element[0] = 0;
1622 for (p = w.atomName; *p != 0; p++) {
1623 if (isalpha(*p) && *p != '_') {
1624 w.element[0] = toupper(*p);
1625 if (isalpha(p[1]) && p[1] != '_') {
1626 w.element[1] = toupper(p[1]);
1634 strncpy(ap->element, w.element, 2);
1635 ap->atomicNumber = ElementToInt(w.element); */
1636 if (w.resName[0] == 0)
1637 strncpy(ap->resName, "XXX", 3);
1638 if (ap->resSeq > mp->nresidues)
1639 mp->nresidues = ap->resSeq;
1641 if (mp->residues != NULL)
1643 if (NewArray(&mp->residues, &mp->nresidues, sizeof(char (*)[4]), mp->nresidues + 1) == 0)
1645 for (i = 0; i < mp->natoms; i++) {
1646 j = mp->atoms[i].resSeq;
1647 if (mp->residues[j][0] == 0)
1648 strncpy(mp->residues[j], mp->atoms[i].resName, 4);
1651 } else if (section == 3) {
1655 ReadFormat(buf, "I8", &nbonds);
1656 if (mp->bonds != NULL)
1658 if (NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, nbonds) == NULL)
1661 for (i = 0; i < nbonds; i += 4) {
1662 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1663 snprintf(errbuf, errbufsize, "line %d: premature end of file while reading bonds", lineNumber);
1667 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1668 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1669 for (j = 0; j < 4 && i + j < nbonds; j++) {
1672 b1 = ibuf[j * 2] - 1; /* Internal atom number is 0-based */
1673 b2 = ibuf[j * 2 + 1] - 1;
1674 if (b1 < 0 || b1 >= mp->natoms || b2 < 0 || b2 >= mp->natoms) {
1675 snprintf(errbuf, errbufsize, "line %d: The bond %d-%d includes non-existent atom", lineNumber, b1+1, b2+1);
1681 ap = ATOM_AT_INDEX(mp->atoms, b1);
1682 AtomConnectInsertEntry(&ap->connect, ap->connect.count, b2);
1683 ap = ATOM_AT_INDEX(mp->atoms, b2);
1684 AtomConnectInsertEntry(&ap->connect, ap->connect.count, b1);
1688 } else if (section == 4) {
1692 ReadFormat(buf, "I8", &nangles);
1693 if (mp->angles != NULL)
1695 if (NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, nangles) == NULL)
1698 for (i = 0; i < nangles; i += 3) {
1699 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1700 snprintf(errbuf, errbufsize, "line %d: premature end of file while reading angles", lineNumber);
1704 ReadFormat(buf, "I8I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1705 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7, ibuf + 8);
1706 for (j = 0; j < 3 && i + j < nangles; j++) {
1708 a1 = ibuf[j * 3] - 1; /* Internal atom number is 0-based */
1709 a2 = ibuf[j * 3 + 1] - 1;
1710 a3 = ibuf[j * 3 + 2] - 1;
1711 if (a1 < 0 || a1 >= mp->natoms || a2 < 0 || a2 >= mp->natoms || a3 < 0 || a3 >= mp->natoms) {
1712 snprintf(errbuf, errbufsize, "line %d: The angle %d-%d-%d includes non-existent atom", lineNumber, a1+1, a2+1, a3+1);
1722 } else if (section == 5 || section == 6) {
1723 /* Dihedrals and Impropers */
1726 ReadFormat(buf, "I8", &ndihedrals);
1728 if (NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, ndihedrals) == NULL)
1732 if (NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, ndihedrals) == NULL)
1736 for (i = 0; i < ndihedrals; i += 2) {
1737 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1739 snprintf(errbuf, errbufsize, "line %d: premature end of file while reading %s", lineNumber, (section == 5 ? "dihedral" : "improper"));
1743 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3, ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1744 for (j = 0; j < 2 && i + j < ndihedrals; j++) {
1746 d1 = ibuf[j * 4] - 1; /* Internal atom number is 0-based */
1747 d2 = ibuf[j * 4 + 1] - 1;
1748 d3 = ibuf[j * 4 + 2] - 1;
1749 d4 = ibuf[j * 4 + 3] - 1;
1750 if (d1 < 0 || d1 >= mp->natoms || d2 < 0 || d2 >= mp->natoms || d3 < 0 || d3 >= mp->natoms || d4 < 0 || d4 >= mp->natoms) {
1751 snprintf(errbuf, errbufsize, "line %d: The %s %d-%d-%d-%d angle includes non-existent atom", lineNumber, (section == 5 ? "dihedral" : "improper"), d1+1, d2+1, d3+1, d4+1);
1765 /* Create frames for each atom if necessary */
1767 for (i = 0; i < mp->natoms; i++) {
1768 ap = ATOM_AT_INDEX(mp->atoms, i);
1769 ap->frames = (Vector *)malloc(sizeof(Vector) * fn);
1770 if (ap->frames == NULL)
1773 for (j = 0; j < fn; j++)
1774 ap->frames[j] = frames[mp->natoms * j + i];
1781 /* funlockfile(fp); */
1783 mp->nframes = -1; /* Should be recalculated later */
1786 else if (section == -1)
1790 Panic("low memory while reading structure file %s", fname);
1791 return 1; /* not reached */
1794 /* ("-x", "y", "-z+0.5") -> (-1,0,0,0,0,1,0,0,0,0,-1,0.5) */
1796 sMoleculeSymopStringsToTransform(char **symops, Transform tr)
1800 memset(tr, 0, sizeof(Transform));
1801 for (i = 0; i < 3; i++) {
1805 while (*symop != 0) {
1807 while (isspace(*symop))
1809 if (*symop == 0 || *symop == '\r' || *symop == 'n')
1811 if (*symop == '-') {
1814 } else if (*symop == '+') {
1818 while (isspace(*symop))
1820 if (*symop == '.' || isdigit(*symop)) {
1821 /* Numerical offset */
1822 double d = strtod(symop, &symop);
1823 if (*symop == '/') {
1824 double dd = strtod(symop + 1, &symop);
1828 return 1; /* Bad format */
1831 } else if (*symop == 'x' || *symop == 'X') {
1834 } else if (*symop == 'y' || *symop == 'Y') {
1837 } else if (*symop == 'z' || *symop == 'Z') {
1840 } else return 1; /* Bad format */
1841 } /* end while (*symop != 0) */
1847 sMoleculeGenerateSymopWithTransform(Molecule *mp, Transform gtr, int num)
1853 for (i = 0; i < num; i++) {
1854 memmove(tr, mp->syms[i], sizeof(Transform));
1855 TransformMul(tr, gtr, tr);
1856 for (j = 9; j < 12; j++) {
1859 else if (tr[j] <= 0.0)
1862 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1869 char *p = buf + strlen(buf) - 1;
1870 if (p >= buf && (*p == '\n' || *p == '\r')) {
1872 if (--p >= buf && (*p == '\n' || *p == '\r')) {
1880 MoleculeLoadTepFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
1890 if (errbuf == NULL) {
1897 fp = fopen(fname, "rb");
1899 snprintf(errbuf, errbufsize, "Cannot open file");
1903 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1904 if (section == -1) {
1911 ReadFormat(buf, "I1F8F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5);
1913 MoleculeSetCell(mp, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], 0);
1920 if (cellType == 0) {
1921 ReadFormat(buf, "I1F14F3F3F3F15F3F3F3F15F3F3F3", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6, fbuf+7, fbuf+8, fbuf+9, fbuf+10, fbuf+11);
1935 char *symops[3], *brks;
1937 memset(tr, 0, sizeof(Transform));
1938 ReadFormat(buf, "I1", ibuf);
1939 symops[0] = strtok_r(buf + 1, ", ", &brks);
1940 symops[1] = strtok_r(NULL, ", ", &brks);
1941 symops[2] = strtok_r(NULL, ", ", &brks);
1942 if (sMoleculeSymopStringsToTransform(symops, tr)) {
1943 snprintf(errbuf, errbufsize, "line %d: bad symmetry specification", lineNumber);
1947 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
1953 if (section == 2) { /* Atoms */
1957 int atomIndex = mp->natoms;
1958 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
1959 memset(ap, 0, gSizeOfAtomRecord);
1960 ReadFormat(buf, "S6x3x9x9F9F9F9F9", name, fbuf, fbuf+1, fbuf+2, fbuf+3);
1961 strncpy(ap->aname, name, 4);
1965 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
1966 /* ap->atomicNumber = AtomNameToElement(ap->name);
1967 ElementToString(ap->atomicNumber, ap->element); */
1968 /* sAtomSetElement(ap, -1, ap->name); */
1971 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1972 snprintf(errbuf, errbufsize, "unexpected end of file");
1975 ReadFormat(buf, "I1F8F9F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
1977 if ((atomType >= 0 && atomType <= 5) || (atomType >= 8 && atomType <= 10)) {
1978 /* Anisotropic thermal parameters */
1979 MoleculeSetAniso(mp, atomIndex, atomType, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[5], fbuf[4], NULL);
1987 MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
1989 MoleculeAddBonds(mp, nbonds, bonds);
1992 mp->nframes = -1; /* Should be recalculated later */
1995 Panic("low memory while reading structure file %s", fname);
1996 return -1; /* not reached */
2000 MoleculeLoadShelxFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
2008 int currentResSeq = 0;
2009 char currentResName[6];
2016 char (*sfacs)[4] = NULL;
2018 if (errbuf == NULL) {
2025 currentResName[0] = 0;
2026 fp = fopen(fname, "rb");
2028 snprintf(errbuf, errbufsize, "Cannot open file");
2032 tr[0] = tr[4] = tr[8] = 1;
2033 tr[1] = tr[2] = tr[3] = tr[5] = tr[6] = tr[7] = tr[9] = tr[10] = tr[11] = 0;
2034 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), 0, tr) == 0)
2036 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2037 if (strncmp(buf, "CELL", 4) == 0) {
2039 sscanf(buf + 4, " %f %f %f %f %f %f %f", fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2040 MoleculeSetCell(mp, fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], fbuf[6], 0);
2042 } else if (strncmp(buf, "SFAC", 4) == 0) {
2044 for (p1 = strtok_r(buf + 4, " ", &p2); p1 != NULL; p1 = strtok_r(NULL, " ", &p2)) {
2045 char *pp = (char *)AssignArray(&sfacs, &nsfacs, 4, nsfacs, NULL);
2052 } else if (strncmp(buf, "LATT", 4) == 0) {
2053 sscanf(buf + 4, " %d", &latticeType);
2055 } else if (strncmp(buf, "SYMM", 4) == 0) {
2056 char *symops[3], *brks;
2057 memset(tr, 0, sizeof(Transform));
2058 // ReadFormat(buf + 4, "I1", ibuf);
2060 symops[0] = strtok_r(buf + 4, ",", &brks);
2061 symops[1] = strtok_r(NULL, ",", &brks);
2062 symops[2] = strtok_r(NULL, ",", &brks);
2063 if (sMoleculeSymopStringsToTransform(symops, tr)) {
2064 snprintf(errbuf, errbufsize, "line %d: bad symmetry specification", lineNumber);
2067 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2070 } else if (strncmp(buf, "RESI", 4) == 0) {
2071 for (p1 = buf + 4; isspace(*p1); p1++);
2073 for (p2 = p1 + 1; isalnum(*p2); p2++);
2075 strncpy(currentResName, p1, 4);
2076 currentResName[4] = 0;
2078 } else currentResName[0] = 0;
2079 sscanf(buf + 4, " %d", ¤tResSeq);
2082 /* Atom name: [A-Za-z]{1,2}[0-9]* */
2083 for (p1 = buf; p1 < buf + 2 && (isalpha(*p1) && *p1 != '_'); p1++);
2085 while (isdigit(*p1))
2088 if (p1 > buf && p1 <= buf + 4 && isspace(*p1)) {
2092 int atomIndex = mp->natoms;
2093 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2094 memset(ap, 0, gSizeOfAtomRecord);
2095 strncpy(ap->aname, buf, 4);
2096 n = sscanf(p1, " %d %f %f %f %f %f %f %2s", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, cont);
2097 if (n == 8 && strcmp(cont, "=") == 0) {
2098 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2099 snprintf(errbuf, errbufsize, "line %d: unexpected end of file within the atom cards", lineNumber);
2102 sscanf(buf, " %f %f %f %f", fbuf+6, fbuf+7, fbuf+8, fbuf+9);
2104 } else n = 5; /* Iso */
2108 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2109 ap->occupancy = fbuf[3];
2110 if (ap->aname[0] != 'Q' && ibuf[0] >= 1 && ibuf[0] <= nsfacs) {
2111 strncpy(ap->element, sfacs[ibuf[0] - 1], 2);
2113 /* sAtomSetElement(ap, -1, sfacs[ibuf[0] - 1]); */
2114 /* strncpy(ap->element, sfacs[ibuf[0] - 1], 4);
2115 ap->atomicNumber = ElementToInt(ap->element); */
2117 sAtomSetElement(ap, -1, ap->name); */
2118 /* ap->atomicNumber = AtomNameToElement(ap->name);
2119 ElementToString(ap->atomicNumber, ap->element); */
2122 if (n == 10 || fbuf[4] >= 0.0) {
2124 /* Read in the standard deviations */
2125 ReadLine(buf, sizeof buf, fp, &lineNumber);
2126 for (i = 0; i < 9; i++) {
2130 dbuf[i] = strtod(buf + j, NULL);
2133 ap->sigma.x = dbuf[0];
2134 ap->sigma.y = dbuf[1];
2135 ap->sigma.z = dbuf[2];
2138 ap->tempFactor = fbuf[4] * 78.9568352087147; /* 8*pi*pi */
2140 MoleculeSetAniso(mp, atomIndex, 8, fbuf[4], fbuf[5], fbuf[6], fbuf[9], fbuf[7], fbuf[8], dbuf);
2141 ap->resSeq = currentResSeq;
2142 strncpy(ap->resName, currentResName, 4);
2149 /* Add symmetry operations according to the lattice type */
2150 switch (latticeType < 0 ? -latticeType : latticeType) {
2151 static Transform tr_i = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0.5};
2152 static Transform tr_c = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0};
2153 static Transform tr_a = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.5};
2154 static Transform tr_b = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5};
2155 static Transform tr_r1 = {0, 1, 0, -1, -1, 0, 0, 0, 1, 0, 0, 0};
2156 static Transform tr_r2 = {-1, -1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0};
2160 sMoleculeGenerateSymopWithTransform(mp, tr_i, 0);
2162 case 3: /* Rhombohedral obverse on hexagonal axes */
2164 sMoleculeGenerateSymopWithTransform(mp, tr_r1, n);
2165 sMoleculeGenerateSymopWithTransform(mp, tr_r2, n);
2169 sMoleculeGenerateSymopWithTransform(mp, tr_a, n);
2170 sMoleculeGenerateSymopWithTransform(mp, tr_b, n);
2171 sMoleculeGenerateSymopWithTransform(mp, tr_c, n);
2174 sMoleculeGenerateSymopWithTransform(mp, tr_a, 0);
2177 sMoleculeGenerateSymopWithTransform(mp, tr_b, 0);
2180 sMoleculeGenerateSymopWithTransform(mp, tr_c, 0);
2184 if (latticeType > 0) {
2185 static Transform tr_inv = {-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0};
2186 sMoleculeGenerateSymopWithTransform(mp, tr_inv, 0);
2189 MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
2191 MoleculeAddBonds(mp, nbonds, bonds);
2194 mp->nframes = -1; /* Should be recalculated later */
2197 Panic("low memory while reading structure file %s", fname);
2198 return -1; /* not reached */
2201 /* Add one gaussian orbital shell information (not undoable) */
2203 MoleculeAddGaussianOrbitalShell(Molecule *mol, Int sym, Int nprims, Int a_idx)
2208 return -1; /* Molecule is empty */
2211 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2213 return -2; /* Low memory */
2215 shellp = AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), bset->nshells, NULL);
2217 return -2; /* Low memory */
2219 case 0: shellp->sym = kGTOType_S; shellp->ncomp = 1; break;
2220 case 1: shellp->sym = kGTOType_P; shellp->ncomp = 3; break;
2221 case -1: shellp->sym = kGTOType_SP; shellp->ncomp = 4; break;
2222 case 2: shellp->sym = kGTOType_D; shellp->ncomp = 6; break;
2223 case -2: shellp->sym = kGTOType_D5; shellp->ncomp = 5; break;
2224 /* TODO: Support F/F7 type orbitals */
2225 /* case 3: sp->sym = kGTOtype_F; sp->ncomp = 10; break;
2226 case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break; */
2228 return -3; /* Unsupported shell type */
2230 shellp->nprim = nprims;
2231 shellp->a_idx = a_idx;
2232 if (bset->shells < shellp) {
2233 shellp->m_idx = shellp[-1].m_idx + shellp[-1].ncomp;
2234 shellp->p_idx = shellp[-1].p_idx + shellp[-1].nprim;
2242 /* Add a set of gaussian primitive coefficients (not undoable) */
2244 MoleculeAddGaussianPrimitiveCoefficients(Molecule *mol, Double exponent, Double contraction, Double contraction_sp)
2249 return -1; /* Molecule is empty */
2252 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2254 return -2; /* Low memory */
2256 primp = AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), bset->npriminfos, NULL);
2258 return -2; /* Low memory */
2259 primp->A = exponent;
2260 primp->C = contraction;
2261 primp->Csp = contraction_sp;
2265 /* Set MO coefficients for idx-th MO */
2267 MoleculeSetMOCoefficients(Molecule *mol, Int idx, Double energy, Int ncomps, Double *coeffs)
2272 return -1; /* Molecule is empty */
2275 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2277 return -2; /* Low memory */
2279 if (bset->nmos == 0) {
2280 if (bset->nshells > 0) {
2281 /* Shell info is already set: calculate the number of MOs from there */
2282 for (i = n = 0; i < bset->nshells; i++)
2283 n += bset->shells[i].ncomp;
2285 } else if (ncomps > 0) {
2286 bset->ncomps = ncomps;
2288 if (bset->rflag == 0)
2289 bset->nmos = bset->ncomps * 2;
2291 bset->nmos = bset->ncomps;
2292 if (bset->nmos <= 0)
2293 return -3; /* Bad or inconsistent number of MOs */
2294 bset->mo = (Double *)calloc(sizeof(Double), bset->nmos * bset->ncomps);
2295 bset->moenergies = (Double *)calloc(sizeof(Double), bset->nmos);
2296 if (bset->mo == NULL || bset->moenergies == NULL) {
2297 if (bset->mo != NULL)
2299 if (bset->moenergies != NULL)
2300 free(bset->moenergies);
2302 bset->moenergies = NULL;
2304 return -2; /* Low memory */
2307 if (idx < 0 || idx >= bset->nmos)
2308 return -4; /* Bad MO index */
2309 if (energy != -1000000)
2310 bset->moenergies[idx] = energy;
2311 if (ncomps < bset->ncomps)
2312 return -5; /* Insufficient number of data provided */
2313 memmove(bset->mo + (idx * bset->ncomps), coeffs, sizeof(Double) * bset->ncomps);
2314 if (bset->cns != NULL) {
2315 /* Clear the cached values */
2323 /* Allocate BasisSet record. rflag: UHF, 0; RHF, 1; ROHF, 2
2324 ne_alpha: number of alpha electrons, ne_beta: number of beta electrons
2325 The natoms and pos are copied from mol. */
2327 MoleculeAllocateBasisSetRecord(Molecule *mol, Int rflag, Int ne_alpha, Int ne_beta)
2332 if (mol == NULL || mol->natoms == 0)
2333 return -1; /* Molecule is empty */
2336 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2338 return -2; /* Low memory */
2340 if (bset->pos != NULL) {
2344 bset->natoms = mol->natoms;
2345 bset->pos = (Vector *)calloc(sizeof(Vector), bset->natoms);
2346 if (bset->pos == NULL)
2347 return -2; /* Low memory */
2348 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
2349 bset->pos[i].x = ap->r.x * kAngstrom2Bohr;
2350 bset->pos[i].y = ap->r.y * kAngstrom2Bohr;
2351 bset->pos[i].z = ap->r.z * kAngstrom2Bohr;
2353 bset->ne_alpha = ne_alpha;
2354 bset->ne_beta = ne_beta;
2355 bset->rflag = rflag;
2360 sSeparateTokens(char *inString, char **outPtr, int size)
2364 for (i = 0; i < size; i++) {
2365 p = strtok((i == 0 ? inString : NULL), " \r\n");
2376 sReadNumberArray(void *basep, Int *countp, Int size, Int num, FILE *fp, int *lnp)
2380 *((void **)basep) = NULL;
2382 if (AssignArray(basep, countp, size, num - 1, NULL) == NULL)
2383 return 4; /* Out of memory */
2385 while (ReadLine(buf, sizeof buf, fp, lnp) > 0) {
2386 char *tokens[16], *p;
2387 sSeparateTokens(buf, tokens, 16);
2388 for (i = 0; i < 16; i++) {
2389 if (tokens[i] == NULL)
2391 if (size == sizeof(Int)) {
2392 (*((Int **)basep))[n] = strtol(tokens[i], &p, 0);
2393 } else if (size == sizeof(Double)) {
2394 (*((Double **)basep))[n] = strtod(tokens[i], &p);
2395 } else return -1; /* Internal error */
2396 if (tokens[i] == p || *p != 0)
2397 return 1; /* Non-digit character */
2399 if (i < 15 && tokens[i + 1] != NULL)
2400 return 2; /* Too many data */
2401 return 0; /* All data are successfully read */
2405 return 3; /* Unexpected EOF */
2409 sSetupGaussianCoefficients(BasisSet *bset)
2416 /* Cache the contraction coefficients for efficient calculation */
2417 /* Sum up the number of components for all primitives */
2418 for (i = k = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2420 k += sp->nprim * sp->ncomp;
2422 /* Allocate memory for the cached values */
2423 if (AssignArray(&bset->cns, &bset->ncns, sizeof(Double), k - 1, NULL) == NULL)
2425 /* Iterate over all primitives */
2427 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2428 for (j = 0, pp = bset->priminfos + sp->p_idx; j < sp->nprim; j++, pp++) {
2431 // (8 alpha^3/pi^3)^0.25 exp(-alpha r^2)
2432 *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2435 // (128 alpha^5/pi^3)^0.25 [x|y|z]exp(-alpha r^2)
2436 d = pp->C * pow(pp->A, 1.25) * 1.425410941;
2442 *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2443 d = pp->Csp * pow(pp->A, 1.25) * 1.425410941;
2449 // xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2)
2450 // xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2451 d = pp->C * pow(pp->A, 1.75);
2452 dp[0] = dp[1] = dp[2] = d * 1.645922781;
2453 dp[3] = dp[4] = dp[5] = d * 2.850821881;
2457 // 3zz-rr: (128 alpha^7/9pi^3)^0.25 (3zz-rr)exp(-alpha r^2)
2458 // xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2459 // xx-yy: (128 alpha^7/pi^3)^0.25 (xx-yy)exp(-alpha r^2)
2460 d = pp->C * pow(pp->A, 1.75);
2461 dp[0] = d * 0.822961390;
2462 dp[1] = dp[2] = dp[4] = d * 2.850821881;
2463 dp[3] = d * 1.425410941;
2473 MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
2478 int natoms, nbasis, i, j, k, n, mxbond, retval, ncomps, nprims, nelec;
2489 if (errbuf == NULL) {
2496 bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2500 fp = fopen(fname, "rb");
2502 snprintf(errbuf, errbufsize, "Cannot open file");
2506 natoms = nbasis = -1;
2514 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2517 if (lineNumber == 2) {
2520 bset->rflag = 0; /* UHF */
2521 else if (buf[11] == 'O')
2522 bset->rflag = 2; /* ROHF */
2523 else bset->rflag = 1; /* RHF */
2526 while (p > buf && *p == ' ')
2529 sSeparateTokens(buf + 42, tokens, 16);
2530 if (strcmp(buf, "Number of atoms") == 0) {
2531 if (tokens[1] == NULL || (natoms = atoi(tokens[1])) <= 0) {
2532 snprintf(errbuf, errbufsize, "Line %d: strange number of atoms: %s", lineNumber, tokens[1]);
2536 /* Allocate atom records (all are empty for now) */
2537 AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms - 1, NULL);
2538 /* Also allocate atom position array for MO calculations */
2539 AssignArray(&bset->pos, &bset->natoms, sizeof(Vector), natoms - 1, NULL);
2540 /* Also allocate nuclear charge array */
2541 bset->nuccharges = (Double *)calloc(sizeof(Double), natoms);
2542 } else if (strcmp(buf, "Number of electrons") == 0) {
2543 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2544 snprintf(errbuf, errbufsize, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
2549 } else if (strcmp(buf, "Number of alpha electrons") == 0) {
2550 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2551 snprintf(errbuf, errbufsize, "Line %d: strange number of alpha electrons: %s", lineNumber, tokens[1]);
2556 } else if (strcmp(buf, "Number of beta electrons") == 0) {
2557 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2558 snprintf(errbuf, errbufsize, "Line %d: strange number of beta electrons: %s", lineNumber, tokens[1]);
2563 if (bset->ne_alpha + bset->ne_beta != nelec) {
2564 snprintf(errbuf, errbufsize, "Line %d: sum of alpha (%d) and beta (%d) electrons does not match the number of electrons (%d)", lineNumber, (int)bset->ne_alpha, (int)bset->ne_beta, (int)nelec);
2568 } else if (strcmp(buf, "Number of basis functions") == 0) {
2569 if (tokens[1] == NULL || (nbasis = atoi(tokens[1])) <= 0) {
2570 snprintf(errbuf, errbufsize, "Line %d: strange number of basis functions: %s", lineNumber, tokens[1]);
2574 } else if (strcmp(buf, "Atomic numbers") == 0) {
2575 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2576 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2580 if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms, fp, &lineNumber) != 0) {
2581 snprintf(errbuf, errbufsize, "Line %d: cannot read atomic numbers", lineNumber);
2585 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2586 ap->atomicNumber = iary[i];
2587 bset->nuccharges[i] = iary[i];
2588 ElementToString(ap->atomicNumber, ap->element);
2589 memmove(ap->aname, ap->element, 4);
2590 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
2595 } else if (strcmp(buf, "Nuclear charges") == 0) {
2596 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2597 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2601 if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms, fp, &lineNumber) != 0) {
2602 snprintf(errbuf, errbufsize, "Line %d: cannot read nuclear charges", lineNumber);
2606 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2607 bset->nuccharges[i] = dary[i];
2611 } else if (strcmp(buf, "Current cartesian coordinates") == 0) {
2612 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * 3) {
2613 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of cartesian coordinates: %s", lineNumber, tokens[2]);
2617 if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms * 3, fp, &lineNumber) != 0) {
2618 snprintf(errbuf, errbufsize, "Line %d: cannot read cartesian coordinates", lineNumber);
2622 for (i = 0, ap = mp->atoms, vp = bset->pos; i < natoms; i++, ap = ATOM_NEXT(ap), vp++) {
2623 vp->x = dary[i * 3];
2624 vp->y = dary[i * 3 + 1];
2625 vp->z = dary[i * 3 + 2];
2626 ap->r.x = vp->x * kBohr2Angstrom;
2627 ap->r.y = vp->y * kBohr2Angstrom;
2628 ap->r.z = vp->z * kBohr2Angstrom;
2632 } else if (strcmp(buf, "MxBond") == 0) {
2633 if (tokens[1] == NULL || (mxbond = atoi(tokens[1])) <= 0) {
2634 snprintf(errbuf, errbufsize, "Line %d: strange number of bonds per atom: %s", lineNumber, tokens[1]);
2638 } else if (strcmp(buf, "IBond") == 0) {
2640 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * mxbond) {
2641 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of bonds: %s", lineNumber, tokens[2]);
2645 if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms * mxbond, fp, &lineNumber) != 0) {
2646 snprintf(errbuf, errbufsize, "Line %d: cannot read bond information", lineNumber);
2650 bonds = (Int *)malloc(sizeof(Int) * (mxbond * 2 + 1));
2651 for (i = 0; i < natoms; i++) {
2652 for (j = k = 0; j < mxbond; j++) {
2653 n = iary[i * mxbond + j] - 1;
2655 /* Connect atom i and atom n */
2661 bonds[k] = kInvalidIndex;
2662 MoleculeAddBonds(mp, k / 2, bonds);
2668 } else if (strcmp(buf, "Shell types") == 0) {
2669 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0) {
2670 snprintf(errbuf, errbufsize, "Line %d: wrong number of shell types: %s", lineNumber, tokens[2]);
2674 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2675 snprintf(errbuf, errbufsize, "Line %d: cannot read shell types", lineNumber);
2679 /* Allocate ShellInfo table and store shell type information */
2680 AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), nary - 1, NULL);
2681 for (i = n = 0, sp = bset->shells; i < nary; i++, sp++) {
2683 case 0: sp->sym = kGTOType_S; sp->ncomp = 1; break;
2684 case 1: sp->sym = kGTOType_P; sp->ncomp = 3; break;
2685 case -1: sp->sym = kGTOType_SP; sp->ncomp = 4; break;
2686 case 2: sp->sym = kGTOType_D; sp->ncomp = 6; break;
2687 case -2: sp->sym = kGTOType_D5; sp->ncomp = 5; break;
2688 /* TODO: Support F/F7 type orbitals */
2689 /* case 3: sp->sym = kGTOtype_F; sp->ncomp = 10; break;
2690 case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break; */
2692 snprintf(errbuf, errbufsize, "Line %d: unsupported shell type %d", lineNumber, iary[i]);
2699 bset->ncomps = ncomps = n;
2702 } else if (strcmp(buf, "Number of primitives per shell") == 0) {
2703 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2704 snprintf(errbuf, errbufsize, "Line %d: wrong size of the primitive table: %s", lineNumber, tokens[2]);
2708 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2709 snprintf(errbuf, errbufsize, "Line %d: cannot read primitive table", lineNumber);
2713 for (i = n = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2714 sp->nprim = iary[i];
2721 } else if (strcmp(buf, "Shell to atom map") == 0) {
2722 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2723 snprintf(errbuf, errbufsize, "Line %d: wrong size of the shell-to-atom map: %s", lineNumber, tokens[2]);
2727 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2728 snprintf(errbuf, errbufsize, "Line %d: cannot read shell-to-atom table", lineNumber);
2732 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2733 sp->a_idx = iary[i] - 1;
2737 } else if (strcmp(buf, "Primitive exponents") == 0) {
2738 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nprims) {
2739 snprintf(errbuf, errbufsize, "Line %d: wrong number of primitive exponents: %s", lineNumber, tokens[2]);
2743 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2744 snprintf(errbuf, errbufsize, "Line %d: cannot read primitive exponents", lineNumber);
2748 /* Allocate PrimInfo table */
2749 AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), nprims - 1, NULL);
2750 for (i = 0, pp = bset->priminfos; i < nprims; i++, pp++) {
2755 } else if (strcmp(buf, "Contraction coefficients") == 0) {
2756 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2757 snprintf(errbuf, errbufsize, "Line %d: wrong number of contraction coefficients: %s", lineNumber, tokens[2]);
2761 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2762 snprintf(errbuf, errbufsize, "Line %d: cannot read contraction coefficients", lineNumber);
2766 for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2771 } else if (strcmp(buf, "P(S=P) Contraction coefficients") == 0) {
2772 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2773 snprintf(errbuf, errbufsize, "Line %d: wrong number of P(S=P) contraction coefficients: %s", lineNumber, tokens[2]);
2777 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2778 snprintf(errbuf, errbufsize, "Line %d: cannot read P(S=P) contraction coefficients", lineNumber);
2782 for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2787 } else if (strcmp(buf, "Alpha Orbital Energies") == 0) {
2788 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2789 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of alpha orbitals: %s", lineNumber, tokens[2]);
2793 if (sReadNumberArray(&bset->moenergies, &bset->nmos, sizeof(Double), i, fp, &lineNumber) != 0) {
2794 snprintf(errbuf, errbufsize, "Line %d: cannot read alpha orbital energies", lineNumber);
2798 } else if (strcmp(buf, "Alpha MO coefficients") == 0) {
2799 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2800 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of alpha MO coefficients: %s", lineNumber, tokens[2]);
2804 if (sReadNumberArray(&bset->mo, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2805 snprintf(errbuf, errbufsize, "Line %d: cannot read MO coefficients", lineNumber);
2809 } else if (strcmp(buf, "Beta Orbital Energies") == 0) {
2810 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2811 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of beta orbitals: %s", lineNumber, tokens[2]);
2815 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2816 snprintf(errbuf, errbufsize, "Line %d: cannot read beta orbital energies", lineNumber);
2820 bset->moenergies = (Double *)realloc(bset->moenergies, sizeof(Double) * 2 * ncomps);
2821 bset->nmos = ncomps * 2;
2822 bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);
2823 memmove(bset->moenergies + ncomps, dary, sizeof(Double) * ncomps);
2824 memset(bset->mo + ncomps * ncomps, 0, sizeof(Double) * ncomps * ncomps);
2827 } else if (strcmp(buf, "Beta MO coefficients") == 0) {
2828 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2829 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of beta MO coefficients: %s", lineNumber, tokens[2]);
2833 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2834 snprintf(errbuf, errbufsize, "Line %d: cannot read alpha MO coefficients", lineNumber);
2838 bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps); /* Should be unnecessary, just in case */
2839 memmove(bset->mo + ncomps * ncomps, dary, sizeof(Double) * ncomps * ncomps);
2842 } else if (strcmp(buf, "Total SCF Density") == 0) {
2843 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * (ncomps + 1) / 2) {
2844 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of SCF densities: %s", lineNumber, tokens[2]);
2848 if (sReadNumberArray(&bset->scfdensities, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2849 snprintf(errbuf, errbufsize, "Line %d: cannot read SCF densities", lineNumber);
2855 if (mp->natoms == 0) {
2856 snprintf(errbuf, errbufsize, "Atom information is missing");
2860 if (bset->shells == NULL || bset->priminfos == NULL) {
2861 snprintf(errbuf, errbufsize, "Gaussian primitive information is missing");
2865 if (bset->mo == NULL) {
2866 snprintf(errbuf, errbufsize, "MO coefficients were not found");
2870 if (sSetupGaussianCoefficients(bset) != 0) {
2871 snprintf(errbuf, errbufsize, "Internal error during setup MO calculation");
2884 if (mp->bset != NULL) {
2885 BasisSetRelease(mp->bset);
2891 Panic("low memory while reading fchk file %s", fname);
2892 return -1; /* not reached */
2896 MoleculeLoadGamessDatFile(Molecule *mol, const char *fname, char *errbuf, int errbufsize)
2901 int lineNumber, i, j, k, len, natoms = 0;
2907 Vector *vbuf = NULL;
2909 int optimizing = 0, status = 0;
2911 if (errbuf == NULL) {
2917 mol = MoleculeNew();
2919 if (mol->natoms == 0)
2922 fp = fopen(fname, "rb");
2924 snprintf(errbuf, errbufsize, "Cannot open file");
2928 /* ESP is cleared (not undoable!) */
2929 if (mol->elpots != NULL) {
2936 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2939 if (strncmp(buf, " $DATA", 6) == 0) {
2940 /* Initial geometry */
2942 vbuf = (Vector *)calloc(sizeof(Vector), mol->natoms);
2945 ReadLine(buf, sizeof buf, fp, &lineNumber); /* Title */
2946 ReadLine(buf, sizeof buf, fp, &lineNumber); /* Symmetry */
2947 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2948 if (strncmp(buf, " $END", 5) == 0)
2950 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
2951 snprintf(errbuf, errbufsize, "Line %d: bad format in $DATA section", lineNumber);
2956 memset(&a, 0, sizeof(a));
2957 strncpy(a.aname, sval, 4);
2961 a.atomicNumber = (Int)dval[0];
2962 strncpy(a.element, ElementToString(a.atomicNumber, sval), 3);
2963 a.type = AtomTypeEncodeToUInt(a.element);
2964 a.weight = WeightForAtomicNumber(a.atomicNumber);
2965 MoleculeCreateAnAtom(mol, &a, mol->natoms);
2968 if (i >= mol->natoms) {
2969 snprintf(errbuf, errbufsize, "Line %d: too many atoms", lineNumber);
2972 if ((ap = ATOM_AT_INDEX(mol->atoms, i))->atomicNumber != dval[0]) {
2973 snprintf(errbuf, errbufsize, "Line %d: atomic number does not match", lineNumber);
2976 vbuf[i].x = dval[1];
2977 vbuf[i].y = dval[2];
2978 vbuf[i].z = dval[3];
2980 /* Skip until a blank line is found */
2981 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2982 for (j = 0; buf[j] == ' '; j++);
2990 /* Set atom positions */
2992 if (natoms < mol->natoms) {
2993 snprintf(errbuf, errbufsize, "Line %d: too few atoms", lineNumber);
2996 ig = IntGroupNewWithPoints(0, natoms, -1);
2997 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, natoms, vbuf);
2998 IntGroupRelease(ig);
3001 vbuf = (Vector *)calloc(sizeof(Vector), natoms);
3002 nframes = MoleculeGetNumberOfFrames(mol);
3006 } else if (strstr(buf, "DATA FROM NSERCH") != NULL || (strstr(buf, "RESULTS FROM SUCCESSFUL") != NULL && (n1 = 1))) {
3007 /* Skip until the separator line is read (three or four lines) */
3011 snprintf(errbuf, errbufsize, "Line %d: the separator line at the top of the coordinates is not found: bad format?", lineNumber);
3014 ReadLine(buf, sizeof buf, fp, &lineNumber);
3015 } while (strstr(buf, "----------------------------") == NULL);
3016 for (i = 0; i < natoms; i++) {
3017 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3018 snprintf(errbuf, errbufsize, "Unexpected end of file in reading NSERCH data");
3021 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3022 snprintf(errbuf, errbufsize, "Line %d: bad format in NSERCH coordinate data", lineNumber);
3025 vbuf[i].x = dval[1];
3026 vbuf[i].y = dval[2];
3027 vbuf[i].z = dval[3];
3029 ig = IntGroupNewWithPoints(nframes, 1, -1);
3030 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, natoms, vbuf, 0, NULL);
3031 IntGroupRelease(ig);
3034 optimizing = 1; /* Flag to skip reading the VEC group */
3038 } else if (strstr(buf, "E(UHF)") != NULL || (strstr(buf, "E(RHF)") != NULL && (n1 = 1)) || (strstr(buf, "E(ROHF)") != NULL && (n1 = 2))) {
3039 if (mol->bset == NULL) {
3040 i = MoleculeAllocateBasisSetRecord(mol, n1, 0, 0);
3042 snprintf(errbuf, errbufsize, "Line %d: cannot allocate basis set internal buffer", lineNumber);
3046 } else if (strncmp(buf, " $VEC", 5) == 0) {
3048 /* Read the vec group */
3049 if (mol->bset == NULL || mol->bset->ncomps == 0)
3050 continue; /* Just ignore */
3052 continue; /* Ignore VEC group during optimization */
3053 coeffs = (Double *)calloc(sizeof(Double), mol->bset->ncomps);
3054 if (coeffs == NULL) {
3055 snprintf(errbuf, errbufsize, "Line %d: low memory during $VEC", lineNumber);
3059 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3061 if (strncmp(buf, " $END", 5) == 0)
3063 while ((j = 5 + (k % 5) * 15) <= len && buf[j] != 0 && buf[j] != '\n') {
3064 strncpy(sval, buf + j, 15);
3066 coeffs[k] = strtod(sval, NULL);
3071 if (k < mol->bset->ncomps)
3073 j = MoleculeSetMOCoefficients(mol, i, -1000000, k, coeffs);
3075 snprintf(errbuf, errbufsize, "Line %d: cannot set coefficients for MO %d", lineNumber, i + 1);
3085 } else if ((strstr(buf, "ELECTRIC POTENTIAL") != NULL || strstr(buf, "ELECTROSTATIC POTENTIAL") != NULL) && strstr(buf, "ELPOTT") != NULL) {
3087 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3089 if (strstr(buf, "TOTAL NUMBER OF GRID POINTS") != NULL)
3091 if (sscanf(buf, "%d %lf %lf %lf %lf", &ival[0], &dval[0], &dval[1], &dval[2], &dval[3]) < 5)
3093 ep = AssignArray(&mol->elpots, &mol->nelpots, sizeof(Elpot), i, NULL);
3094 ep->pos.x = dval[0];
3095 ep->pos.y = dval[1];
3096 ep->pos.z = dval[2];
3101 goto redo; /* This section has no end line, so the last line should be processed again */
3102 else break; /* End of file encountered or interrupted */
3103 } /* TODO: read MOLPLT info if present */
3106 snprintf(errbuf, errbufsize, "User interrupt at line %d", lineNumber);
3111 if (newmol && mol->nbonds == 0) {
3114 MoleculeGuessBonds(mol, 1.2, &nbonds, &bonds);
3116 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds);
3124 MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
3127 if (ftype == NULL || *ftype == 0) {
3129 cp = strrchr(fname, '.');
3133 cp = guessMoleculeType(fname);
3134 if (strcmp(cp, "???") != 0)
3138 if (strcasecmp(ftype, "pdb") == 0) {
3139 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
3142 /* Try all formats once again */
3143 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
3149 MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3155 int i, j, new_unit, retval;
3162 if (errbuf == NULL) {
3167 fp = fopen(fname, "rb");
3169 snprintf(errbuf, errbufsize, "Cannot open file");
3172 /* flockfile(fp); */
3173 if (mp->natoms == 0)
3176 /* Allocate buffer for undo-capable modification */
3177 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
3178 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3179 /* Retain current position if the atom info is missing in the input file */
3185 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3186 if (strncmp(buf, "END", 3) == 0)
3188 if (strncmp(buf, "HETATM", 6) == 0 || strncmp(buf, "ATOM", 4) == 0) {
3190 Int serial, intCharge, resSeq;
3193 char segName[5], resName[4], atomName[5], resSeqStr[5], atomType[3], element[3], occStr[6];
3195 memset(&w, 0, sizeof(w));
3196 ReadFormat(buf, "x6 I5 x1 S4 x1 S3 x1 x1 S4 x1 x3 F8 F8 F8 S6 F6 x6 S4 S2 I2",
3197 &w.serial, w.atomName, w.resName, w.resSeqStr, &w.r.x, &w.r.y, &w.r.z,
3198 w.occStr, &w.temp, w.segName, w.element, &w.intCharge);
3199 if (w.atomName[0] == 0) {
3200 continue; /* Atom name is empty */
3202 /* A workaround for residue number >= 10000 (XPLOR style) */
3203 if (w.resSeqStr[0] >= 'A' && w.resSeqStr[0] <= 'Z') {
3204 w.resSeq = (w.resSeqStr[0] - 'A' + 10) * 1000 + atoi(w.resSeqStr + 1);
3206 w.resSeq = atoi(w.resSeqStr);
3208 if (w.element[0] == 0) {
3209 /* $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl */
3210 for (p = w.atomName; *p != 0; p++) {
3211 if (isalpha(*p) && *p != '_') {
3212 w.element[0] = toupper(*p);
3213 if (isalpha(p[1]) && p[1] != '_') {
3214 w.element[1] = toupper(p[1]);
3223 if (w.occStr[0] == 0)
3226 w.occ = atof(w.occStr);
3227 if (w.serial <= 0) {
3228 snprintf(errbuf, errbufsize, "line %d: non-positive atom number %d", lineNumber, w.serial);
3232 w.serial--; /* The internal atom number is 0-based */
3233 if (w.serial >= mp->natoms) {
3235 /* Create a new atom entry */
3236 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, w.serial, NULL);
3238 snprintf(errbuf, errbufsize, "line %d: the atom number %d does not exist in the structure file", lineNumber, w.serial+1);
3244 ap = ATOM_AT_INDEX(mp->atoms, w.serial);
3246 ap->occupancy = w.occ;
3247 ap->tempFactor = w.temp;
3248 if (w.segName[0] == 0)
3249 strncpy(w.segName, "MAIN", 4);
3250 strncpy(ap->segName, w.segName, 4);
3251 ap->resSeq = w.resSeq;
3252 strncpy(ap->resName, w.resName, 4);
3253 strncpy(ap->aname, w.atomName, 4);
3254 strncpy(ap->element, w.element, 2);
3255 ap->intCharge = w.intCharge;
3256 if (ap->resSeq > 0) {
3257 if (ap->resSeq < mp->nresidues) {
3258 /* Update the resName according to residues[] */
3259 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
3261 /* Register the resName to residues[] */
3262 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, w.resName);
3266 strcpy(ap->resName, "XXX");
3267 if (mp->nresidues == 0)
3268 AssignArray(&mp->residues, &mp->nresidues, 4, 0, ap->resName);
3270 i = ElementToInt(ap->element);
3272 ap->weight = gElementParameters[i].weight;
3274 /* Not a new unit: only the atom position is updated */
3278 } else if (strncmp(buf, "CONECT", 6) == 0 && new_unit) {
3279 i = ReadFormat(buf, "x6 I5I5I5I5I5I5I5I5I5I5I5I5",
3280 ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
3281 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7,
3282 ibuf + 8, ibuf + 9, ibuf + 10, ibuf + 11);
3286 for (j = 0; j < i; j++) {
3287 if (ibuf[j] < 0 || ibuf[j] > mp->natoms) {
3288 snprintf(errbuf, errbufsize, "line %d: The CONECT record contains non-existent atom %d", lineNumber, ibuf[j]);
3291 } else if (ibuf[j] == 0)
3297 for (j = 1, bi = 0; j < i; j++) {
3298 if (ibuf[0] < ibuf[j]) {
3299 bbuf[bi * 2] = ibuf[0] - 1;
3300 bbuf[bi * 2 + 1] = ibuf[j] - 1;
3307 retval = MoleculeAddBonds(mp, bi, bbuf);
3309 snprintf(errbuf, errbufsize, "line %d: bad bond specification", lineNumber);
3316 /* funlockfile(fp); */
3319 /* Renumber atoms if some atom number is unoccupied */
3320 int *old2new, oldidx, newidx;
3321 old2new = (int *)calloc(sizeof(int), mp->natoms);
3322 if (old2new == NULL) {
3323 snprintf(errbuf, errbufsize, "Out of memory");
3327 for (oldidx = newidx = 0; oldidx < mp->natoms; oldidx++) {
3328 ap = ATOM_AT_INDEX(mp->atoms, oldidx);
3329 if (ap->aname[0] != 0) {
3330 old2new[oldidx] = newidx;
3331 if (oldidx > newidx)
3332 memmove(ATOM_AT_INDEX(mp->atoms, newidx), ap, gSizeOfAtomRecord);
3336 mp->natoms = newidx;
3337 if (oldidx > newidx) {
3338 /* Renumber the connects and bonds */
3340 for (i = 0; i < mp->natoms; i++) {
3341 ap = ATOM_AT_INDEX(mp->atoms, i);
3342 cp = AtomConnectData(&ap->connect);
3343 for (j = 0; j < ap->connect.count; j++) {
3344 cp[j] = old2new[cp[j]];
3347 for (i = 0; i < mp->nbonds * 2; i++) {
3348 mp->bonds[i] = old2new[mp->bonds[i]];
3351 retval = MoleculeRebuildTablesFromConnects(mp);
3353 /* This error may not happen */
3354 snprintf(errbuf, errbufsize, "Cannot build angle/dihedral/improper tables");
3358 /* Undo action: delete all atoms */
3361 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3362 act = MolActionNew(gMolActionUnmergeMolecule, ig);
3363 act->frame = mp->cframe;
3364 MolActionCallback_registerUndo(mp, act);
3365 MolActionRelease(act);
3366 IntGroupRelease(ig);
3369 /* Set the new atom positions */
3370 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3371 MolActionCreateAndPerform(mp, gMolActionSetAtomPositions, ig, mp->natoms, vp);
3372 IntGroupRelease(ig);
3376 mp->nframes = -1; /* Should be recalculated later */
3378 return 1; /* No atoms */
3382 /* funlockfile(fp); */
3388 return 1; /* Maybe different format? */
3393 MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3396 SFloat32 *xp, *yp, *zp;
3401 if (mp == NULL || mp->natoms == 0) {
3402 snprintf(errbuf, errbufsize, "Molecule is empty");
3405 n = DcdOpen(fname, &dcd);
3408 case -2: snprintf(errbuf, errbufsize, "Cannot open file"); break;
3409 case 1: snprintf(errbuf, errbufsize, "Premature EOF encountered"); break;
3410 case 2: snprintf(errbuf, errbufsize, "Bad block length of the first section"); break;
3411 case 3: snprintf(errbuf, errbufsize, "\"CORD\" signature is missing"); break;
3412 case 4: snprintf(errbuf, errbufsize, "Bad termination of the first section"); break;
3413 case 5: snprintf(errbuf, errbufsize, "The title section is not correct"); break;
3414 case 6: snprintf(errbuf, errbufsize, "The atom number section is not correct"); break;
3415 default: snprintf(errbuf, errbufsize, "Read error in dcd file"); break;
3418 if (dcd.natoms == 0)
3419 snprintf(errbuf, errbufsize, "No atoms were found in the dcd file");
3420 else if (dcd.nframes == 0)
3421 snprintf(errbuf, errbufsize, "No frames were found in the dcd file");
3423 if (errbuf[0] != 0) {
3429 vp = (Vector *)calloc(sizeof(Vector), mp->natoms * dcd.nframes);
3431 cp = (Vector *)calloc(sizeof(Vector), dcd.nframes * 4);
3433 xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3434 yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3435 zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3436 ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mp), dcd.nframes, -1);
3437 if (vp == NULL || xp == NULL || yp == NULL || zp == NULL || ig == NULL) {
3438 snprintf(errbuf, errbufsize, "Cannot allocate memory");
3444 if (ig) IntGroupRelease(ig);
3447 for (n = 0; n < dcd.nframes; n++) {
3450 SFloat32 dcdcell[6];
3451 if (DcdReadFrame(&dcd, n, xp, yp, zp, dcdcell)) {
3452 snprintf(errbuf, errbufsize, "Read error in dcd file");
3455 for (i = 0, vpp = &vp[n * mp->natoms]; i < dcd.natoms && i < mp->natoms; i++, vpp++) {
3463 /* dcdcell = {a, gamma, b, beta, alpha, c} */
3464 /* angles are described either in cosines (Charmm and NAMD > 2.5) or degrees (NAMD 2.5) */
3465 if (dcdcell[1] < -1.0 || dcdcell[1] > 1.0 || dcdcell[3] < -1.0 || dcdcell[3] > 1.0 || dcdcell[4] < -1.0 || dcdcell[4] > 1.0) {
3466 dcdcell[4] = cos(dcdcell[4] * kDeg2Rad); /* cos(alpha) */
3467 dcdcell[3] = cos(dcdcell[3] * kDeg2Rad); /* cos(beta) */
3468 dcdcell[1] = cos(dcdcell[1] * kDeg2Rad); /* cos(gamma) */
3470 /* a axis lies along the cartesian x axis */
3471 sing = sqrt(1 - dcdcell[1] * dcdcell[1]);
3472 vpp[0].x = dcdcell[0];
3475 vpp[1].x = dcdcell[2] * dcdcell[1];
3476 vpp[1].y = dcdcell[2] * sing;
3478 vpp[2].x = dcdcell[5] * dcdcell[3];
3479 vpp[2].y = dcdcell[5] * (dcdcell[4] - dcdcell[3] * dcdcell[1]) / sing;
3480 vpp[2].z = sqrt(dcdcell[5] * dcdcell[5] - vpp[2].x * vpp[2].x - vpp[2].y * vpp[2].y);
3481 vpp[3].x = vpp[3].y = vpp[3].z = 0.0;
3482 if (mp->cell == NULL) {
3483 /* Create periodicity if not present */
3484 MolActionCreateAndPerform(mp, gMolActionSetBox, &vpp[0], &vpp[1], &vpp[2], &vpp[3], 7, 0);
3488 if (MolActionCreateAndPerform(mp, gMolActionInsertFrames, ig, mp->natoms * dcd.nframes, vp, (cp == NULL ? 0 : dcd.nframes * 4), cp) != 0)
3489 snprintf(errbuf, errbufsize, "Cannot insert frames");
3490 mp->startStep = dcd.nstart;
3491 mp->stepsPerFrame = dcd.ninterval;
3492 mp->psPerStep = dcd.delta;
3501 IntGroupRelease(ig);
3508 MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3518 fp = fopen(fname, "rb");
3520 snprintf(errbuf, errbufsize, "Cannot open file");
3526 flags[0] = flags[1] = flags[2] = 0;
3527 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3528 if (strncmp(buf, "Bounding box:", 13) == 0) {
3529 for (i = 0; i < 3; i++) {
3530 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3531 snprintf(errbuf, errbufsize, "line %d: missing %d component of the bounding box", lineNumber, i + 1);
3535 n = sscanf(buf, "%lf %lf %lf %d", &d[0], &d[1], &d[2], &flag);
3537 vv.x = vv.y = vv.z = 0.0;
3539 case 0: vv.x = d[0]; break;
3540 case 1: vv.y = d[0]; break;
3541 case 2: vv.z = d[0]; break;
3543 if (n == 1 || (n == 2 && d[1] != 0.0))
3550 flags[i] = (flag != 0);
3552 flags[i] = (VecLength2(vv) != 0);
3556 if (mp->cell != NULL)
3557 vv = mp->cell->origin;
3559 vv.x = vv.y = vv.z = 0.0;
3560 MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
3561 } else if (strncmp(buf, "Bounding box origin:", 20) == 0) {
3562 if (mp->cell != NULL) {
3563 v[0] = mp->cell->axes[0];
3564 v[1] = mp->cell->axes[1];
3565 v[2] = mp->cell->axes[2];
3566 memmove(flags, mp->cell->flags, 3);
3568 v[0].x = 1.0; v[0].y = v[0].z = 0.0;
3569 v[1].y = 1.0; v[1].x = v[1].z = 0.0;
3570 v[2].z = 1.0; v[2].x = v[2].y = 0.0;
3571 flags[0] = flags[1] = flags[2] = 1.0;
3573 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0 || (n = sscanf(buf, "%lf %lf %lf", &d[0], &d[1], &d[2]) < 3)) {
3574 snprintf(errbuf, errbufsize, "line %d: wrong format for the bounding box origin", lineNumber);
3581 MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
3593 MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
3596 if (ftype == NULL || *ftype == 0) {
3598 cp = strrchr(fname, '.');
3602 cp = guessMoleculeType(fname);
3603 if (strcmp(cp, "???") != 0)
3607 if (strcasecmp(ftype, "psf") == 0) {
3608 retval = MoleculeWriteToPsfFile(mp, fname, errbuf, errbufsize);
3609 } else if (strcasecmp(ftype, "pdb") == 0) {
3610 retval = MoleculeWriteToPdbFile(mp, fname, errbuf, errbufsize);
3611 } else if (strcasecmp(ftype, "tep") == 0) {
3612 retval = MoleculeWriteToTepFile(mp, fname, errbuf, errbufsize);
3614 snprintf(errbuf, errbufsize, "The file format should be specified");
3618 MoleculeSetPath(mp, fname);
3623 MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3626 int i, j, k, n1, n2, n3, n_aniso, nframes;
3630 fp = fopen(fname, "wb");
3632 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
3637 nframes = MoleculeFlushFrames(mp);
3639 fprintf(fp, "!:atoms\n");
3640 fprintf(fp, "! idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge\n");
3641 n1 = n2 = n3 = n_aniso = 0;
3642 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3643 strncpy(bufs[0], ap->segName, 4);
3645 strncpy(bufs[1], ap->resName, 4);
3647 strncpy(bufs[2], ap->aname, 4);
3649 AtomTypeDecodeToString(ap->type, bufs[3]);
3651 strncpy(bufs[4], ap->element, 4);
3653 for (j = 0; j < 5; j++) {
3654 if (bufs[j][0] == 0) {
3658 for (k = 0; k < 6; k++) {
3659 if (bufs[j][k] == 0)
3661 if (bufs[j][k] > 0 && bufs[j][k] < ' ')
3665 if (SYMOP_ALIVE(ap->symop))
3667 if (ap->fix_force != 0)
3669 if (ap->mm_exclude || ap->periodic_exclude)
3671 if (ap->aniso != NULL)
3673 fprintf(fp, "%d %s %d %s %s %s %.5f %.5f %s %d %f %f %d\n", i, bufs[0], ap->resSeq, bufs[1], bufs[2], bufs[3], ap->charge, ap->weight, bufs[4], ap->atomicNumber, ap->occupancy, ap->tempFactor, ap->intCharge);
3678 fprintf(fp, "!:atoms_symop\n");
3679 fprintf(fp, "! idx symop symbase\n");
3680 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3682 n = ap->symop.sym * 1000000 + ap->symop.dx * 10000 + ap->symop.dy * 100 + ap->symop.dz;
3683 fprintf(fp, "%d %d %d\n", i, n, ap->symbase);
3689 fprintf(fp, "!:atoms_fix\n");
3690 fprintf(fp, "! idx fix_force fix_pos\n");
3691 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3692 fprintf(fp, "%d %f %f %f %f\n", i, ap->fix_force, ap->fix_pos.x, ap->fix_pos.y, ap->fix_pos.z);
3698 fprintf(fp, "!:mm_exclude\n");
3699 fprintf(fp, "! idx mm_exclude periodic_exclude\n");
3700 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3701 fprintf(fp, "%d %d %d\n", i, ap->mm_exclude, ap->periodic_exclude);
3711 for (i = 0; (i == n2 || i < n1); i++) {
3712 fprintf(fp, "!:positions ; frame %d\n", i);
3713 fprintf(fp, "! idx x y z [sx sy sz]\n");
3714 for (j = 0, ap = mp->atoms; j < mp->natoms; j++, ap = ATOM_NEXT(ap)) {
3717 if (i != n2 && i < ap->nframes)
3718 vp = ap->frames + i;
3721 if (ap->sigma.x != 0.0 || ap->sigma.y != 0.0 || ap->sigma.z != 0.0)
3724 fprintf(fp, "%d %.8f %.8f %.8f", j, vp->x, vp->y, vp->z);
3726 fprintf(fp, " %.8f %.8f %.8f", ap->sigma.x, ap->sigma.y, ap->sigma.z);
3733 if (mp->nbonds > 0) {
3734 fprintf(fp, "!:bonds\n");
3735 fprintf(fp, "! from1 to1 from2 to2 from3 to3 from4 to4\n");
3736 for (i = 0; i < mp->nbonds; i++) {
3737 fprintf(fp, "%d %d%c", mp->bonds[i * 2], mp->bonds[i * 2 + 1], (i % 4 == 3 || i == mp->nbonds - 1 ? '\n' : ' '));
3742 if (mp->nangles > 0) {
3743 fprintf(fp, "!:angles\n");
3744 fprintf(fp, "! a1 b1 c1 a2 b2 c2 a3 b3 c3\n");
3745 for (i = 0; i < mp->nangles; i++) {
3746 fprintf(fp, "%d %d %d%c", mp->angles[i * 3], mp->angles[i * 3 + 1], mp->angles[i * 3 + 2], (i % 3 == 2 || i == mp->nangles - 1 ? '\n' : ' '));
3751 if (mp->ndihedrals > 0) {
3752 fprintf(fp, "!:dihedrals\n");
3753 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3754 for (i = 0; i < mp->ndihedrals; i++) {
3755 fprintf(fp, "%d %d %d %d%c", mp->dihedrals[i * 4], mp->dihedrals[i * 4 + 1], mp->dihedrals[i * 4 + 2], mp->dihedrals[i * 4 + 3], (i % 2 == 1 || i == mp->ndihedrals - 1 ? '\n' : ' '));
3760 if (mp->nimpropers > 0) {
3761 fprintf(fp, "!:impropers\n");
3762 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3763 for (i = 0; i < mp->nimpropers; i++) {
3764 fprintf(fp, "%d %d %d %d%c", mp->impropers[i * 4], mp->impropers[i * 4 + 1], mp->impropers[i * 4 + 2], mp->impropers[i * 4 + 3], (i % 2 == 1 || i == mp->nimpropers - 1 ? '\n' : ' '));
3769 if (mp->cell != NULL) {
3770 fprintf(fp, "!:xtalcell\n");
3771 fprintf(fp, "! a b c alpha beta gamma\n");
3772 fprintf(fp, "! This information is redundant and overridden by the following periodic_box info\n");
3773 fprintf(fp, "%f %f %f %f %f %f\n", mp->cell->cell[0], mp->cell->cell[1], mp->cell->cell[2], mp->cell->cell[3], mp->cell->cell[4], mp->cell->cell[5]);
3776 fprintf(fp, "!:periodic_box\n");
3777 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz; fa fb fc [sigma; sa sb sc s_alpha s_beta s_gamma]\n");
3778 for (i = 0; i < 3; i++)
3779 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->axes[i].x, mp->cell->axes[i].y, mp->cell->axes[i].z);
3780 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->origin.x, mp->cell->origin.y, mp->cell->origin.z);
3781 fprintf(fp, "%d %d %d%s\n", mp->cell->flags[0], mp->cell->flags[1], mp->cell->flags[2], (mp->cell->has_sigma ? " 1" : ""));
3782 if (mp->cell->has_sigma) {
3783 fprintf(fp, "%f %f %f %f %f %f\n", mp->cell->cellsigma[0], mp->cell->cellsigma[1], mp->cell->cellsigma[2], mp->cell->cellsigma[3], mp->cell->cellsigma[4], mp->cell->cellsigma[5]);
3788 if (mp->useFlexibleCell != 0) {
3789 fprintf(fp, "!:frame_periodic_boxes\n");
3790 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz\n");
3791 for (i = 0; i < mp->nframe_cells * 4; i++) {
3792 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->frame_cells[i].x, mp->frame_cells[i].y, mp->frame_cells[i].z);
3797 if (mp->nsyms > 0) {
3798 fprintf(fp, "!:symmetry_operations\n");
3799 fprintf(fp, "! a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3\n");
3800 for (i = 0; i < mp->nsyms; i++) {
3801 Transform *tp = mp->syms + i;
3802 const unsigned char s_index_order[12] = {0, 3, 6, 1, 4, 7, 2, 5, 8, 9, 10, 11};
3803 for (j = 0; j < 12; j++)
3804 fprintf(fp, "%11.6f%c", (*tp)[s_index_order[j]], (j % 3 == 2 ? '\n' : ' '));
3810 fprintf(fp, "!:anisotropic_thermal_parameters\n");
3811 fprintf(fp, "! b11 b22 b33 b12 b13 b23 [sigma; sb11 sb22 sb33 sb12 sb13 sb23]\n");
3812 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3813 if (ap->aniso != NULL) {
3814 Double *bp = ap->aniso->bij;
3815 fprintf(fp, "%14.8g %14.8g %14.8g %14.8g %14.8g %14.8g%s\n", bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], (ap->aniso->has_bsig ? " 1" : ""));
3816 if (ap->aniso->has_bsig) {
3817 bp = ap->aniso->bsig;
3818 fprintf(fp, "%14.8g %14.8g %14.8g %14.8g %14.8g %14.8g\n", bp[0], bp[1], bp[2], bp[3], bp[4], bp[5]);
3821 fprintf(fp, "0 0 0 0 0 0\n");
3827 if (mp->arena != NULL) {
3828 MDArena *arena = mp->arena;
3829 fprintf(fp, "!:md_parameters\n");
3830 fprintf(fp, "log_file %s\n", arena->log_result_name);
3831 fprintf(fp, "coord_file %s\n", arena->coord_result_name);
3832 fprintf(fp, "vel_file %s\n", arena->vel_result_name);
3833 fprintf(fp, "force_file %s\n", arena->force_result_name);
3834 fprintf(fp, "debug_file %s\n", arena->debug_result_name);
3835 fprintf(fp, "debug_output_level %d\n", arena->debug_output_level);
3836 fprintf(fp, "step %d\n", arena->step);
3837 fprintf(fp, "coord_output_freq %d\n", arena->coord_output_freq);
3838 fprintf(fp, "energy_output_freq %d\n", arena->energy_output_freq);
3839 fprintf(fp, "coord_frame %d\n", arena->coord_result_frame);
3840 fprintf(fp, "timestep %g\n", arena->timestep);
3841 fprintf(fp, "cutoff %g\n", arena->cutoff);
3842 fprintf(fp, "electro_cutoff %g\n", arena->electro_cutoff);
3843 fprintf(fp, "pairlist_distance %g\n", arena->pairlist_distance);
3844 fprintf(fp, "temperature %g\n", arena->temperature);
3845 fprintf(fp, "andersen_freq %d\n", arena->andersen_thermo_freq);
3846 fprintf(fp, "andersen_coupling %g\n", arena->andersen_thermo_coupling);
3847 fprintf(fp, "random_seed %d\n", arena->random_seed);
3848 fprintf(fp, "dielectric %g\n", arena->dielectric);
3849 fprintf(fp, "gradient_convergence %g\n", arena->gradient_convergence);
3850 fprintf(fp, "coordinate_convergence %g\n", arena->coordinate_convergence);
3851 fprintf(fp, "use_xplor_shift %d\n", arena->use_xplor_shift);
3852 fprintf(fp, "scale14_vdw %g\n", arena->scale14_vdw);
3853 fprintf(fp, "scale14_elect %g\n", arena->scale14_elect);
3854 fprintf(fp, "relocate_center %d\n", arena->relocate_center);
3855 fprintf(fp, "surface_probe_radius %g\n", arena->probe_radius);
3856 fprintf(fp, "surface_tension %g\n", arena->surface_tension);
3857 fprintf(fp, "surface_potential_freq %d\n", arena->surface_potential_freq);
3858 fprintf(fp, "use_graphite %d\n", arena->use_graphite);
3859 fprintf(fp, "alchemical_lambda %g\n", arena->alchem_lambda);
3860 fprintf(fp, "alchemical_delta_lambda %g\n", arena->alchem_dlambda);
3861 if (arena->nalchem_flags > 0) {
3862 fprintf(fp, "alchem_flags %d", arena->nalchem_flags);
3863 for (i = 0; i < arena->nalchem_flags; i++) {
3866 else if (i % 10 == 0)
3868 fputc('0' + arena->alchem_flags[i], fp);
3872 if (arena->pressure != NULL) {
3874 fprintf(fp, "pressure_freq %d\n", arena->pressure->freq);
3875 fprintf(fp, "pressure_coupling %g\n", arena->pressure->coupling);
3876 dp = arena->pressure->apply;
3877 fprintf(fp, "pressure %g %g %g %g %g %g %g %g %g\n", dp[0], dp[1], dp[2], dp[3], dp[4], dp[5], dp[6], dp[7], dp[8]);
3878 dp = arena->pressure->cell_flexibility;
3879 fprintf(fp, "pressure_cell_flexibility %g %g %g %g %g %g %g %g\n", dp[0], dp[1], dp[2], dp[3], dp[4], dp[5], dp[6], dp[7]);
3880 fprintf(fp, "pressure_fluctuate_cell_origin %g\n", arena->pressure->fluctuate_cell_origin);
3881 fprintf(fp, "pressure_fluctuate_cell_orientation %g\n", arena->pressure->fluctuate_cell_orientation);
3885 if (mp->par != NULL) {
3886 Parameter *par = mp->par;
3887 fprintf(fp, "!:parameters\n");
3888 ParameterAppendToFile(par, fp);
3892 fprintf(fp, "!:velocity\n");
3893 fprintf(fp, "! idx vx vy vz\n");
3894 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3895 fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->v.x, ap->v.y, ap->v.z);
3899 fprintf(fp, "!:force\n");
3900 fprintf(fp, "! idx fx fy fz\n");
3901 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3902 fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->f.x, ap->f.y, ap->f.z);
3907 if (mp->mview != NULL) {
3909 if (mp->mview->track != NULL) {
3910 fprintf(fp, "!:trackball\n");
3911 fprintf(fp, "! scale; trx try trz; theta_deg x y z\n");
3912 f[0] = TrackballGetScale(mp->mview->track);
3913 fprintf(fp, "%f\n", f[0]);
3914 TrackballGetTranslate(mp->mview->track, f);
3915 fprintf(fp, "%f %f %f\n", f[0], f[1], f[2]);
3916 TrackballGetRotate(mp->mview->track, f);
3917 fprintf(fp, "%f %f %f %f\n", f[0], f[1], f[2], f[3]);
3920 fprintf(fp, "!:view\n");
3921 fprintf(fp, "show_unit_cell %d\n", mp->mview->showUnitCell);
3922 fprintf(fp, "show_periodic_box %d\n", mp->mview->showPeriodicBox);
3923 fprintf(fp, "show_expanded_atoms %d\n", mp->mview->showExpandedAtoms);
3924 fprintf(fp, "show_ellipsoids %d\n", mp->mview->showEllipsoids);
3925 fprintf(fp, "show_hydrogens %d\n", mp->mview->showHydrogens);
3926 fprintf(fp, "show_dummy_atoms %d\n", mp->mview->showDummyAtoms);
3927 fprintf(fp, "show_rotation_center %d\n", mp->mview->showRotationCenter);
3928 fprintf(fp, "show_graphite_flag %d\n", mp->mview->showGraphiteFlag);
3929 fprintf(fp, "show_graphite %d\n", mp->mview->showGraphite);
3930 fprintf(fp, "show_periodic_image_flag %d\n", mp->mview->showPeriodicImageFlag);
3931 fprintf(fp, "show_periodic_image %d %d %d %d %d %d\n",
3932 mp->mview->showPeriodicImage[0], mp->mview->showPeriodicImage[1],
3933 mp->mview->showPeriodicImage[2], mp->mview->showPeriodicImage[3],
3934 mp->mview->showPeriodicImage[4], mp->mview->showPeriodicImage[5]);
3943 MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3948 fp = fopen(fname, "wb");
3950 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
3954 fprintf(fp, "PSF\n\n");
3955 fprintf(fp, " 1 !NTITLE\n");
3956 fprintf(fp, " REMARKS FILENAME=\n");
3960 fprintf(fp, "%8d !NATOM\n", mp->natoms);
3961 for (i = 0; i < mp->natoms; i++) {
3963 ap = ATOM_AT_INDEX(mp->atoms, i);
3964 fprintf(fp, "%8d ", i + 1);
3965 if (ap->resSeq >= 10000) {
3966 fmt = "%-3.3s %-5d ";
3968 fmt = "%-4.4s %-4d ";
3970 fprintf(fp, fmt, ap->segName, ap->resSeq);
3971 fprintf(fp, "%-3.3s %-4.4s %-4.4s %12.6f %8.4f 0\n",
3972 ap->resName, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->charge, ap->weight);
3977 fprintf(fp, "%8d !NBOND: bonds\n", mp->nbonds);
3978 for (i = 0; i < mp->nbonds * 2; i++) {
3979 fprintf(fp, "%8d", mp->bonds[i] + 1);
3988 fprintf(fp, "%8d !NTHETA: angles\n", mp->nangles);
3989 for (i = 0; i < mp->nangles * 3; i++) {
3990 fprintf(fp, "%8d", mp->angles[i] + 1);
3999 fprintf(fp, "%8d !NPHI: dihedrals\n", mp->ndihedrals);
4000 for (i = 0; i < mp->ndihedrals * 4; i++) {
4001 fprintf(fp, "%8d", mp->dihedrals[i] + 1);
4010 fprintf(fp, "%8d !NIMPHI: impropers\n", mp->nimpropers);
4011 for (i = 0; i < mp->nimpropers * 4; i++) {
4012 fprintf(fp, "%8d", mp->impropers[i] + 1);
4020 fprintf(fp, "%8d !NDON: donors\n\n", 0);
4021 fprintf(fp, "%8d !NACC: acceptors\n\n", 0);
4022 fprintf(fp, "%8d !NNB: non-bonding exclusions\n\n", 0);
4023 for (i = 0; i < mp->natoms; i++) {
4024 fprintf(fp, "%8d", 0);
4031 fprintf(fp, "%8d !NGRP: groups\n", 1);
4032 fprintf(fp, " 0 0 0\n");
4036 if (i > 5 && strcmp(fname + i - 5, ".psfx") == 0) {
4037 /* Extended psf (with coordinates and other info) */
4038 fprintf(fp, "%8d !COORD: coordinates\n", mp->natoms);
4039 for (i = 0; i < mp->natoms; i++) {
4041 ap = ATOM_AT_INDEX(mp->atoms, i);
4043 fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->aname);
4047 if (mp->nframes > 0) {
4048 int fn; /* Frame number */
4049 for (fn = 0; fn < ap->nframes; fn++) {
4050 fprintf(fp, "%8d !COORD: coordinates for frame %d\n", mp->natoms, fn);
4051 for (i = 0; i < mp->natoms; i++) {
4053 ap = ATOM_AT_INDEX(mp->atoms, i);
4054 if (ap->frames == NULL || fn >= ap->nframes)
4058 fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->name);
4071 MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
4076 fp = fopen(fname, "wb");
4078 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
4082 for (i = 0; i < mp->natoms; i++) {
4084 ap = ATOM_AT_INDEX(mp->atoms, i);
4085 if (ap->resSeq >= 10000) {
4086 snprintf(buf, sizeof buf, "%c%03d", 'A' + (ap->resSeq - 10000) / 1000, ap->resSeq % 1000);
4088 snprintf(buf, sizeof buf, "%4d", ap->resSeq);
4090 fprintf(fp, "ATOM %5d %-4.4s%1.1s%-3.3s %1.1s%4.4s%1.1s "
4091 "%8.3f%8.3f%8.3f %5.2f %5.2f "
4092 "%-4.4s%-2.2s%-2d\n",
4093 i + 1, ap->aname, " ", ap->resName, " ", buf, " ",
4094 ap->r.x, ap->r.y, ap->r.z, ap->occupancy, ap->tempFactor,
4095 ap->segName, ap->element, ap->intCharge);
4097 for (i = 0; i < mp->natoms; i++) {
4099 ap = ATOM_AT_INDEX(mp->atoms, i);
4100 cp = AtomConnectData(&ap->connect);
4101 for (j = 0; j < ap->connect.count; j++) {
4105 fprintf(fp, "CONECT%5d", i + 1);
4107 fprintf(fp, "%5d", cp[j] + 1);
4112 fprintf(fp, "END\n");
4118 MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
4121 SFloat32 *xp, *yp, *zp;
4124 if (mp == NULL || mp->natoms == 0) {
4125 snprintf(errbuf, errbufsize, "Molecule is empty");
4128 memset(&dcd, 0, sizeof(dcd));
4129 dcd.natoms = mp->natoms;
4130 dcd.nframes = MoleculeGetNumberOfFrames(mp);
4131 if (dcd.nframes == 0) {
4132 snprintf(errbuf, errbufsize, "no frame is present");
4135 dcd.nstart = mp->startStep;
4136 dcd.ninterval = mp->stepsPerFrame;
4137 if (dcd.ninterval == 0)
4139 dcd.nend = dcd.nstart + (dcd.nframes - 1) * dcd.ninterval;
4140 if (mp->cell != NULL)
4142 dcd.delta = mp->psPerStep;
4143 if (dcd.delta == 0.0)
4146 n = DcdCreate(fname, &dcd);
4149 snprintf(errbuf, errbufsize, "Cannot create dcd file");
4151 snprintf(errbuf, errbufsize, "Cannot write dcd header");
4156 xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4157 yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4158 zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4159 if (xp == NULL || yp == NULL || zp == NULL) {
4160 snprintf(errbuf, errbufsize, "Cannot allocate memory");
4167 for (n = 0; n < dcd.nframes; n++) {
4170 for (i = 0, ap = mp->atoms; i < dcd.natoms && mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4172 if (ap->frames == NULL || n >= ap->nframes)
4180 if (i < dcd.natoms) {
4181 size_t sz = (dcd.natoms - i) * sizeof(SFloat32);
4182 memset(xp + i, 0, sz);
4183 memset(yp + i, 0, sz);
4184 memset(zp + i, 0, sz);
4186 if (n < mp->nframe_cells && mp->frame_cells != NULL) {
4187 Vector *cp = &(mp->frame_cells[n * 4]);
4188 dcd.globalcell[0] = VecLength(cp[0]);
4189 dcd.globalcell[2] = VecLength(cp[1]);
4190 dcd.globalcell[5] = VecLength(cp[2]);
4191 dcd.globalcell[1] = VecDot(cp[0], cp[1]) / (dcd.globalcell[0] * dcd.globalcell[2]);
4192 dcd.globalcell[3] = VecDot(cp[0], cp[2]) / (dcd.globalcell[0] * dcd.globalcell[5]);
4193 dcd.globalcell[4] = VecDot(cp[1], cp[2]) / (dcd.globalcell[2] * dcd.globalcell[5]);
4195 if (DcdWriteFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
4196 snprintf(errbuf, errbufsize, "Write error in dcd file");
4212 MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
4218 fp = fopen(fname, "wb");
4220 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
4223 if (mp->cell != NULL) {
4224 fprintf(fp, "Bounding box:\n");
4225 for (i = 0; i < 3; i++) {
4226 v = mp->cell->axes[i];
4227 fprintf(fp, "%.3f %.3f %.3f %d\n", v.x, v.y, v.z, mp->cell->flags[i]);
4229 fprintf(fp, "Bounding box origin:\n");
4230 v = mp->cell->origin;
4231 fprintf(fp, "%.3f %.3f %.3f\n", v.x, v.y, v.z);
4238 sCompareByElement(const void *ap, const void *bp)
4240 return ((*(Atom **)bp)->atomicNumber - (*(Atom **)ap)->atomicNumber);
4244 sMakeAdc(int n, int base, Symop symop)
4247 if (SYMOP_ALIVE(symop)) {
4249 sym = (symop.dx + 5) * 10000 + (symop.dy + 5) * 1000 + (symop.dz + 5) * 100 + symop.sym + 1;
4254 return (an + 1) * 100000 + sym;
4258 sCompareAdc(const void *ap, const void *bp)
4260 int n = *((Int *)ap) % 100000 - *((Int *)bp) % 100000;
4262 n = *((Int *)ap) / 100000 - *((Int *)bp) / 100000;
4267 sOutputAtomListInstructions(FILE *fp, int natoms, Atom *atoms)
4269 int i, j, k, an, sym;
4272 adc = (Int *)malloc(sizeof(Int) * natoms);
4275 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4276 if (ap->exflags & kAtomHiddenFlag)
4278 adc[i] = sMakeAdc(i, ap->symbase, ap->symop);
4280 mergesort(adc, natoms, sizeof(Int), sCompareAdc);
4282 /* Create the atom list */
4284 for (i = j = k = 0; i < natoms; i++) {
4285 int an1 = adc[i] / 100000;
4286 int sym1 = adc[i] % 100000;
4287 if (sym == sym1 && an1 == an + 1) {
4294 /* Output the last atom with a minus sign */
4295 adc[j++] = -(an * 100000 + sym);
4296 /* Output this atom */
4303 adc[j++] = -(an * 100000 + sym);
4305 /* Create the instruction cards */
4306 for (i = k = 0; i < j; i++) {
4308 fprintf(fp, " 401");
4309 fprintf(fp, "%9d", adc[i]);
4311 if (i == j - 1 || k == 6 || (k == 5 && i < j - 2 && adc[i + 2] < 0)) {
4320 sEllipsoidType(int an)
4322 return (an >= 18 ? 3 : (an >= 2 && an != 6 ? 2 : (an > 0 ? 1 : 0)));
4326 sOutputAtomTypeInstructions(FILE *fp, int natoms, Atom *atoms)
4330 int etype, elast, istart, ilast, n1, n2;
4331 elast = istart = ilast = -1;
4332 for (i = 0, ap = atoms; i <= natoms; i++, ap++) {
4334 if (SYMOP_ALIVE(ap->symop))
4336 if (ap->exflags & kAtomHiddenFlag)
4338 etype = sEllipsoidType(ap->atomicNumber);
4343 } else if (elast == etype && ilast == i - 1) {
4348 /* Output the instruction card for the 'last' block of atoms */
4351 n1 = 4; n2 = 0; break;
4353 n1 = 4; n2 = 5; break;
4355 n1 = 1; n2 = 0; break;
4357 fprintf(fp, " 1 715 %8d 0 %8d 0 0.100 0.000 0.000\n", n1, n2);
4358 fprintf(fp, " %9d%9d\n", istart + 1, ilast + 1);
4365 sCompareBondType(const void *ap, const void *bp)
4367 /* Descending order */
4368 return *((int *)bp) - *((int *)ap);
4372 sOutputBondInstructions(FILE *fp, int natoms, Atom *atoms, int overlap_correction)
4376 int i, j, n[5], an, count, n1, n2, k;
4380 static const float sBondRad[4] = {0.060, 0.060, 0.060, 0.040};
4381 static const int sBondShade[4] = {5, 3, 1, 1};
4383 n[0] = n[1] = n[2] = n[3] = 0; /* Start index of 3rd row atoms (and higher), 2nd row, 1st row, and H */
4385 for (i = natoms - 1, ap = atoms + i; i >= 0; i--, ap--) {
4386 an = ap->atomicNumber;
4398 if (overlap_correction)
4399 strcpy(buf, " 2 1001 0.000\n");
4401 strcpy(buf, " 2 812\n");
4403 for (i = 0; i < 4; i++) {
4404 for (j = i; j < 4; j++) {
4405 /* Examine bonds between "group i" and "group j" */
4408 double min_bond = 10000.0; /* Minimum distance between bound atoms */
4409 double min_nonbond = 10000.0; /* Minimum distance between non-bound atoms */
4410 double max_bond = -10000.0; /* Maximum distance between bound atoms */
4411 int count_exbond = 0; /* Number of explicit bonds in this group */
4412 for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
4413 for (n2 = n[j], ap2 = atoms + n2; n2 < n[j + 1]; n2++, ap2++) {
4416 VecSub(dr, ap->r, ap2->r);
4418 cp = AtomConnectData(&ap->connect);
4419 for (k = ap->connect.count - 1; k >= 0; k--) {
4424 /* n1 and n2 are bound */
4430 /* n1 and n2 are not bound */
4431 if (d < min_nonbond)
4436 if (min_bond == 10000.0)
4437 continue; /* No bonds between these groups */
4439 if (max_bond + 0.002 < min_nonbond)
4442 max_bond = min_nonbond - 0.002;
4443 /* Some bonds may be omitted, so scan all bonds again */
4444 for (n1 = n[i], ap = ATOM_AT_INDEX(atoms, n1); n1 < n[i + 1]; n1++, ap = ATOM_NEXT(ap)) {
4445 cp = AtomConnectData(&ap->connect);
4446 for (k = ap->connect.count - 1; k >= 0; k--) {
4448 if (n2 < n[j] || n2 >= n[j + 1])
4451 VecSub(dr, ap->r, ap2->r);
4454 /* This bond should be explicitly defined */
4456 if (count_exbond == 0) {
4457 adc1 = -(i + 1); /* Bond type */
4458 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4460 adc1 = sMakeAdc(n1, ap->symbase, ap->symop);
4461 adc2 = sMakeAdc(n2, ap2->symbase, ap2->symop);
4462 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4463 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc2);
4469 /* Output the last instruction card */
4471 /* Make a new trailer card */
4472 snprintf(buf, sizeof(buf), " 2 %3d%3d%3d%3d%3d%6.3f%6.3f%6.3f\n", n[i]+1, n[i+1], n[j]+1, n[j+1], sBondShade[i], min_bond, max_bond, sBondRad[i]);
4477 /* Output the last trailer card */
4482 if (count == 0 && overlap_correction) {
4483 /* 1001 card is not yet written, so write it */
4487 snprintf(buf, sizeof(buf), " 1 %3d", (overlap_correction ? 821 : 811));
4488 k = -exbonds[0] - 1; /* Bond type for the first block */
4489 i = 1; /* Index for exbonds[] */
4490 j = 0; /* Count in this block */
4491 while (i <= nexbonds) {
4492 if (j >= 29 || i == nexbonds || exbonds[i] < 0) {
4496 /* The trailer card */
4497 fprintf(fp, " %3d %6.3f\n", sBondShade[k], sBondRad[k]);
4501 k = -exbonds[i++] - 1; /* The new bond type */
4503 } else if (j > 0 && j % 3 == 0) {
4509 snprintf(buf + 9 + (j % 3) * 18, sizeof(buf) - 9 - (j % 3) * 18, "%9d%9d\n", n1, n2);
4518 /* Explicit bond table, sorted by bond type */
4519 for (i = j = 0; i < mp->nbonds; i++) {
4520 n1 = mp->bonds[i * 2];
4521 n2 = mp->bonds[i * 2 + 1];
4522 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
4523 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
4524 if ((ap1->exflags & kAtomHiddenFlag) || (ap2->exflags & kAtomHiddenFlag))
4526 if (ap1->atomicNumber > 18 || ap2->atomicNumber > 18) {
4528 } else if (ap1->atomicNumber > 1 && ap1->atomicNumber > 1) {
4534 ip[j * 3 + 1] = sMakeAdc(n1, ap1->symbase, ap1->symop);
4535 ip[j * 3 + 2] = sMakeAdc(n2, ap2->symbase, ap2->symop);
4538 mergesort(ip, j, sizeof(int) * 3, sCompareBondType);
4540 /* Output instruction cards */
4541 strcpy(buf, " 1 811");
4542 for (i = n1 = 0; i < j; i++) {
4543 n2 = (n1 % 3) * 18 + 9;
4544 snprintf(buf + n2, 80 - n2, "%9d%9d\n", ip[i * 3 + 1], ip[i * 3 + 2]);
4545 if (i == j - 1 || n1 >= 29 || ip[i * 3] != ip[i * 3 + 3]) {
4546 /* End of this instruction */
4549 switch (ip[i * 3]) {
4550 case 3: rad = 0.06; nshades = 5; break;
4551 case 2: rad = 0.06; nshades = 1; break;
4552 default: rad = 0.04; nshades = 1; break;
4554 fprintf(fp, " %3d %6.3f\n", nshades, rad);
4555 strcpy(buf, " 1 811");
4558 } else if (n1 % 3 == 2) {
4569 MoleculeWriteToTepFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
4572 int i, j, natoms, *ip;
4574 Atom *ap, *atoms, **app;
4576 static Double sUnit[] = {1, 1, 1, 90, 90, 90};
4580 /* Create sorted array of atoms */
4581 natoms = mp->natoms;
4582 atoms = (Atom *)calloc(sizeof(Atom), natoms);
4583 app = (Atom **)calloc(sizeof(Atom *), natoms);
4584 ip = (int *)calloc(sizeof(int), natoms);
4585 if (atoms == NULL || app == NULL || ip == NULL) {
4586 snprintf(errbuf, errbufsize, "Cannot allocate memory");
4589 /* Sort the atom pointer by atomic number */
4590 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
4592 mergesort(app, natoms, sizeof(Atom *), sCompareByElement);
4593 for (i = 0; i < natoms; i++) {
4594 /* ip[old_index] is new_index */
4595 ip[app[i] - mp->atoms] = i;
4597 /* Copy the atom record to atoms[] */
4598 /* The 'v' member contains crystallographic coordinates */
4599 /* The connection table and symbase are renumbered */
4600 /* Hidden flags are modified to reflect the visibility in the MainView */
4601 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4602 AtomDuplicateNoFrame(ap, app[i]);
4603 /* memmove(ap, app[i], gSizeOfAtomRecord); */
4604 MoleculeCartesianToXtal(mp, &(ap->v), &(ap->r));
4605 cp = AtomConnectData(&ap->connect);
4606 for (j = ap->connect.count - 1; j >= 0; j--) {
4609 if (SYMOP_ALIVE(ap->symop))
4610 ap->symbase = ip[ap->symbase];
4611 if (MainView_isAtomHidden(mp->mview, i)) {
4612 ap->exflags |= kAtomHiddenFlag;
4614 ap->exflags &= ~kAtomHiddenFlag;
4620 fp = fopen(fname, "wb");
4622 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
4628 fprintf(fp, "Generated by Molby\n");
4631 if (mp->cell != NULL) {
4632 dp = mp->cell->cell;
4636 fprintf(fp, "%9.3f%9.3f%9.3f%9.3f%9.3f%9.3f\n", dp[0], dp[1], dp[2], dp[3], dp[4], dp[5]);
4638 /* Symmetry operations */
4639 if (mp->nsyms > 0) {
4640 for (i = 0; i < mp->nsyms; i++) {
4642 fprintf(fp, "%c%14g%3g%3g%3g%15g%3g%3g%3g%15g%3g%3g%3g\n", (i == mp->nsyms - 1 ? '1' : ' '), dp[9], dp[0], dp[1], dp[2], dp[10], dp[3], dp[4], dp[5], dp[11], dp[6], dp[7], dp[8]);
4645 fprintf(fp, "1 0 1 0 0 0 0 1 0 0 0 0 1\n");
4649 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4650 /* The 'v' field contains crystallographic coordinates */
4651 fprintf(fp, " %4.4s%22s%9.4f%9.4f%9.4f%9d\n", ap->aname, "", ap->v.x, ap->v.y, ap->v.z, 0);
4652 if (ap->aniso != NULL) {
4653 dp = ap->aniso->bij;
4654 fprintf(fp, " %8.5f%9.6f%9.6f%9.6f%9.6f%9.6f%9d\n", dp[0], dp[1], dp[2], dp[3], dp[4], dp[5], 0);
4656 Double temp = ap->tempFactor;
4659 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", temp, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4662 /* Special points */
4664 Vector camera, lookat, up, xvec, yvec, zvec;
4665 MainView_getCamera(mp->mview, &camera, &lookat, &up);
4666 VecSub(zvec, lookat, camera);
4667 VecCross(xvec, zvec, up);
4668 NormalizeVec(&xvec, &xvec);
4669 NormalizeVec(&yvec, &up);
4670 VecInc(xvec, lookat);
4671 VecInc(yvec, lookat);
4672 MoleculeCartesianToXtal(mp, &lookat, &lookat);
4673 MoleculeCartesianToXtal(mp, &xvec, &xvec);
4674 MoleculeCartesianToXtal(mp, &yvec, &yvec);
4675 fprintf(fp, " ORGN %9g%9g%9g 0\n", 0.0, 0.0, 0.0);
4676 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4677 fprintf(fp, " CNTR %9g%9g%9g 0\n", lookat.x, lookat.y, lookat.z);
4678 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4679 fprintf(fp, " X %9g%9g%9g 0\n", xvec.x, xvec.y, xvec.z);
4680 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4681 fprintf(fp, " Y %9g%9g%9g 0\n", yvec.x, yvec.y, yvec.z);
4682 fprintf(fp, "1%8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4686 fprintf(fp, " 201\n");
4687 fprintf(fp, " 205 12\n");
4688 fprintf(fp, " 301 6.6 6.6 0 0.8\n");
4689 sOutputAtomListInstructions(fp, natoms, atoms);
4690 fprintf(fp, " 501%4d55501%4d55501%4d55501%4d55501%4d55501 1\n", natoms + 2, natoms + 2, natoms + 3, natoms + 2, natoms + 4);
4691 fprintf(fp, " 502 1 0.0 2 0.0 3 0.0\n");
4692 fprintf(fp, " 604 1.538\n");
4694 sOutputBondInstructions(fp, natoms, atoms, 1);
4695 sOutputAtomTypeInstructions(fp, natoms, atoms);
4696 sOutputBondInstructions(fp, natoms, atoms, 0);
4698 for (i = 0; i < natoms; i++) {
4699 AtomClean(atoms + i);
4703 fprintf(fp, " 202\n");
4704 fprintf(fp, " 0 -1\n");
4710 MoleculeDump(Molecule *mol)
4715 for (i = 0; i < mol->natoms; i++) {
4717 ap = ATOM_AT_INDEX(mol->atoms, i);
4718 snprintf(buf1, sizeof buf1, "%3.4s.%d", ap->resName, ap->resSeq);
4719 fprintf(stderr, "%4d %-7s %-4.6s %-4.6s %-2.2s %7.3f %7.3f %7.3f %6.3f [", i, buf1, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->element, ap->r.x, ap->r.y, ap->r.z, ap->charge);
4720 cp = AtomConnectData(&ap->connect);
4721 for (j = 0; j < ap->connect.count; j++) {
4722 fprintf(stderr, "%s%d", (j > 0 ? "," : ""), cp[j]);
4724 fprintf(stderr, "]\n");
4728 #pragma mark ====== MD support (including modification of Molecule) ======
4730 /* Call md_prepare for the MDArena. If MDArena has not been created, a new arena is created.
4731 If something goes wrong, returns 1 (for missing parameters) or -1 (more serious error).
4732 If retmsg is not NULL, a message describing the problem is returned there. This message
4733 must be free'd by the caller. */
4735 MoleculePrepareMDArena(Molecule *mol, int check_only, char **retmsg)
4738 Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
4740 IntGroup *ig1, *ig2, *ig3;
4741 MDArena *arena = mol->arena;
4743 if (arena == NULL) {
4746 } else if (arena->xmol != mol)
4747 md_arena_set_molecule(arena, mol);
4749 arena->is_initialized = 0;
4751 /* Rebuild the tables */
4752 ig1 = ig2 = ig3 = NULL;
4753 nangles = MoleculeFindMissingAngles(mol, &angles);
4754 ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
4755 nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
4757 ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
4758 MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
4760 IntGroupRelease(ig1);
4762 if (ndihedrals > 0) {
4763 ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
4764 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
4766 IntGroupRelease(ig2);
4768 if (nimpropers > 0) {
4769 ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
4770 MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
4772 IntGroupRelease(ig3);
4775 /* Prepare parameters and internal information */
4776 msg = md_prepare(arena, check_only);
4778 /* Some parameters are missing? */
4780 if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
4784 asprintf(retmsg, "cannot initialize for MD: %s", msg);
4789 /* The local parameter list is updated */
4792 if (mol->par == NULL)
4793 mol->par = ParameterNew();
4794 for (parType = kFirstParType; parType <= kLastParType; parType++) {
4795 /* Delete global and undefined parameters */
4796 UnionPar *up, *upbuf;
4798 ig1 = IntGroupNew();
4799 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
4800 if (up->bond.src != 0)
4801 IntGroupAdd(ig1, idx, 1);
4803 if (IntGroupGetCount(ig1) > 0)
4804 MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
4805 IntGroupRelease(ig1);
4806 /* Copy global and undefined parameters from arena and insert to mol->par */
4807 nparams = ParameterGetCountForType(arena->par, parType);
4810 upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
4811 ig1 = IntGroupNew();
4812 ig2 = IntGroupNew();
4813 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
4814 if (up->bond.src > 0)
4815 IntGroupAdd(ig1, idx, 1); /* Global parameter */
4816 else if (up->bond.src < 0)
4817 IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
4819 if ((count = IntGroupGetCount(ig1)) > 0) {
4820 /* Insert global parameters (at the top) */
4821 ParameterCopy(arena->par, parType, upbuf, ig1);
4822 ig3 = IntGroupNewWithPoints(0, count, -1);
4823 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
4824 IntGroupRelease(ig3);
4826 if ((count = IntGroupGetCount(ig2)) > 0) {
4827 /* Insert undefined parameters (at the bottom) */
4828 ParameterCopy(arena->par, parType, upbuf, ig2);
4829 idx = ParameterGetCountForType(mol->par, parType);
4830 ig3 = IntGroupNewWithPoints(idx, count, -1);
4831 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
4832 IntGroupRelease(ig3);
4834 IntGroupRelease(ig2);
4835 IntGroupRelease(ig1);
4838 mol->needsMDRebuild = 0; /* We know the "modified" parameters are consistent with the MDArena */
4843 *retmsg = strdup(msg);
4848 #pragma mark ====== Serialize ======
4851 MoleculeDeserialize(const char *data, Int length, Int *timep)
4860 par = ParameterNew();
4864 while (length >= 12) {
4865 const char *ptr = data + 8 + sizeof(Int);
4866 int len = *((const Int *)(data + 8));
4868 if (strcmp(data, "ATOM") == 0) {
4869 n = len / gSizeOfAtomRecord;
4870 NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, n);
4871 memmove(mp->atoms, ptr, len);
4872 } else if (strcmp(data, "ANISO") == 0) {
4874 n = len / (sizeof(Int) + sizeof(Aniso));
4875 for (i = 0; i < n; i++) {
4876 j = *((const Int *)ptr);
4877 if (j < 0 || j >= mp->natoms)
4879 ap = ATOM_AT_INDEX(mp->atoms, j);
4880 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
4881 if (ap->aniso == NULL)
4883 *(ap->aniso) = *((Aniso *)(ptr + sizeof(Int)));
4884 ptr += sizeof(Int) + sizeof(Aniso);
4886 } else if (strcmp(data, "FRAME") == 0) {
4888 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4889 if (ap->nframes == 0)
4891 ap->frames = (Vector *)malloc(sizeof(Vector) * ap->nframes);
4892 if (ap->frames == NULL)
4894 memmove(ap->frames, ptr, sizeof(Vector) * ap->nframes);
4895 ptr += sizeof(Vector) * ap->nframes;
4897 } else if (strcmp(data, "EXTCON") == 0) {
4899 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4900 if (ap->connect.count <= ATOM_CONNECT_LIMIT)
4902 n = ap->connect.count;
4903 ap->connect.count = 0;
4904 ap->connect.u.ptr = NULL;
4905 NewArray(&(ap->connect.u.ptr), &(ap->connect.count), sizeof(Int), n);
4906 memmove(ap->connect.u.ptr, ptr, sizeof(Int) * n);
4907 ptr += sizeof(Int) * n;
4909 } else if (strcmp(data, "BOND") == 0) {
4910 n = len / (sizeof(Int) * 2);
4911 NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, n);
4912 memmove(mp->bonds, ptr, len);
4913 } else if (strcmp(data, "ANGLE") == 0) {
4914 n = len / (sizeof(Int) * 3);
4915 NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, n);
4916 memmove(mp->angles, ptr, len);
4917 } else if (strcmp(data, "DIHED") == 0) {
4918 n = len / (sizeof(Int) * 4);
4919 NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, n);
4920 memmove(mp->dihedrals, ptr, len);
4921 } else if (strcmp(data, "IMPROP") == 0) {
4922 n = len / (sizeof(Int) * 4);
4923 NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, n);
4924 memmove(mp->impropers, ptr, len);
4925 } else if (strcmp(data, "RESIDUE") == 0) {
4927 NewArray(&mp->residues, &mp->nresidues, 4, n);
4928 memmove(mp->residues, ptr, len);
4929 } else if (strcmp(data, "CELL") == 0) {
4930 mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
4931 if (mp->cell == NULL)
4933 memmove(mp->cell, ptr, sizeof(XtalCell));
4934 } else if (strcmp(data, "SYMOP") == 0) {
4935 n = len / sizeof(Transform);
4936 NewArray(&mp->syms, &mp->nsyms, sizeof(Transform), n);
4937 memmove(mp->syms, ptr, len);
4938 } else if (strcmp(data, "TIME") == 0) {
4940 *timep = *((Int *)ptr);
4941 } else if (strcmp(data, "BONDPAR") == 0) {
4943 n = len / sizeof(BondPar);
4944 NewArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), n);
4945 memmove(par->bondPars, ptr, len);
4946 } else if (strcmp(data, "ANGPAR") == 0) {
4948 n = len / sizeof(AnglePar);
4949 NewArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), n);
4950 memmove(par->anglePars, ptr, len);
4951 } else if (strcmp(data, "DIHEPAR") == 0) {
4953 n = len / sizeof(TorsionPar);
4954 NewArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), n);
4955 memmove(par->dihedralPars, ptr, len);
4956 } else if (strcmp(data, "IMPRPAR") == 0) {
4958 n = len / sizeof(TorsionPar);
4959 NewArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), n);
4960 memmove(par->improperPars, ptr, len);
4961 } else if (strcmp(data, "VDWPAR") == 0) {
4963 n = len / sizeof(VdwPar);
4964 NewArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), n);
4965 memmove(par->vdwPars, ptr, len);
4966 } else if (strcmp(data, "VDWPPAR") == 0) {
4968 n = len / sizeof(VdwPairPar);
4969 NewArray(&par->vdwpPars, &par->nvdwpPars, sizeof(VdwPairPar), n);
4970 memmove(par->vdwpPars, ptr, len);
4971 } else if (strcmp(data, "VCUTPAR") == 0) {
4973 n = len / sizeof(VdwCutoffPar);
4974 NewArray(&par->vdwCutoffPars, &par->nvdwCutoffPars, sizeof(VdwCutoffPar), n);
4975 memmove(par->vdwCutoffPars, ptr, len);
4977 len += 8 + sizeof(Int);
4981 if (mp->par == NULL)
4982 ParameterRelease(par);
4983 /* result = MoleculeRebuildTablesFromConnects(mp);
4989 Panic("Low memory while deserializing molecule data");
4990 return NULL; /* Not reached */
4993 Panic("internal error: bad format during deserializing molecule data");
4994 return NULL; /* Not reached */
4998 MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep)
5001 int len, len_all, i, naniso, nframes, nconnects;
5003 /* Array of atoms */
5004 len = 8 + sizeof(Int) + gSizeOfAtomRecord * mp->natoms;
5005 ptr = (char *)malloc(len);
5008 memmove(ptr, "ATOM\0\0\0\0", 8);
5009 *((Int *)(ptr + 8)) = gSizeOfAtomRecord * mp->natoms;
5010 p = ptr + 8 + sizeof(Int);
5011 memmove(p, mp->atoms, gSizeOfAtomRecord * mp->natoms);
5012 naniso = nframes = nconnects = 0;
5013 for (i = 0; i < mp->natoms; i++) {
5014 Atom *ap = ATOM_AT_INDEX(p, i);
5015 if (ap->aniso != NULL) {
5019 if (ap->frames != NULL) {
5020 nframes += ap->nframes;
5023 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5024 nconnects += ap->connect.count;
5025 ap->connect.u.ptr = NULL;
5030 /* Array of aniso */
5032 len = 8 + sizeof(Int) + (sizeof(Int) + sizeof(Aniso)) * naniso;
5033 ptr = (char *)realloc(ptr, len_all + len);
5037 memmove(p, "ANISO\0\0\0", 8);
5038 *((Int *)(p + 8)) = (sizeof(Int) + sizeof(Aniso)) * naniso;
5039 p += 8 + sizeof(Int);
5040 for (i = 0; i < mp->natoms; i++) {
5041 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
5042 if (ap->aniso != NULL) {
5044 *((Aniso *)(p + sizeof(Int))) = *(ap->aniso);
5045 p += sizeof(Int) + sizeof(Aniso);
5051 /* Array of frames */
5053 len = 8 + sizeof(Int) + sizeof(Vector) * nframes;
5054 ptr = (char *)realloc(ptr, len_all + len);
5058 memmove(p, "FRAME\0\0\0", 8);
5059 *((Int *)(p + 8)) = sizeof(Vector) * nframes;
5060 p += 8 + sizeof(Int);
5061 for (i = 0; i < mp->natoms; i++) {
5062 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
5063 if (ap->frames != NULL) {
5064 memmove(p, ap->frames, sizeof(Vector) * ap->nframes);
5065 p += sizeof(Vector) * ap->nframes;
5071 /* Array of connects */
5072 if (nconnects > 0) {
5073 len = 8 + sizeof(Int) + sizeof(Int) * nconnects;
5074 ptr = (char *)realloc(ptr, len_all + len);
5078 memmove(p, "EXTCON\0\0", 8);
5079 *((Int *)(p + 8)) = sizeof(Int) * nconnects;
5080 p += 8 + sizeof(Int);
5081 for (i = 0; i < mp->natoms; i++) {
5082 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
5083 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5084 memmove(p, ap->connect.u.ptr, sizeof(Int) * ap->connect.count);
5085 p += sizeof(Int) * ap->connect.count;
5091 /* Bonds, angles, dihedrals, impropers */
5092 if (mp->nbonds > 0) {
5093 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nbonds;
5094 ptr = (char *)realloc(ptr, len_all + len);
5098 memmove(p, "BOND\0\0\0\0", 8);
5099 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nbonds;
5100 p += 8 + sizeof(Int);
5101 memmove(p, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
5104 if (mp->nangles > 0) {
5105 len = 8 + sizeof(Int) + sizeof(Int) * 3 * mp->nangles;
5106 ptr = (char *)realloc(ptr, len_all + len);
5110 memmove(p, "ANGLE\0\0\0", 8);
5111 *((Int *)(p + 8)) = sizeof(Int) * 3 * mp->nangles;
5112 p += 8 + sizeof(Int);
5113 memmove(p, mp->angles, sizeof(Int) * 3 * mp->nangles);
5116 if (mp->ndihedrals > 0) {
5117 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->ndihedrals;
5118 ptr = (char *)realloc(ptr, len_all + len);
5122 memmove(p, "DIHED\0\0\0", 8);
5123 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->ndihedrals;
5124 p += 8 + sizeof(Int);
5125 memmove(p, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
5128 if (mp->nimpropers > 0) {
5129 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->nimpropers;
5130 ptr = (char *)realloc(ptr, len_all + len);
5134 memmove(p, "IMPROP\0\0", 8);
5135 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->nimpropers;
5136 p += 8 + sizeof(Int);
5137 memmove(p, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
5141 /* Array of residues */
5142 if (mp->nresidues > 0) {
5143 len = 8 + sizeof(Int) + 4 * mp->nresidues;
5144 ptr = (char *)realloc(ptr, len_all + len);
5148 memmove(p, "RESIDUE\0", 8);
5149 *((Int *)(p + 8)) = 4 * mp->nresidues;
5150 p += 8 + sizeof(Int);
5151 memmove(p, mp->residues, 4 * mp->nresidues);
5156 if (mp->cell != NULL) {
5157 len = 8 + sizeof(Int) + sizeof(XtalCell);
5158 ptr = (char *)realloc(ptr, len_all + len);
5162 memmove(p, "CELL\0\0\0\0", 8);
5163 *((Int *)(p + 8)) = sizeof(XtalCell);
5164 p += 8 + sizeof(Int);
5165 memmove(p, mp->cell, sizeof(XtalCell));
5169 /* Symmetry operations */
5170 if (mp->nsyms > 0) {
5171 len = 8 + sizeof(Int) + sizeof(Transform) * mp->nsyms;
5172 ptr = (char *)realloc(ptr, len_all + len);
5176 memmove(p, "SYMOP\0\0\0", 8);
5177 *((Int *)(p + 8)) = sizeof(Transform) * mp->nsyms;
5178 p += 8 + sizeof(Int);
5179 memmove(p, mp->syms, sizeof(Transform) * mp->nsyms);
5184 if (mp->par != NULL) {
5186 for (type = kFirstParType; type <= kLastParType; type++) {
5187 const char *parname;
5188 Int parsize, parcount;
5192 parname = "BONDPAR\0";
5193 parsize = sizeof(BondPar);
5194 parcount = mp->par->nbondPars;
5195 parptr = mp->par->bondPars;
5198 parname = "ANGPAR\0\0";
5199 parsize = sizeof(AnglePar);
5200 parcount = mp->par->nanglePars;
5201 parptr = mp->par->anglePars;
5203 case kDihedralParType:
5204 parname = "DIHEPAR\0";
5205 parsize = sizeof(TorsionPar);
5206 parcount = mp->par->ndihedralPars;
5207 parptr = mp->par->dihedralPars;
5209 case kImproperParType:
5210 parname = "IMPRPAR\0";
5211 parsize = sizeof(TorsionPar);
5212 parcount = mp->par->nimproperPars;
5213 parptr = mp->par->improperPars;
5216 parname = "VDWPAR\0\0";
5217 parsize = sizeof(VdwPar);
5218 parcount = mp->par->nvdwPars;
5219 parptr = mp->par->vdwPars;
5221 case kVdwPairParType:
5222 parname = "VDWPPAR\0";
5223 parsize = sizeof(VdwPairPar);
5224 parcount = mp->par->nvdwpPars;
5225 parptr = mp->par->vdwpPars;
5227 case kVdwCutoffParType:
5228 parname = "VCUTPAR\0";
5229 parsize = sizeof(VdwCutoffPar);
5230 parcount = mp->par->nvdwCutoffPars;
5231 parptr = mp->par->vdwCutoffPars;
5237 len = 8 + sizeof(Int) + parsize * parcount;
5238 ptr = (char *)realloc(ptr, len_all + len);
5242 memmove(p, parname, 8);
5243 *((Int *)(p + 8)) = parsize * parcount;
5244 p += 8 + sizeof(Int);
5245 memmove(p, parptr, parsize * parcount);
5253 time_t tm = time(NULL);
5254 len = 8 + sizeof(Int) + sizeof(Int);
5255 ptr = (char *)realloc(ptr, len_all + len);
5259 memmove(p, "TIME\0\0\0\0", 8);
5260 *((Int *)(p + 8)) = sizeof(Int);
5261 p += 8 + sizeof(Int);
5262 *((Int *)p) = (Int)tm;
5268 if (outLength != NULL)
5269 *outLength = len_all;
5273 Panic("Low memory while serializing a molecule data");
5274 return NULL; /* Not reached */
5277 #pragma mark ====== Search for bonds, angles, dihedrals, impropers ======
5280 sMoleculeSearchIncludingAtoms(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5284 IntGroup *gp = NULL;
5285 if (atomgroup == NULL)
5287 for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5288 for (j = 0; j < nsize; j++) {
5289 if (IntGroupLookup(atomgroup, ip[j], NULL) != 0) {
5292 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5293 Panic("Low memory while searching %s", msg);
5302 MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5306 return sMoleculeSearchIncludingAtoms(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5310 MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5314 return sMoleculeSearchIncludingAtoms(mp->nangles, mp->angles, 3, atomgroup, "angles");
5318 MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5322 return sMoleculeSearchIncludingAtoms(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5326 MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5330 return sMoleculeSearchIncludingAtoms(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5334 sMoleculeSearchAcrossAtomGroup(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5338 IntGroup *gp = NULL;
5339 if (atomgroup == NULL)
5341 for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5343 for (j = 0; j < nsize; j++) {
5345 kk = (IntGroupLookup(atomgroup, ip[j], NULL) != 0);
5349 /* This bond etc. crosses the atom group border */
5352 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5353 Panic("Low memory while searching %s", msg);
5362 MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5366 return sMoleculeSearchAcrossAtomGroup(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5370 MoleculeSearchAnglesAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5374 return sMoleculeSearchAcrossAtomGroup(mp->nangles, mp->angles, 3, atomgroup, "angles");
5378 MoleculeSearchDihedralsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5382 return sMoleculeSearchAcrossAtomGroup(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5386 MoleculeSearchImpropersAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5390 return sMoleculeSearchAcrossAtomGroup(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5393 /* Subroutine for MoleculeGuessBonds. It can be also used independently, but make sure that *outNbonds/*outBonds
5394 _correctly_ represents an array of two integers (as in mp->nbonds/mp->bonds). */
5395 /* Find atoms within the given "distance" from the given atom. */
5396 /* If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5397 the threshold distance is given by the sum of van der Waals radii times limit. */
5398 /* If triangle is non-zero, then only atoms with lower indexes than index are looked for. */
5400 MoleculeFindCloseAtoms(Molecule *mp, Int index, Double limit, Int *outNbonds, Int **outBonds, Int triangle)
5402 Int n1, n2, j, nlim, newbond[2];
5403 Double a1, a2, alim;
5405 Atom *ap = ATOM_AT_INDEX(mp->atoms, index);
5406 n1 = ap->atomicNumber;
5407 if (n1 >= 0 && n1 < gCountElementParameters)
5408 a1 = gElementParameters[n1].radius;
5409 else a1 = gElementParameters[6].radius;
5411 nlim = (triangle ? index : mp->natoms);
5412 for (j = 0; j < nlim; j++) {
5413 Atom *bp = ATOM_AT_INDEX(mp->atoms, j);
5416 n2 = bp->atomicNumber;
5417 if (n2 >= 0 && n2 < gCountElementParameters)
5418 a2 = gElementParameters[n2].radius;
5419 else a2 = gElementParameters[6].radius;
5425 alim = limit * (a1 + a2);
5426 if (VecLength2(dr) < alim * alim) {
5429 /* MoleculeAddBonds(mp, 1, newbonds); */
5430 AssignArray(outBonds, outNbonds, sizeof(Int) * 2, *outNbonds, newbond);
5436 /* Guess the bonds from the coordinates */
5437 /* If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5438 the threshold distance is given by the sum of van der Waals radii times limit. */
5440 MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds)
5442 Int nbonds, *bonds, i, newbond[2];
5443 /* int i, j, n1, n2;
5446 Double a1, a2, alim;
5448 ElementPar *p = gElementParameters; */
5451 for (i = 0; i < mp->natoms; i++) {
5452 MoleculeFindCloseAtoms(mp, i, limit, &nbonds, &bonds, 1);
5454 ap = ATOM_AT_INDEX(mp->atoms, i);
5455 n1 = ap->atomicNumber;
5456 if (n1 >= 0 && n1 < gCountElementParameters)
5458 else a1 = p[6].radius;
5460 for (j = 0; j < i; j++) {
5461 bp = ATOM_AT_INDEX(mp->atoms, j);
5462 n2 = bp->atomicNumber;
5463 if (n2 >= 0 && n2 < gCountElementParameters)
5465 else a2 = p[6].radius;
5471 alim = limit * (a1 + a2);
5472 if (VecLength2(dr) < alim * alim) {
5475 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5481 newbond[0] = kInvalidIndex;
5483 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5486 if (outNbonds != NULL)
5487 *outNbonds = nbonds;
5488 if (outBonds != NULL)
5493 /* Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information */
5495 MoleculeRebuildTablesFromConnects(Molecule *mp)
5497 int i, j, k, retval;
5504 if (mp->nbonds == 0) {
5505 for (i = 0; i < mp->natoms; i++) {
5506 ap = ATOM_AT_INDEX(mp->atoms, i);
5507 cp = AtomConnectData(&ap->connect);
5508 for (j = 0; j < ap->connect.count; j++) {
5514 /* MoleculeAddBonds() should not be used, because it assumes connects[] and
5515 bonds are already in sync */
5516 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
5517 /* retval = MoleculeAddBonds(mp, 1, ibuf);
5525 if (mp->nangles == 0) {
5526 for (i = 0; i < mp->natoms; i++) {
5527 ap = ATOM_AT_INDEX(mp->atoms, i);
5528 cp = AtomConnectData(&ap->connect);
5529 for (j = 0; j < ap->connect.count; j++) {
5530 for (k = j + 1; k < ap->connect.count; k++) {
5535 retval = MoleculeAddAngles(mp, ibuf, NULL);
5543 /* Find dihedrals */
5544 if (mp->ndihedrals == 0) {
5545 for (i = 0; i < mp->natoms; i++) {
5546 ap = ATOM_AT_INDEX(mp->atoms, i);
5547 cp = AtomConnectData(&ap->connect);
5548 for (j = 0; j < ap->connect.count; j++) {
5555 apjj = ATOM_AT_INDEX(mp->atoms, jj);
5556 cpjj = AtomConnectData(&apjj->connect);
5557 for (k = 0; k < ap->connect.count; k++) {
5561 for (m = 0; m < apjj->connect.count; m++) {
5563 if (mm == i || mm == kk)
5570 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
5579 /* Find impropers */
5580 if (mp->nimpropers == 0) {
5581 for (i = 0; i < mp->natoms; i++) {
5582 int i1, i2, i4, n1, n2, n4;
5583 ap = ATOM_AT_INDEX(mp->atoms, i);
5584 cp = AtomConnectData(&ap->connect);
5585 for (i1 = 0; i1 < ap->connect.count; i1++) {
5587 for (i2 = i1 + 1; i2 < ap->connect.count; i2++) {
5589 for (i4 = i2 + 1; i4 < ap->connect.count; i4++) {
5596 retval = MoleculeAddImpropers(mp, ibuf, NULL);
5605 mp->needsMDRebuild = 1;
5606 __MoleculeUnlock(mp);
5610 __MoleculeUnlock(mp);
5614 #pragma mark ====== Atom names ======
5616 /* Look for the n1-th atom in resno-th residue (n1 is 0-based) */
5618 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
5622 if (mp == NULL || mp->natoms == 0)
5625 for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5626 if (ap->resSeq == resno) {
5633 return lasti; /* max */
5638 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
5642 n = strtol(s, &p, 0);
5654 if ((p = strchr(s, ':')) != NULL) {
5655 /* Residue is specified */
5657 if ((pp = strchr(s, '.')) != NULL && pp < p) {
5658 /* Residue number is also specified */
5661 *resSeq = strtol(pp + 1, &ppp, 0);
5663 return -2; /* Bad format */
5664 while (isspace(*ppp))
5667 return -2; /* Bad format */
5670 /* Check whether the "residue name" is an integer */
5671 n = strtol(s, &pp, 0);
5673 while (isspace(*pp))
5675 if (*pp == 0 || *pp == ':') {
5678 return -2; /* Bad format */
5686 if (n >= sizeof(resName))
5687 n = sizeof(resName) - 1;
5688 strncpy(resName, s, n);
5696 strncpy(atomName, p, 4);
5701 /* Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer */
5703 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
5710 n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
5711 if (atomName[0] == 0) {
5712 if (n >= mp->natoms)
5713 n = -1; /* Out of range */
5716 for (n = 0; n < mp->natoms; n++) {
5717 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
5718 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
5719 && (resSeq < 0 || ap->resSeq == resSeq)
5720 && strncmp(atomName, ap->aname, 4) == 0) {
5724 return -1; /* Not found */
5728 MoleculeAreAtomsConnected(Molecule *mp, int n1, int n2)
5732 if (mp == NULL || n1 < 0 || n1 >= mp->natoms || n2 < 0 || n2 >= mp->natoms)
5734 ap = ATOM_AT_INDEX(mp->atoms, n1);
5735 cp = AtomConnectData(&ap->connect);
5736 for (i = 0; i < ap->connect.count; i++)
5744 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
5748 if (mp == NULL || index < 0 || index >= mp->natoms) {
5752 ap = mp->atoms + index;
5753 if (ap->resSeq != 0) {
5754 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
5758 snprintf(buf, bufsize, "%.4s", ap->aname);
5761 #pragma mark ====== Selection ======
5764 sMoleculeNotifyChangeSelection(Molecule *mp)
5766 /* TODO: Finer control of notification types may be necessary */
5767 MoleculeCallback_notifyModification(mp, 0);
5771 MoleculeSetSelection(Molecule *mp, IntGroup *select)
5776 IntGroupRetain(select);
5777 if (mp->selection != NULL)
5778 IntGroupRelease(mp->selection);
5779 mp->selection = select;
5780 sMoleculeNotifyChangeSelection(mp);
5784 MoleculeGetSelection(Molecule *mp)
5788 else return mp->selection;
5792 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
5794 if (mp->selection == NULL)
5795 mp->selection = IntGroupNew();
5797 IntGroupClear(mp->selection);
5798 IntGroupAdd(mp->selection, n1, 1);
5799 sMoleculeNotifyChangeSelection(mp);
5803 MoleculeUnselectAtom(Molecule *mp, int n1)
5805 if (mp->selection != NULL)
5806 IntGroupRemove(mp->selection, n1, 1);
5807 sMoleculeNotifyChangeSelection(mp);
5811 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
5813 if (mp->selection == NULL)
5814 mp->selection = IntGroupNew();
5815 IntGroupReverse(mp->selection, n1, 1);
5816 sMoleculeNotifyChangeSelection(mp);
5820 MoleculeIsAtomSelected(Molecule *mp, int n1)
5822 if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
5828 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
5830 if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
5836 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
5839 IntGroup *remain, *ig1, *ig2;
5841 remain = IntGroupNewFromIntGroup(remove);
5845 status = IntGroupReverse(remain, 0, mp->natoms);
5847 ig1 = IntGroupNew();
5851 status = IntGroupDifference(selection, remove, ig1);
5854 ig2 = IntGroupNew();
5858 status = IntGroupDeconvolute(ig1, remain, ig2);
5861 IntGroupRelease(remain);
5863 IntGroupRelease(ig1);
5868 IntGroupRelease(ig2);
5873 #pragma mark ====== Atom Equivalence ======
5877 struct sEqList *next;
5878 struct sEqList *link;
5881 static struct sEqList *sListBase = NULL;
5882 static struct sEqList *sListFree = NULL;
5884 static struct sEqList *
5888 if (sListFree != NULL) {
5890 sListFree = lp->next;
5891 lp->i[0] = lp->i[1] = 0;
5895 lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
5896 lp->link = sListBase;
5902 sFreeEqList(struct sEqList *list)
5904 list->next = sListFree;
5909 sDeallocateEqLists(void)
5911 struct sEqList *lp, *lp_link;
5912 for (lp = sListBase; lp != NULL; lp = lp_link) {
5921 sExistInEqList(int i, int idx, struct sEqList *list)
5923 while (list != NULL) {
5924 if (list->i[idx] == i)
5931 static struct sEqList *
5932 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
5935 struct sEqList *list1, *list2;
5936 Int ii, jj, ni, nj, *cpi, *cpj;
5937 api = ATOM_AT_INDEX(mol->atoms, i);
5938 apj = ATOM_AT_INDEX(mol->atoms, j);
5939 if (api->atomicNumber != apj->atomicNumber)
5941 list1 = sAllocEqList();
5947 if (i == j || (db[i] != NULL && db[i] == db[j]))
5949 cpi = AtomConnectData(&api->connect);
5950 cpj = AtomConnectData(&apj->connect);
5951 for (ni = 0; ni < api->connect.count; ni++) {
5953 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
5955 if (sExistInEqList(ii, 0, list1))
5958 for (nj = 0; nj < apj->connect.count; nj++) {
5960 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
5962 if (sExistInEqList(jj, 1, list1))
5964 list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
5968 if (list2 == NULL) {
5970 return NULL; /* No equivalent to ii */
5972 list1 = list2; /* ii is OK, try next */
5978 sDBInclude(Int *ip, int i)
5983 for (j = ip[0] - 1; j >= 0; j--) {
5991 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
5993 Int **db; /* List of equivalents for each atom */
5995 Atom *api, *apj, *apk;
5996 Int *cpi, *cpj, *ibuf, nibuf;
5997 int i, j, k, ii, jj, kk;
5998 if (mol == NULL || mol->natoms == 0)
6000 db = (Int **)calloc(sizeof(Int *), mol->natoms);
6004 /* Find the equivalent univalent atoms */
6005 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6006 if (api->connect.count < 2)
6008 cpi = AtomConnectData(&api->connect);
6009 for (j = 0; j < api->connect.count; j++) {
6013 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6015 AssignArray(&ibuf, &nibuf, sizeof(Int), n, &jj);
6017 apj = ATOM_AT_INDEX(mol->atoms, jj);
6018 if (apj->connect.count != 1 || db[jj] != NULL)
6020 cpj = AtomConnectData(&apj->connect);
6021 for (k = j + 1; k < api->connect.count; k++) {
6023 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
6025 apk = ATOM_AT_INDEX(mol->atoms, kk);
6026 if (apk->connect.count != 1 || db[kk] != NULL)
6028 if (apj->atomicNumber == apk->atomicNumber) {
6029 AssignArray(&ibuf, &nibuf, sizeof(Int), n, &kk);
6034 ip = (Int *)calloc(sizeof(Int), n + 1);
6038 memmove(ip + 1, ibuf, sizeof(Int) * n);
6039 for (k = 0; k < n; k++)
6049 /* Try matching (i,j) pair */
6050 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6051 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
6053 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
6054 struct sEqList *list;
6055 if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
6057 if (api->atomicNumber != apj->atomicNumber)
6058 continue; /* Different elements do not match */
6059 if (db[i] != NULL && db[i] == db[j])
6060 continue; /* Already equivalent */
6061 list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
6063 continue; /* (i,j) do not match */
6064 while (list != NULL) {
6067 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
6068 /* Merge db[ii] and db[jj] */
6069 k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
6070 ip = (Int *)calloc(sizeof(Int), k + 1);
6072 return NULL; /* Out of memory */
6073 if (db[ii] == NULL) {
6077 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
6080 if (db[jj] == NULL) {
6083 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
6092 for (k = 0; k < ip[0]; k++)
6096 printf("(%d,%d) matched: ", ii, jj);
6097 for (k = 0; k < ip[0]; k++) {
6098 printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
6108 /* Record the equivalent atoms with the lowest index for each atom */
6109 result = (Int *)calloc(sizeof(Int), mol->natoms);
6110 for (i = 0; i < mol->natoms; i++)
6112 for (i = 0; i < mol->natoms; i++) {
6113 if (result[i] >= 0 || (ip = db[i]) == NULL)
6116 for (j = 0; j < ip[0]; j++) {
6121 for (j = 0; j < ip[0]; j++) {
6122 result[ip[j + 1]] = k;
6123 db[ip[j + 1]] = NULL;
6127 sDeallocateEqLists();
6131 #pragma mark ====== Symmetry expansion ======
6134 MoleculeGetTransformForSymop(Molecule *mp, Symop symop, Transform *tf, int is_cartesian)
6137 if (mp == NULL || mp->cell == NULL)
6139 if (symop.sym >= mp->nsyms && symop.sym != 0)
6141 memmove(*tf, SYMMETRY_AT_INDEX(mp->syms, symop.sym), sizeof(Transform));
6142 (*tf)[9] += symop.dx;
6143 (*tf)[10] += symop.dy;
6144 (*tf)[11] += symop.dz;
6146 TransformMul(t, *tf, mp->cell->rtr);
6147 TransformMul(*tf, mp->cell->tr, t);
6153 MoleculeGetSymopForTransform(Molecule *mp, const Transform tf, Symop *symop, int is_cartesian)
6157 if (mp == NULL || mp->cell == NULL)
6162 TransformMul(t, tf, mp->cell->tr);
6163 TransformMul(t, mp->cell->rtr, t);
6165 memmove(t, tf, sizeof(Transform));
6167 for (i = 0; i < mp->nsyms; i++) {
6168 Transform *tp = mp->syms + i;
6169 for (j = 0; j < 9; j++) {
6170 if (fabs((*tp)[j] - t[j]) > 1e-4)
6174 for (j = 9; j < 12; j++) {
6175 double f1 = t[j] - (*tp)[j];
6176 double f2 = floor(f1 + 0.5);
6177 if (fabs(f1 - f2) > 1e-4)
6187 symop->alive = (SYMOP_ALIVE((*symop)) != 0);
6192 return -3; /* Not found */
6196 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
6200 if (symop.sym >= mp->nsyms && symop.sym != 0)
6202 if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
6203 TransformVec(vpout, mp->cell->rtr, vpin);
6204 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpout);
6205 vpout->x += symop.dx;
6206 vpout->y += symop.dy;
6207 vpout->z += symop.dz;
6208 TransformVec(vpout, mp->cell->tr, vpout);
6210 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpin);
6211 vpout->x += symop.dx;
6212 vpout->y += symop.dy;
6213 vpout->z += symop.dz;
6218 /* Add expanded atoms. Returns the number of newly created atoms.
6219 If indices is non-NULL, it should be an array of Int with at least
6220 IntGroupGetCount(group) entries, and on return it contains the
6221 indices of the expanded atoms (may be existing atoms if the expanded
6222 atoms are already present) */
6224 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group, Int *indices)
6226 int i, n, n0, n1, count, *table;
6228 IntGroupIterator iter;
6231 if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
6233 if (symop.sym >= mp->nsyms)
6236 /* Create atoms, with avoiding duplicates */
6237 n0 = n1 = mp->natoms;
6238 table = (int *)malloc(sizeof(int) * n0);
6241 for (i = 0; i < n0; i++)
6243 IntGroupIteratorInit(group, &iter);
6244 MoleculeGetTransformForSymop(mp, symop, &tr, 0);
6246 for (i = 0; i < count; i++) {
6251 n = IntGroupIteratorNext(&iter);
6252 ap = ATOM_AT_INDEX(mp->atoms, n);
6253 if (SYMOP_ALIVE(ap->symop)) {
6254 /* Calculate the cumulative symop */
6256 MoleculeGetTransformForSymop(mp, ap->symop, &t1, 0);
6257 TransformMul(t1, tr, t1);
6258 if (MoleculeGetSymopForTransform(mp, t1, &symop1, 0) != 0) {
6259 if (indices != NULL)
6261 continue; /* Skip this atom */
6268 /* Is this expansion already present? */
6269 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
6270 if (ap2->symbase == base && SYMOP_EQUAL(symop1, ap2->symop))
6274 /* If yes, then skip it */
6275 if (indices != NULL)
6279 /* Is the expanded position coincides with itself? */
6280 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
6281 VecSub(dr, ap->r, nr);
6282 if (VecLength2(dr) < 1e-6) {
6283 /* If yes, then this atom is included but no new atom is created */
6285 if (indices != NULL)
6288 /* Create a new atom */
6290 AtomDuplicate(&newAtom, ap);
6291 MoleculeCreateAnAtom(mp, &newAtom, -1);
6292 AtomClean(&newAtom);
6293 ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
6297 ap2->symop.alive = (symop.dx != 0 || symop.dy != 0 || symop.dz != 0 || symop.sym != 0);
6298 table[n] = n1; /* The index of the new atom */
6299 MoleculeSetAnisoBySymop(mp, n1); /* Recalculate anisotropic parameters according to symop */
6300 if (indices != NULL)
6305 IntGroupIteratorRelease(&iter);
6308 for (i = 0; i < n0; i++) {
6312 if (b[0] < 0 || b[0] == i)
6314 ap = ATOM_AT_INDEX(mp->atoms, i);
6315 cp = AtomConnectData(&ap->connect);
6316 for (n = 0; n < ap->connect.count; n++) {
6317 b[1] = table[cp[n]];
6320 if (b[1] > n0 && b[0] > b[1])
6322 MoleculeAddBonds(mp, 1, b);
6325 mp->needsMDRebuild = 1;
6326 __MoleculeUnlock(mp);
6328 return n1 - n0; /* The number of added atoms */
6331 /* Recalculate the coordinates of symmetry expanded atoms.
6332 Returns the number of affected atoms.
6333 If group is non-NULL, only the expanded atoms whose base atoms are in the
6334 given group are considered.
6335 If groupout and vpout are non-NULL, the indices of the affected atoms
6336 and the original positions are returned (for undo operation).
6337 The pointers returned in *groupout and *vpout must be released and
6338 free()'ed by the caller */
6340 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
6344 if (mp == NULL || mp->natoms == 0 || mp->nsyms == 0)
6346 if (groupout != NULL && vpout != NULL) {
6347 *groupout = IntGroupNew();
6348 if (*groupout == NULL)
6350 *vpout = (Vector *)malloc(sizeof(Vector) * mp->natoms);
6351 if (*vpout == NULL) {
6352 IntGroupRelease(*groupout);
6355 } else groupout = NULL; /* To simplify test for validity of groupout/vpout */
6359 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6361 if (!SYMOP_ALIVE(ap->symop))
6363 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
6365 bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6366 MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
6367 VecSub(dr, nr, ap->r);
6368 if (VecLength2(dr) < 1e-20)
6370 if (groupout != NULL) {
6371 (*vpout)[count] = ap->r;
6372 IntGroupAdd(*groupout, i, 1);
6377 mp->needsMDCopyCoordinates = 1;
6378 __MoleculeUnlock(mp);
6379 if (groupout != NULL) {
6383 IntGroupRelease(*groupout);
6386 *vpout = (Vector *)realloc(*vpout, sizeof(Vector) * count);
6392 #pragma mark ====== Show/hide atoms ======
6395 sMoleculeNotifyChangeAppearance(Molecule *mp)
6397 /* TODO: Finer control of notification types may be necessary */
6398 MoleculeCallback_notifyModification(mp, 0);
6403 sMoleculeUnselectHiddenAtoms(Molecule *mp)
6406 if (mp == NULL || mp->selection == NULL)
6408 for (i = 0; i < mp->natoms; i++) {
6409 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6410 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
6411 IntGroupRemove(mp->selection, i, 1);
6413 sMoleculeNotifyChangeAppearance(mp);
6417 MoleculeShowAllAtoms(Molecule *mp)
6422 for (i = 0; i < mp->natoms; i++) {
6423 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6424 ap->exflags &= ~kAtomHiddenFlag;
6426 sMoleculeNotifyChangeAppearance(mp);
6431 MoleculeShowReverse(Molecule *mp)
6436 for (i = 0; i < mp->natoms; i++) {
6437 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6438 ap->exflags ^= kAtomHiddenFlag;
6440 sMoleculeUnselectHiddenAtoms(mp);
6441 sMoleculeNotifyChangeAppearance(mp);
6446 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
6449 if (mp == NULL || ig == NULL)
6451 for (i = 0; i < mp->natoms; i++) {
6452 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6453 if (ap->exflags & kAtomHiddenFlag)
6454 continue; /* Already hidden */
6455 if (IntGroupLookupPoint(ig, i) >= 0)
6456 ap->exflags |= kAtomHiddenFlag;
6458 sMoleculeUnselectHiddenAtoms(mp);
6459 sMoleculeNotifyChangeAppearance(mp);
6463 #pragma mark ====== Reversible Editing ======
6467 sMoleculeNotifyModification(Molecule *mp)
6469 ** TODO: Finer control of notification types may be necessary **
6470 MoleculeCallback_notifyModification(mp, 0);
6474 /* Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup */
6476 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
6479 if (where == NULL) {
6480 /* Append the new objects at the end */
6481 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
6484 n1 = IntGroupGetCount(where); /* Position to get new object */
6485 n2 = nobjs; /* Position to get old object */
6486 n3 = n1 + n2; /* Position to place new/old object */
6487 for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
6488 int start = IntGroupGetStartPoint(where, i);
6489 int end = IntGroupGetEndPoint(where, i);
6491 /* old[end-(n3-n2)..n2-1] is moved to old[end..n3-1] */
6492 memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
6493 n2 = end - (n3 - n2);
6496 /* new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1] */
6497 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
6504 /* Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
6506 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6508 int n1, n2, n3, start, end, i;
6509 if (objs == NULL || where == NULL)
6510 return 1; /* Bad argument */
6511 n1 = 0; /* Position to move remaining elements to */
6512 n2 = 0; /* Position to move remaining elements from */
6513 n3 = 0; /* Position to move removed elements to */
6514 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6515 end = IntGroupGetEndPoint(where, i);
6517 /* Move (start - n2) elements from objs[n2] to objs[n1] */
6519 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
6523 /* Move (end - start) elements from objs[n2] to clip[n3] */
6525 memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
6526 n3 += (end - start);
6527 n2 += (end - start);
6529 /* Move (nobjs - n2) elements from objs[n2] to objs[n1] */
6531 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
6535 /* Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
6537 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6539 int n1, start, end, i;
6540 if (objs == NULL || where == NULL)
6541 return 1; /* Bad argument */
6542 n1 = 0; /* Position to move removed elements to */
6543 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6544 end = IntGroupGetEndPoint(where, i);
6545 /* Copy (end - start) elements from objs[start] to clip[n1] */
6547 memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
6548 n1 += (end - start);
6553 /* Create a new atom with no bonding information. ap must _not_ be inside the given molecule
6554 (Use AtomDuplicate() first) */
6556 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
6560 if (mp == NULL || ap == NULL || mp->noModifyTopology)
6563 if (pos < 0 || pos >= mp->natoms)
6565 ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
6567 goto error; /* Out of memory */
6568 ap1 = ATOM_AT_INDEX(mp->atoms, pos);
6569 if (pos < mp->natoms - 1) {
6570 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6572 if (AtomDuplicate(ap1, ap) == NULL) {
6573 /* Cannot duplicate: restore the original state */
6574 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6578 ap1->connect.count = 0;
6579 if (ap1->resSeq >= mp->nresidues)
6580 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
6581 if (ap1->resName[0] == 0)
6582 strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
6583 if (ap1->segName[0] == 0)
6584 strncpy(ap1->segName, "MAIN", 4);
6585 if (pos < mp->natoms - 1) {
6586 /* Renumber the connect table, bonds, angles, etc. */
6587 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
6590 for (j = 0; j < api->connect.count; j++) {
6591 cp = AtomConnectData(&api->connect);
6596 for (i = 0; i < mp->nbonds * 2; i++) {
6597 if (mp->bonds[i] >= pos)
6600 for (i = 0; i < mp->nangles * 3; i++) {
6601 if (mp->angles[i] >= pos)
6604 for (i = 0; i < mp->ndihedrals * 4; i++) {
6605 if (mp->dihedrals[i] >= pos)
6608 for (i = 0; i < mp->nimpropers * 4; i++) {
6609 if (mp->impropers[i] >= pos)
6613 mp->nframes = -1; /* Should be recalculated later */
6614 MoleculeIncrementModifyCount(mp);
6615 mp->needsMDRebuild = 1;
6616 __MoleculeUnlock(mp);
6619 __MoleculeUnlock(mp);
6623 /* Merge two molecules. We use this procedure for all add-atom operations. */
6624 /* resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
6626 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, int resSeqOffset)
6629 Int i, j, n1, n2, n3, n4, *cp;
6630 Int *new2old, *old2new;
6632 if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
6633 return 0; /* Do nothing */
6635 if (dst->noModifyTopology)
6636 return 1; /* Prohibited operation */
6638 if (where != NULL && IntGroupGetCount(where) != src->natoms)
6639 return 1; /* Bad parameter */
6641 __MoleculeLock(dst);
6644 if (resSeqOffset < 0)
6647 /* Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
6648 and ndst..ndst+nsrc-1 are for atoms in src. */
6649 new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
6650 if (new2old == NULL)
6652 old2new = new2old + ndst + nsrc;
6653 n1 = 0; /* dst index */
6654 n2 = 0; /* src index */
6655 n3 = 0; /* "merged" index */
6657 while (n1 < ndst || n2 < nsrc) {
6658 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
6661 /* n4 elements from dst[n1] will go to merged[n3] */
6662 for (j = 0; j < n4; j++) {
6663 old2new[n1 + j] = n3 + j;
6664 new2old[n3 + j] = n1 + j;
6668 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
6670 /* n4 elements from src[n2] will go to merged[n3] */
6671 for (j = 0; j < n4; j++) {
6672 old2new[ndst + n2 + j] = n3 + j;
6673 new2old[n3 + j] = ndst + n2 + j;
6680 /* Expand the destination array */
6681 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
6684 /* Move the atoms */
6685 if (where == NULL) {
6686 /* Duplicate atoms to the end of the destination array */
6687 for (i = 0; i < nsrc; i++) {
6688 if (AtomDuplicate(ATOM_AT_INDEX(dst->atoms, ndst + i), ATOM_AT_INDEX(src->atoms, i)) == NULL)
6691 // memmove(ATOM_AT_INDEX(dst->atoms, ndst), src->atoms, gSizeOfAtomRecord * nsrc);
6693 /* Duplicate to a temporary storage and then insert */
6694 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
6695 if (tempatoms == NULL)
6697 for (i = 0; i < nsrc; i++) {
6698 if (AtomDuplicate(ATOM_AT_INDEX(tempatoms, i), ATOM_AT_INDEX(src->atoms, i)) == NULL)
6701 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
6705 dst->natoms = ndst + nsrc;
6707 /* Renumber the atom indices in connect[] and symbase, and modify the residue numbers */
6708 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
6709 if (new2old[i] < ndst) {
6710 /* This atom is from dst */
6713 /* This atom is from src */
6714 n1 = ndst; /* Offset to the internal number */
6715 if (ap->resSeq != 0)
6716 ap->resSeq += resSeqOffset; /* Modify residue number */
6718 cp = AtomConnectData(&ap->connect);
6719 for (j = 0; j < ap->connect.count; j++)
6720 cp[j] = old2new[cp[j] + n1];
6721 if (SYMOP_ALIVE(ap->symop))
6722 ap->symbase = old2new[ap->symbase + n1];
6725 /* Move the bonds, angles, dihedrals, impropers */
6726 for (i = 0; i < 4; i++) {
6727 Int *nitems, *nitems_src;
6728 Int **items, **items_src;
6729 Int nsize; /* Number of Ints in one element */
6732 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
6734 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
6736 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
6738 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
6740 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
6741 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
6742 /* Keep the old number of entries in dst, because it is updated by AssignArray() */
6744 /* Also keep the old number of entries in src, in case src and dst point the same molecule */
6746 /* Expand the array */
6747 if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
6749 /* Copy the items */
6750 memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
6752 for (j = 0; j < n1 * nsize; j++)
6753 (*items)[j] = old2new[(*items)[j]];
6754 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
6755 (*items)[j] = old2new[(*items)[j] + ndst];
6758 /* Merge parameters */
6759 if (src->par != NULL) {
6760 UnionPar *up1, *up2;
6763 if (dst->par == NULL)
6764 dst->par = ParameterNew();
6766 /* Renumber existing parameters */
6767 for (type = kFirstParType; type <= kLastParType; type++) {
6768 n1 = ParameterGetCountForType(dst->par, type);
6769 for (i = 0; i < n1; i++) {
6770 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
6771 ParameterRenumberAtoms(type, up1, ndst, old2new);
6776 for (type = kFirstParType; type <= kLastParType; type++) {
6777 n1 = ParameterGetCountForType(src->par, type);
6778 n2 = ParameterGetCountForType(dst->par, type);
6781 /* Determine which parameter should be copied from src to dst */
6782 for (i = 0; i < n1; i++) {
6784 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
6785 n3 = ParameterGetAtomTypes(type, up1, types);
6786 for (j = 0; j < n3; j++) {
6787 /* If it includes explicit atom index, then it should be copied */
6788 if (types[j] < kAtomTypeMinimum) {
6789 IntGroupAdd(ig, i, 1);
6794 for (j = 0; j < n2; j++) {
6795 up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
6796 if (ParameterCompare(up1, up2, type))
6800 /* This is an unknown parameter; should be copied */
6801 IntGroupAdd(ig, i, 1);
6804 n1 = IntGroupGetCount(ig);
6807 up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
6810 /* Copy parameters and renumber indices if necessary */
6811 for (i = 0; i < n1; i++) {
6812 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
6816 ParameterRenumberAtoms(type, up1 + i, nsrc, old2new + ndst);
6818 /* Merge parameters */
6820 IntGroupAdd(ig, n2, n1);
6821 if (ParameterInsert(dst->par, type, up1, ig) < n1)
6826 IntGroupRelease(ig);
6829 /* Copy the residues if necessary */
6830 /* src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
6831 However, 1+resSeqOffset should not overwrite the existing residue in dst;
6832 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1]. */
6833 n1 = dst->nresidues;
6834 if (1 + resSeqOffset < n1) {
6836 } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
6837 if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
6838 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
6840 memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
6843 MoleculeCleanUpResidueTable(dst);
6846 dst->nframes = -1; /* Should be recalculated later */
6848 MoleculeIncrementModifyCount(dst);
6849 dst->needsMDRebuild = 1;
6850 __MoleculeUnlock(dst);
6854 __MoleculeUnlock(dst);
6855 Panic("Low memory while adding atoms");
6856 return 1; /* Not reached */
6860 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag)
6862 Int nsrc, ndst, nsrcnew;
6863 Int i, j, n1, n2, n3, n4, *cp;
6864 Int *new2old, *old2new;
6865 IntGroup *move_g, *del_g, *remain_g, *dst_par_g, *remove_par_g;
6870 if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
6877 if (src->noModifyTopology && moveFlag)
6878 return 1; /* Prohibit editing */
6880 if ((ndst = IntGroupGetCount(where)) > src->natoms)
6881 return 1; /* Bad parameter */
6883 __MoleculeLock(src);
6886 nsrcnew = nsrc - ndst;
6887 if (resSeqOffset < 0)
6890 /* Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
6891 and nsrcnew..nsrc-1 are for atoms moved into dst. */
6892 new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
6893 if (new2old == NULL)
6895 old2new = new2old + nsrc;
6896 n1 = 0; /* src index */
6897 n2 = 0; /* dst index */
6898 n3 = 0; /* src index after "unmerge" */
6900 while (n1 < nsrc || n2 < ndst) {
6901 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
6904 /* n4 elements from src[n1] will go to unmerged[n3] */
6905 for (j = 0; j < n4; j++) {
6906 old2new[n1 + j] = n3 + j;
6907 new2old[n3 + j] = n1 + j;
6911 if ((n4 = IntGroupGetInterval(where, i)) < 0)
6913 /* n4 elements from src[n1] will go to dst[n2] */
6914 for (j = 0; j < n4; j++) {
6915 old2new[n1 + j] = nsrcnew + n2 + j;
6916 new2old[nsrcnew + n2 + j] = n1 + j;
6923 /* Atoms to remain in the source group */
6925 remain_g = IntGroupNewWithPoints(0, nsrc, -1);
6926 IntGroupRemoveIntGroup(remain_g, where);
6927 } else remain_g = NULL;
6929 /* Find parameters to be moved to the dst (dst_par_g), and to be removed from the src (remove_par_g) */
6930 if (src->par != NULL) {
6931 dst_par_g = IntGroupNew();
6933 remove_par_g = IntGroupNew();
6934 else remove_par_g = NULL;
6935 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
6936 n2 = ParameterGetCountForType(src->par, n1);
6939 for (i = 0; i < n2; i++) {
6940 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
6941 if (ParameterIsRelevantToAtomGroup(n1, up, src->atoms, where)) {
6942 /* This parameter is to be copied to dst */
6943 IntGroupAdd(dst_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
6945 if (moveFlag && !ParameterIsRelevantToAtomGroup(n1, up, src->atoms, remain_g)) {
6946 /* This parameter is to be removed */
6947 IntGroupAdd(remove_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
6951 } else dst_par_g = remove_par_g = NULL;
6953 /* Make a new molecule */
6955 dst = MoleculeNew();
6958 /* Expand the destination array */
6959 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
6961 dst_ap = dst->atoms;
6964 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
6969 /* Move the atoms */
6971 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
6973 src->natoms = nsrcnew;
6975 /* The atom record must be deallocated correctly */
6976 for (i = 0; i < ndst; i++)
6977 AtomClean(ATOM_AT_INDEX(dst_ap, i));
6981 for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
6982 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
6987 /* The dummy destination array is no longer needed */
6992 /* Renumber the atom indices in connect[] */
6994 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
6995 cp = AtomConnectData(&ap->connect);
6996 for (j = n1 = 0; j < ap->connect.count; j++) {
6997 n2 = old2new[cp[j]];
7001 AtomConnectResize(&ap->connect, n1);
7005 /* Renumber the atom indices in connect[] and the residue indices */
7007 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7008 if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
7009 ap->resSeq -= resSeqOffset;
7010 else ap->resSeq = 0;
7011 cp = AtomConnectData(&ap->connect);
7012 for (j = n1 = 0; j < ap->connect.count; j++) {
7013 n2 = old2new[cp[j]] - nsrcnew;
7017 AtomConnectResize(&ap->connect, n1);
7021 /* Separate the bonds, angles, dihedrals, impropers */
7022 /* TODO: Improper torsions should also be copied! */
7023 move_g = IntGroupNew();
7024 del_g = IntGroupNew();
7025 if (move_g == NULL || del_g == NULL)
7027 for (i = 0; i < 4; i++) {
7028 Int *nitems, *nitems_dst;
7029 Int **items, **items_dst;
7030 Int nsize; /* Number of Ints in one element */
7031 unsigned char *counts;
7034 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
7036 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
7038 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
7040 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
7042 nitems = NULL; items = NULL; nsize = 0; break; /* Not reached */
7045 nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
7046 items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
7051 counts = (unsigned char *)calloc(1, *nitems);
7052 /* Find the entries that should be moved to dst */
7054 for (j = 0; j < *nitems * nsize; j++) {
7055 n1 = old2new[(*items)[j]];
7057 counts[j / nsize]++; /* Count the atom belonging to dst */
7058 /* if (n1 >= nsrcnew) {
7060 if (j % nsize == 0) {
7061 if (IntGroupAdd(sep, j / nsize, 1) != 0)
7066 (*items)[j] = n1; */
7068 for (j = n2 = n3 = 0; j < *nitems; j++) {
7069 if (counts[j] > 0) {
7070 /* Remove from src */
7072 if (IntGroupAdd(del_g, j, 1) != 0)
7074 if (counts[j] == nsize) {
7077 if (IntGroupAdd(move_g, j, 1) != 0)
7083 /* Expand the destination array */
7084 if (items_dst != NULL && n3 > 0) {
7085 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
7087 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
7090 /* Remove from src */
7092 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
7097 /* Renumber the entries */
7099 for (j = 0; j < *nitems * nsize; j++) {
7100 (*items)[j] = old2new[(*items)[j]];
7103 if (items_dst != NULL) {
7104 for (j = 0; j < *nitems_dst * nsize; j++) {
7105 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
7109 IntGroupClear(move_g);
7110 IntGroupClear(del_g);
7112 IntGroupRelease(move_g);
7113 IntGroupRelease(del_g);
7115 /* Copy the residues */
7117 /* src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset) */
7118 n1 = src->nresidues - resSeqOffset; /* This will be dst->nresidues (if >0) */
7119 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
7122 memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
7126 /* Copy the parameters to dst */
7127 if (dst != NULL && dst_par_g != NULL && (n2 = IntGroupGetCount(dst_par_g)) > 0) {
7128 IntGroup *dst_new_g = IntGroupNew();
7129 Int dst_par_count[kLastParType - kFirstParType + 1];
7130 if (dst_new_g == NULL)
7132 for (i = 0; i <= kLastParType - kFirstParType; i++)
7133 dst_par_count[i] = 0;
7134 up = (UnionPar *)calloc(sizeof(UnionPar), n2);
7137 if (ParameterCopy(src->par, kFirstParType, up, dst_par_g) < n2)
7139 /* Renumber the explicit atom indices */
7140 for (i = 0; i < nsrc; i++)
7141 old2new[i] -= nsrcnew; /* new indices for atoms in dst; otherwise negative numbers */
7142 for (i = 0; i < n2; i++) {
7143 /* Renumber the indices, and count the number of parameters for each type */
7144 n1 = kFirstParType + IntGroupGetNthPoint(dst_par_g, i) / kParameterIndexOffset;
7145 dst_par_count[n1 - kFirstParType]++;
7146 ParameterRenumberAtoms(n1, up + i, nsrc, old2new);
7148 for (i = 0; i < nsrc; i++)
7149 old2new[i] += nsrcnew;
7150 if (dst->par == NULL)
7151 dst->par = ParameterNew();
7152 for (i = 0; i <= kLastParType - kFirstParType; i++) {
7153 if (dst_par_count[i] > 0)
7154 IntGroupAdd(dst_new_g, i * kParameterIndexOffset, dst_par_count[i]);
7156 if (ParameterInsert(dst->par, kFirstParType, up, dst_new_g) < n2)
7159 IntGroupRelease(dst_new_g);
7161 IntGroupRelease(dst_par_g);
7163 /* Remove the unused parameter. Note: the parameters that are in remove_par_g and not in
7164 dst_par_g will disappear. To support undo, these parameters should be taken care separately. */
7165 if (remove_par_g != NULL && (n2 = IntGroupGetCount(remove_par_g)) > 0) {
7166 ParameterDelete(src->par, kFirstParType, NULL, remove_par_g);
7168 IntGroupRelease(remove_par_g);
7170 /* Renumber the parameter records remaining in the src */
7172 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7173 n2 = ParameterGetCountForType(src->par, n1);
7174 for (i = 0; i < n2; i++) {
7175 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7176 ParameterRenumberAtoms(n1, up, nsrc, old2new);
7182 IntGroupRelease(remain_g);
7183 MoleculeCleanUpResidueTable(src);
7185 MoleculeCleanUpResidueTable(dst);
7188 src->nframes = -1; /* Should be recalculated later */
7190 dst->nframes = -1; /* Should be recalculated later */
7196 MoleculeIncrementModifyCount(src);
7197 src->needsMDRebuild = 1;
7198 __MoleculeUnlock(src);
7203 __MoleculeUnlock(src);
7204 /* Panic("Low memory while removing atoms"); */
7208 /* Separate molecule into two parts. The atoms specified by 'where' are moved
7209 from src to a new molecule, which is returned as *dstp. Dstp can be NULL,
7210 in which case the moved atoms are discarded. */
7212 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset)
7214 return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1);
7217 /* Extract atoms from a given molecule into two parts. The atoms specified by
7218 'where' are copied from src to a new molecule, which is returned as *dstp.
7219 If dummyFlag is non-zero, then the atoms that are not included in the group
7220 but are connected to any atoms in the group are converted to "dummy" atoms
7221 (i.e. with element "Du" and names beginning with an underscore) and included
7222 in the new molecule object. */
7224 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
7228 /* Extract the fragment */
7229 retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0);
7235 /* Search bonds crossing the molecule border */
7236 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
7238 IntGroupIterator iter;
7241 IntGroupIteratorInit(ig, &iter);
7242 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7243 /* The atoms at the border */
7246 n1 = src->bonds[i*2];
7247 n2 = src->bonds[i*2+1];
7248 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
7252 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
7253 continue; /* Actually this is an internal error */
7255 /* n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule */
7256 /* Create a new dummy atom with the same segment/residue info with n1
7257 and the same position as n2 */
7258 ap = ATOM_AT_INDEX(src->atoms, n1);
7259 memset(&a, 0, gSizeOfAtomRecord);
7260 a.segSeq = ap->segSeq;
7261 memmove(a.segName, ap->segName, 4);
7262 a.resSeq = ap->resSeq;
7263 memmove(a.resName, ap->resName, 4);
7264 ElementToString(0, a.element); /* "Du" */
7265 snprintf(a.aname, 4, "_%d", idx++);
7266 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
7267 /* Add the dummy atom to the new molecule; nn[1] is the index
7268 of the new dummy atom in the new molecule */
7269 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
7270 /* Connect nn1 and nn2 */
7271 nn[2] = kInvalidIndex;
7272 MoleculeAddBonds(*dstp, 1, nn);
7274 IntGroupIteratorRelease(&iter);
7275 IntGroupRelease(ig);
7283 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds)
7285 int i, j, n1, n2, n;
7287 Int *bonds_tmp, *cp;
7289 if (mp == NULL || bonds == NULL || nbonds <= 0)
7291 if (mp->noModifyTopology)
7292 return -4; /* Prohibited operation */
7294 /* Check the bonds */
7295 bonds_tmp = (Int *)malloc(sizeof(Int) * nbonds * 2);
7296 if (bonds_tmp == NULL)
7297 return -4; /* Out of memory */
7299 for (i = 0; i < nbonds; i++) {
7301 n2 = bonds[i * 2 + 1];
7302 if (n1 < 0 || n1 >= mp->natoms || n2 < 0 || n2 >= mp->natoms)
7303 return -1; /* Bad bond specification */
7306 ap = ATOM_AT_INDEX(mp->atoms, n1);
7307 /* Check duplicates */
7308 cp = AtomConnectData(&ap->connect);
7309 for (j = 0; j < ap->connect.count; j++) {
7313 if (j == ap->connect.count) {
7314 bonds_tmp[n * 2] = n1;
7315 bonds_tmp[n * 2 + 1] = n2;
7320 /* No bonds to add */
7327 /* Add connects[] */
7328 for (i = 0; i < n; i++) {
7329 n1 = bonds_tmp[i * 2];
7330 n2 = bonds_tmp[i * 2 + 1];
7331 ap = ATOM_AT_INDEX(mp->atoms, n1);
7332 AtomConnectInsertEntry(&ap->connect, ap->connect.count, n2);
7333 ap = ATOM_AT_INDEX(mp->atoms, n2);
7334 AtomConnectInsertEntry(&ap->connect, ap->connect.count, n1);
7337 /* Expand the array and insert */
7339 /* if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, mp->nbonds + nb - 1, NULL) == NULL
7340 || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nb, sizeof(Int) * 2, where) != 0) */
7341 if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, mp->nbonds + n - 1, NULL) == NULL)
7343 memmove(mp->bonds + n1 * 2, bonds_tmp, sizeof(Int) * 2 * n);
7345 /* Add angles, dihedrals, impropers */
7347 Int nangles, ndihedrals, nimpropers;
7348 Int *angles, *dihedrals, *impropers;
7350 Int *ip, *cp1, *cp2;
7354 angles = dihedrals = impropers = NULL;
7355 nangles = ndihedrals = nimpropers = 0;
7357 for (i = 0; i < n; i++) {
7358 n1 = bonds_tmp[i * 2];
7359 n2 = bonds_tmp[i * 2 + 1];
7360 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
7361 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
7362 cp1 = AtomConnectData(&ap1->connect);
7363 cp2 = AtomConnectData(&ap2->connect);
7364 /* Angles X-n1-n2 */
7365 for (j = 0; j < ap1->connect.count; j++) {
7372 for (k = 0; k < nangles; k++) {
7373 ip = angles + k * 3;
7374 if (ip[1] == n1 && ((ip[0] == n3 && ip[2] == n2) || (ip[0] == n2 && ip[2] == n3)))
7378 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7381 /* Dihedrals X-n1-n2-X */
7382 for (k = 0; k < ap2->connect.count; k++) {
7384 if (n4 == n1 || n4 == n3)
7387 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7390 /* Impropers X-n2-n1-X */
7393 for (k = 0; k < ap1->connect.count; k++) {
7394 n4 = ap1->connects[k];
7395 if (n4 == n2 || n4 <= n3)
7398 if (AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, temp) == NULL)
7402 /* Angles X-n2-n1 */
7403 for (j = 0; j < ap2->connect.count; j++) {
7410 for (k = 0; k < nangles; k++) {
7411 ip = angles + k * 3;
7412 if (ip[1] == n2 && ((ip[0] == n3 && ip[2] == n1) || (ip[0] == n1 && ip[2] == n3)))
7416 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7421 temp[0] = kInvalidIndex;
7422 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7424 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7426 if (AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, temp) == NULL)
7428 MoleculeAddAngles(mp, angles, NULL);
7429 MoleculeAddDihedrals(mp, dihedrals, NULL);
7430 MoleculeAddImpropers(mp, impropers, NULL);
7433 if (dihedrals != NULL)
7435 if (impropers != NULL)
7439 MoleculeIncrementModifyCount(mp);
7440 mp->needsMDRebuild = 1;
7441 __MoleculeUnlock(mp);
7447 __MoleculeUnlock(mp);
7448 Panic("Low memory while adding bonds");
7449 return -1; /* Not reached */
7453 MoleculeDeleteBonds(Molecule *mp, Int nbonds, const Int *bonds)
7455 Int i, j, n1, n2, *cp;
7458 if (mp == NULL || nbonds <= 0)
7460 if (mp->noModifyTopology)
7461 return -4; /* Prohibited operation */
7465 /* Update connects[] */
7466 for (i = 0; i < nbonds; i++) {
7468 n2 = bonds[i * 2 + 1];
7469 ap = ATOM_AT_INDEX(mp->atoms, n1);
7470 cp = AtomConnectData(&ap->connect);
7471 for (j = 0; j < ap->connect.count; j++) {
7473 AtomConnectDeleteEntry(&ap->connect, j);
7477 ap = ATOM_AT_INDEX(mp->atoms, n2);
7478 cp = AtomConnectData(&ap->connect);
7479 for (j = 0; j < ap->connect.count; j++) {
7481 AtomConnectDeleteEntry(&ap->connect, j);
7487 /* Remove bonds, angles, dihedrals, impropers */
7489 IntGroup *bg, *ag, *dg, *ig;
7496 if (bg == NULL || ag == NULL || dg == NULL || ig == NULL)
7498 for (i = 0; i < nbonds; i++) {
7500 n2 = bonds[i * 2 + 1];
7501 for (j = 0; j < mp->nbonds; j++) {
7502 ip = mp->bonds + j * 2;
7503 if ((ip[0] == n1 && ip[1] == n2)
7504 || (ip[1] == n1 && ip[0] == n2)) {
7505 if (IntGroupAdd(bg, j, 1) != 0)
7509 for (j = 0; j < mp->nangles; j++) {
7510 ip = mp->angles + j * 3;
7511 if ((ip[0] == n1 && ip[1] == n2)
7512 || (ip[1] == n1 && ip[0] == n2)
7513 || (ip[1] == n1 && ip[2] == n2)
7514 || (ip[2] == n1 && ip[1] == n2)) {
7515 if (IntGroupAdd(ag, j, 1) != 0)
7519 for (j = 0; j < mp->ndihedrals; j++) {
7520 ip = mp->dihedrals + j * 4;
7521 if ((ip[1] == n1 && ip[2] == n2)
7522 || (ip[2] == n1 && ip[1] == n2)) {
7523 if (IntGroupAdd(dg, j, 1) != 0)
7527 for (j = 0; j < mp->nimpropers; j++) {
7528 ip = mp->impropers + j * 4;
7529 if ((ip[0] == n1 && ip[2] == n2)
7530 || (ip[1] == n1 && ip[2] == n2)
7531 || (ip[3] == n1 && ip[2] == n2)
7532 || (ip[0] == n2 && ip[2] == n1)
7533 || (ip[1] == n2 && ip[2] == n1)
7534 || (ip[3] == n2 && ip[2] == n1)) {
7535 if (IntGroupAdd(ig, j, 1) != 0)
7540 if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, NULL, sizeof(Int) * 2, bg) != 0)
7542 mp->nbonds -= IntGroupGetCount(bg);
7544 if (IntGroupGetCount(ag) > 0)
7545 MoleculeDeleteAngles(mp, NULL, ag);
7546 if (IntGroupGetCount(dg) > 0)
7547 MoleculeDeleteDihedrals(mp, NULL, dg);
7548 if (IntGroupGetCount(ig) > 0)
7549 MoleculeDeleteImpropers(mp, NULL, ig);
7550 IntGroupRelease(bg);
7551 IntGroupRelease(ag);
7552 IntGroupRelease(dg);
7553 IntGroupRelease(ig);
7556 MoleculeIncrementModifyCount(mp);
7557 mp->needsMDRebuild = 1;
7558 __MoleculeUnlock(mp);
7563 __MoleculeUnlock(mp);
7564 Panic("Low memory while removing bonds");
7565 return -1; /* Not reached */
7569 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
7572 if (mp == NULL || angles == NULL)
7574 if (mp->noModifyTopology)
7575 return -4; /* Prohibited operation */
7579 nc = IntGroupGetCount(where);
7581 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
7587 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
7588 || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
7589 __MoleculeUnlock(mp);
7590 Panic("Low memory while adding angles");
7593 mp->needsMDRebuild = 1;
7594 __MoleculeUnlock(mp);
7599 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
7602 if (mp == NULL || where == NULL)
7604 if (mp->noModifyTopology)
7605 return -4; /* Prohibited operation */
7607 if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
7608 __MoleculeUnlock(mp);
7609 Panic("Low memory while adding angles");
7611 mp->nangles -= (nc = IntGroupGetCount(where));
7612 mp->needsMDRebuild = 1;
7613 __MoleculeUnlock(mp);
7618 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
7621 if (mp == NULL || dihedrals == NULL)
7623 if (mp->noModifyTopology)
7624 return -4; /* Prohibited operation */
7626 nc = IntGroupGetCount(where);
7628 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
7634 n1 = mp->ndihedrals;
7636 if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
7637 || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
7638 __MoleculeUnlock(mp);
7639 Panic("Low memory while adding dihedrals");
7641 mp->needsMDRebuild = 1;
7642 __MoleculeUnlock(mp);
7647 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
7650 if (mp == NULL || where == NULL)
7652 if (mp->noModifyTopology)
7653 return -4; /* Prohibited operation */
7655 if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
7656 __MoleculeUnlock(mp);
7657 Panic("Low memory while adding dihedrals");
7659 mp->ndihedrals -= (nc = IntGroupGetCount(where));
7660 mp->needsMDRebuild = 1;
7661 __MoleculeUnlock(mp);
7666 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
7669 if (mp == NULL || impropers == NULL)
7671 if (mp->noModifyTopology)
7672 return -4; /* Prohibited operation */
7674 nc = IntGroupGetCount(where);
7676 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
7682 n1 = mp->nimpropers;
7684 if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
7685 || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
7686 __MoleculeUnlock(mp);
7687 Panic("Low memory while adding impropers");
7689 mp->needsMDRebuild = 1;
7690 __MoleculeUnlock(mp);
7695 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
7698 if (mp == NULL || where == NULL)
7700 if (mp->noModifyTopology)
7701 return -4; /* Prohibited operation */
7703 if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
7704 __MoleculeUnlock(mp);
7705 Panic("Low memory while adding impropers");
7707 mp->nimpropers -= (nc = IntGroupGetCount(where));
7708 __MoleculeUnlock(mp);
7713 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
7716 if (mp == NULL || mp->bonds == NULL)
7718 for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
7719 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
7726 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
7729 if (mp == NULL || mp->angles == NULL)
7731 for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
7732 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
7733 (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
7740 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
7743 if (mp == NULL || mp->dihedrals == NULL)
7745 for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
7746 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
7747 (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
7754 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
7757 if (mp == NULL || mp->impropers == NULL)
7759 for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
7762 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
7763 (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
7764 (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
7770 /* Remove the bond at bondIndex and create two dummy atoms instead.
7771 The dummy atoms are placed at the end of atoms[], and the residue
7772 numbers are the same as the root atoms (i.e. the atoms to which
7773 the dummy atoms are connected). The indices are returned in
7774 dummyIndices[0,1]. */
7776 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
7778 Int roots[3], newBonds[5];
7783 if (mp == NULL || mp->noModifyTopology)
7785 if (bondIndex < 0 || bondIndex >= mp->nbonds)
7787 roots[0] = mp->bonds[bondIndex * 2];
7788 roots[1] = mp->bonds[bondIndex * 2 + 1];
7789 roots[2] = kInvalidIndex;
7790 rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
7791 rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
7792 VecSub(dr, rootp[0]->r, rootp[1]->r);
7793 for (i = 0; i < 2; i++) {
7796 memmove(nap, rootp[i], sizeof(na));
7797 nap->aname[0] = '*';
7798 strcpy(nap->element, "Du");
7800 nap->charge = nap->weight = 0.0;
7801 nap->atomicNumber = 0;
7802 nap->connect.count = 0;
7803 w = (i == 0 ? 0.4 : -0.4);
7804 VecScaleInc(nap->r, dr, w);
7811 /* Expand atoms array and append the dummy atoms at the end */
7813 natoms = mp->natoms;
7814 if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
7816 memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
7817 dummyIndices[0] = natoms;
7818 dummyIndices[1] = natoms + 1;
7820 /* Remove the old bond and create new bonds */
7821 /* ig = IntGroupNewWithPoints(bondIndex, 1, -1);
7824 MoleculeDeleteBonds(mp, NULL, ig);
7825 IntGroupRelease(ig); */
7826 MoleculeDeleteBonds(mp, 1, roots);
7827 newBonds[0] = roots[0];
7828 newBonds[1] = dummyIndices[0];
7829 newBonds[2] = roots[1];
7830 newBonds[3] = dummyIndices[1];
7831 newBonds[4] = kInvalidIndex;
7833 i = (MoleculeAddBonds(mp, 2, newBonds) < 0 ? -1 : 0);
7834 mp->needsMDRebuild = 1;
7835 __MoleculeUnlock(mp);
7839 __MoleculeUnlock(mp);
7840 Panic("Low memory during creating dummy atoms");
7844 /* Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
7845 a bond between the two root atoms. The value bondIndex is used as a
7846 hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
7847 the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
7848 is ignored and the new bond is stored at the end of bonds[]. */
7850 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
7857 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
7860 if (mol == NULL || mol->noModifyTopology)
7867 __MoleculeLock(mol);
7868 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
7869 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
7870 mol->needsMDRebuild = 1;
7871 __MoleculeUnlock(mol);
7878 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
7881 if (mol == NULL || mol->noModifyTopology)
7883 n1 = mol->ndihedrals;
7884 np1 = mol->dihedrals;
7885 mol->ndihedrals = 0;
7886 mol->dihedrals = NULL;
7887 if (ndihedrals > 0) {
7888 __MoleculeLock(mol);
7889 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
7890 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
7891 mol->needsMDRebuild = 1;
7892 __MoleculeUnlock(mol);
7894 *outDihedrals = np1;
7899 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
7902 if (mol == NULL || mol->noModifyTopology)
7904 n1 = mol->nimpropers;
7905 np1 = mol->impropers;
7906 mol->nimpropers = 0;
7907 mol->impropers = NULL;
7908 if (nimpropers > 0) {
7909 __MoleculeLock(mol);
7910 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
7911 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
7912 mol->needsMDRebuild = 1;
7913 __MoleculeUnlock(mol);
7915 *outImpropers = np1;
7921 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
7928 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
7929 return 0; /* molecule is empty */
7930 if (mol->noModifyTopology)
7934 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7935 Int *cp = AtomConnectData(&ap->connect);
7936 for (j = 0; j < ap->connect.count; j++) {
7938 for (k = j + 1; k < ap->connect.count; k++) {
7940 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
7941 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
7950 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
7954 if (outAngles != NULL)
7955 *outAngles = angles;
7960 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
7962 Int n1, n2, n3, n4, *ip, *cp2, *cp3;
7967 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
7968 return 0; /* molecule is empty */
7971 for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
7972 Int i1, i3, i4, *ip;
7973 cp2 = AtomConnectData(&ap2->connect);
7974 for (i3 = 0; i3 < ap2->connect.count; i3++) {
7978 ap3 = ATOM_AT_INDEX(mol->atoms, n3);
7979 cp3 = AtomConnectData(&ap3->connect);
7980 for (i1 = 0; i1 < ap2->connect.count; i1++) {
7984 for (i4 = 0; i4 < ap3->connect.count; i4++) {
7986 if (n2 == n4 || n1 == n4)
7988 if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
7989 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
7999 if (ndihedrals > 0) {
8000 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
8004 if (outDihedrals != NULL)
8005 *outDihedrals = dihedrals;
8010 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
8012 Int n1, n2, n3, n4, t1, t2, t3, t4, *ip, *cp;
8013 Parameter *par = mol->par;
8018 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8019 return 0; /* molecule is empty */
8020 if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
8021 return 0; /* No improper parameters are defined */
8025 for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
8026 Int i1, i2, i4, found, *ip;
8028 cp = AtomConnectData(&ap3->connect);
8029 for (i1 = 0; i1 < ap3->connect.count; i1++) {
8031 t1 = ATOM_AT_INDEX(ap, n1)->type;
8032 for (i2 = i1 + 1; i2 < ap3->connect.count; i2++) {
8034 t2 = ATOM_AT_INDEX(ap, n2)->type;
8035 for (i4 = i2 + 1; i4 < ap3->connect.count; i4++) {
8037 t4 = ATOM_AT_INDEX(ap, n4)->type;
8039 if (ParameterLookupImproperPar(par, t1, t2, t3, t4, n1, n2, n3, n4, 0) != NULL)
8041 else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, -1, -1, -1, -1, 0) != NULL)
8043 if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
8044 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8054 if (nimpropers > 0) {
8055 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8059 if (outImpropers != NULL)
8060 *outImpropers = impropers;
8064 #pragma mark ====== Residues ======
8067 MoleculeCleanUpResidueTable(Molecule *mp)
8071 if (mp == NULL || mp->natoms == 0)
8075 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8076 if (ap->resSeq >= maxres)
8077 maxres = ap->resSeq + 1;
8078 if (ap->resSeq < mp->nresidues) {
8079 if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
8080 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8082 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
8085 if (maxres < mp->nresidues)
8086 mp->nresidues = maxres;
8087 __MoleculeUnlock(mp);
8090 /* Change the number of residues. If nresidues is greater than the current value,
8091 then the array mp->residues is expanded with null names. If nresidues is smaller
8092 than the current value, mp->nresidues is set to the smallest possible value
8093 that is no smaller than nresidues and larger than any of the resSeq values. */
8095 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
8100 if (mp->nresidues == nresidues)
8102 else if (mp->nresidues < nresidues) {
8105 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
8106 while (n < nresidues)
8107 mp->residues[n++][0] = 0;
8108 __MoleculeUnlock(mp);
8114 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8115 if (ap->resSeq >= n)
8124 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
8126 IntGroupIterator iter;
8127 int withArray, resSeq, maxSeq;
8131 /* If LSB of resSeqs is 1, then a constant value is used for all specified atoms */
8132 if (((int)resSeqs & 1) == 0) {
8137 resSeq = ((int)resSeqs - 1) / 2;
8140 IntGroupIteratorInit(group, &iter);
8142 /* Change resSeqs */
8146 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8147 ap = ATOM_AT_INDEX(mp->atoms, i);
8149 resSeq = resSeqs[j++];
8150 if (resSeq > maxSeq)
8152 ap->resSeq = resSeq;
8154 __MoleculeUnlock(mp);
8156 /* Expand array if necessary */
8157 if (maxSeq >= mp->nresidues)
8158 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8160 /* Synchronize resName and residues[] */
8162 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8163 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8165 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8167 IntGroupIteratorRelease(&iter);
8168 __MoleculeUnlock(mp);
8170 MoleculeIncrementModifyCount(mp);
8176 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
8178 return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(resSeq * 2 + 1));
8181 /* Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
8182 specifies the mp->nresidues after modifying the residue numbers.
8183 If all atoms are modified, then the table of residue names is also shifted. Otherwise,
8184 the table of residue names is not touched. */
8186 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
8188 int i, maxSeq, nmodatoms;
8190 IntGroupIterator iter;
8191 IntGroupIteratorInit(group, &iter);
8194 nresidues = mp->nresidues;
8197 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8198 ap = ATOM_AT_INDEX(mp->atoms, i);
8199 ap->resSeq += offset;
8200 if (ap->resSeq < 0) {
8201 /* Bad argument; undo change and returns this index + 1 */
8203 ap->resSeq -= offset;
8204 while ((i = IntGroupIteratorLast(&iter)) >= 0) {
8205 ap = ATOM_AT_INDEX(mp->atoms, i);
8206 ap->resSeq -= offset;
8208 IntGroupIteratorRelease(&iter);
8209 return bad_index + 1;
8211 if (ap->resSeq > maxSeq)
8212 maxSeq = ap->resSeq;
8215 if (maxSeq >= nresidues)
8216 nresidues = maxSeq + 1;
8217 if (offset < 0 && nmodatoms == mp->natoms) {
8218 /* Shift the residue names downward */
8219 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
8221 __MoleculeUnlock(mp);
8222 MoleculeChangeNumberOfResidues(mp, nresidues);
8223 if (offset > 0 && nmodatoms == mp->natoms) {
8224 /* Shift the residue names upward */
8226 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
8227 __MoleculeUnlock(mp);
8229 IntGroupIteratorRelease(&iter);
8231 MoleculeIncrementModifyCount(mp);
8236 /* Change residue names for the specified residue numbers. Names is an array of
8237 chars containing argc*4 characters, and every 4 characters represent a
8238 residue name; characters '\x01'-'\x1f' are converted to '\0', which allow
8239 names to be handled as a C string. */
8241 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
8246 for (i = 0; i < argc; i++) {
8247 if (maxSeq < resSeqs[i])
8248 maxSeq = resSeqs[i];
8250 if (maxSeq >= mp->nresidues)
8251 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8253 for (i = 0; i < argc; i++) {
8254 char *p = mp->residues[resSeqs[i]];
8256 strncpy(p, names + i * 4, 4);
8257 for (j = 0; j < 4; j++) {
8258 if (p[j] >= 0 && p[j] < 0x20)
8262 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8263 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8265 __MoleculeUnlock(mp);
8267 MoleculeIncrementModifyCount(mp);
8272 /* Returns the maximum residue number actually used */
8274 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
8279 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8280 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8282 if (ap->resSeq > maxSeq)
8283 maxSeq = ap->resSeq;
8288 /* Returns the minimum residue number actually used */
8290 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
8294 minSeq = ATOMS_MAX_NUMBER;
8295 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8296 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8298 if (ap->resSeq < minSeq)
8299 minSeq = ap->resSeq;
8301 return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
8304 #pragma mark ====== Sort by Residues ======
8307 sAtomSortComparator(const void *a, const void *b)
8309 const Atom *ap, *bp;
8310 ap = *((const Atom **)a);
8311 bp = *((const Atom **)b);
8312 if (ap->resSeq == bp->resSeq) {
8313 /* Retain the original order (i.e. atom with larger pointer address is larger) */
8320 /* Compare the residue sequence. However, residue sequence 0 is always larger. */
8321 if (ap->resSeq == 0)
8323 else if (bp->resSeq == 0)
8325 else if (ap->resSeq < bp->resSeq)
8327 else if (ap->resSeq > bp->resSeq)
8334 sMoleculeReorder(Molecule *mp)
8336 int i, res, prevRes;
8340 if (mp == NULL || mp->natoms <= 1)
8343 /* Sort the atoms, bonds, etc. */
8344 apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
8345 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
8346 newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
8347 if (apArray == NULL || old2new == NULL || newAtoms == NULL)
8348 Panic("Low memory during reordering atoms");
8349 for (i = 0; i < mp->natoms; i++)
8350 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
8352 /* Sort the atoms. Note: apArray is an array of "Pointer to Atom" */
8353 qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
8355 /* Make a table of 'which atom becomes which' */
8356 for (i = 0; i < mp->natoms; i++) {
8357 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
8361 /* Renumber the bonds, etc. */
8362 for (i = 0; i < mp->nbonds * 2; i++) {
8363 mp->bonds[i] = old2new[mp->bonds[i]];
8365 for (i = 0; i < mp->nangles * 3; i++) {
8366 mp->angles[i] = old2new[mp->angles[i]];
8368 for (i = 0; i < mp->ndihedrals * 4; i++) {
8369 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
8371 for (i = 0; i < mp->nimpropers * 4; i++) {
8372 mp->impropers[i] = old2new[mp->impropers[i]];
8374 for (i = 0; i < mp->natoms; i++) {
8376 ip = AtomConnectData(&(apArray[i]->connect));
8377 for (j = 0; j < apArray[i]->connect.count; j++, ip++)
8381 /* Renumber the residues so that the residue numbers are contiguous */
8383 for (i = 0; i < mp->natoms; i++) {
8384 if (apArray[i]->resSeq == 0)
8386 if (apArray[i]->resSeq != prevRes) {
8388 prevRes = apArray[i]->resSeq;
8389 if (prevRes != res) {
8390 strncpy(mp->residues[res], mp->residues[prevRes], 4);
8393 apArray[i]->resSeq = res;
8395 mp->nresidues = res + 1;
8397 /* Sort the atoms and copy back to atoms[] */
8398 for (i = 0; i < mp->natoms; i++) {
8399 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
8401 memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
8403 /* Free the locally allocated storage */
8410 /* Renumber atoms */
8412 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
8414 Int *old2new, i, j, retval;
8418 if (mp->noModifyTopology)
8420 if (old2new_out != NULL)
8421 old2new = old2new_out;
8423 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
8424 saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
8425 if (old2new == NULL || saveAtoms == NULL)
8426 Panic("Low memory during reordering atoms");
8427 memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
8429 for (i = 0; i < mp->natoms; i++)
8431 for (i = 0; i < isize && i < mp->natoms; i++) {
8433 if (j < 0 || j >= mp->natoms) {
8434 retval = 1; /* Out of range */
8437 if (old2new[j] != -1) {
8438 retval = 2; /* Duplicate entry */
8443 if (i < mp->natoms) {
8444 for (j = 0; j < mp->natoms; j++) {
8445 if (old2new[j] != -1)
8450 if (i != mp->natoms) {
8451 retval = 3; /* Internal inconsistency */
8455 /* Renumber the bonds, etc. */
8456 for (i = 0; i < mp->nbonds * 2; i++) {
8457 mp->bonds[i] = old2new[mp->bonds[i]];
8459 for (i = 0; i < mp->nangles * 3; i++) {
8460 mp->angles[i] = old2new[mp->angles[i]];
8462 for (i = 0; i < mp->ndihedrals * 4; i++) {
8463 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
8465 for (i = 0; i < mp->nimpropers * 4; i++) {
8466 mp->impropers[i] = old2new[mp->impropers[i]];
8468 for (i = 0; i < mp->natoms; i++) {
8469 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
8470 Int *ip = AtomConnectData(&ap->connect);
8471 for (j = 0; j < ap->connect.count; j++, ip++)
8474 if (mp->par != NULL) {
8475 /* Renumber the parameters */
8477 for (j = kFirstParType; j <= kLastParType; j++) {
8478 n = ParameterGetCountForType(mp->par, j);
8479 for (i = 0; i < n; i++) {
8480 UnionPar *up = ParameterGetUnionParFromTypeAndIndex(mp->par, j, i);
8482 ParameterRenumberAtoms(j, up, mp->natoms, old2new);
8487 /* Renumber the atoms */
8488 for (i = 0; i < mp->natoms; i++)
8489 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
8492 MoleculeIncrementModifyCount(mp);
8493 mp->needsMDRebuild = 1;
8496 __MoleculeUnlock(mp);
8498 if (old2new_out == NULL)
8503 #pragma mark ====== Coordinate Transform ======
8506 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
8511 Transform rtr, symtr;
8512 if (mp == NULL || tr == NULL)
8514 TransformInvert(rtr, tr);
8516 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8517 if (group == NULL || IntGroupLookup(group, i, NULL) != 0) {
8518 TransformVec(&ap->r, tr, &ap->r);
8519 if (!SYMOP_ALIVE(ap->symop))
8521 /* Transform symop */
8522 if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
8524 TransformMul(symtr, tr, symtr);
8525 if (group == NULL || IntGroupLookup(group, ap->symbase, NULL) != 0)
8526 TransformMul(symtr, symtr, rtr);
8528 if (!SYMOP_ALIVE(ap->symop))
8530 /* Transform symop if the base atom is transformed */
8531 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
8533 if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
8535 TransformMul(symtr, symtr, rtr);
8537 if (MoleculeGetSymopForTransform(mp, symtr, &new_symop, 1) != 0)
8539 ap->symop = new_symop;
8541 mp->needsMDCopyCoordinates = 1;
8542 __MoleculeUnlock(mp);
8543 sMoleculeNotifyChangeAppearance(mp);
8548 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
8552 if (mp == NULL || tr == NULL)
8555 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8556 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8558 TransformVec(&ap->r, tr, &ap->r);
8560 mp->needsMDCopyCoordinates = 1;
8561 __MoleculeUnlock(mp);
8562 sMoleculeNotifyChangeAppearance(mp);
8567 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
8570 if (mp == NULL || vp == NULL)
8572 memset(tr, 0, sizeof(tr));
8573 tr[0] = tr[4] = tr[8] = 1.0;
8577 MoleculeTransform(mp, tr, group);
8581 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
8584 TransformForRotation(tr, axis, angle, center);
8585 MoleculeTransform(mp, tr, group);
8589 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
8594 if (mp == NULL || center == NULL)
8596 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
8597 return 2; /* Empty molecule */
8599 center->x = center->y = center->z = 0.0;
8600 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8601 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8603 VecScaleInc(*center, ap->r, ap->weight);
8607 return 3; /* Atomic weights are not defined? */
8609 VecScaleSelf(*center, w);
8614 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
8621 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
8622 return 2; /* Empty molecule */
8623 vmin.x = vmin.y = vmin.z = 1e50;
8624 vmax.x = vmax.y = vmax.z = -1e50;
8625 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8626 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8628 if (vmin.x > ap->r.x)
8630 if (vmin.y > ap->r.y)
8632 if (vmin.z > ap->r.z)
8634 if (vmax.x < ap->r.x)
8636 if (vmax.y < ap->r.y)
8638 if (vmax.z < ap->r.z)
8648 #pragma mark ====== Measurements ======
8651 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
8654 /* if (mp->is_xtal_coord) {
8655 TransformVec(&r1, mp->cell->tr, vp1);
8656 TransformVec(&r2, mp->cell->tr, vp2);
8662 return VecLength(r1);
8666 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
8670 /* if (mp->is_xtal_coord) {
8671 TransformVec(&r1, mp->cell->tr, vp1);
8672 TransformVec(&r2, mp->cell->tr, vp2);
8673 TransformVec(&r3, mp->cell->tr, vp3);
8681 w = VecLength(r1) * VecLength(r3);
8684 return acos(VecDot(r1, r3) / w) * kRad2Deg;
8688 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
8690 Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
8692 /* if (mp->is_xtal_coord) {
8693 TransformVec(&r1, mp->cell->tr, vp1);
8694 TransformVec(&r2, mp->cell->tr, vp2);
8695 TransformVec(&r3, mp->cell->tr, vp3);
8696 TransformVec(&r4, mp->cell->tr, vp4);
8703 VecSub(r21, r1, r2);
8704 VecSub(r32, r2, r3);
8705 VecSub(r43, r3, r4);
8706 VecCross(v1, r21, r32);
8707 VecCross(v2, r32, r43);
8708 VecCross(v3, r32, v1);
8712 if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
8718 VecScaleSelf(v1, w1);
8719 VecScaleSelf(v2, w2);
8720 VecScaleSelf(v3, w3);
8721 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
8725 #pragma mark ====== XtalCell Parameters ======
8728 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
8730 if (mp->cell != NULL) {
8731 TransformVec(dst, mp->cell->tr, src);
8736 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
8738 if (mp->cell != NULL) {
8739 TransformVec(dst, mp->cell->rtr, src);
8744 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
8746 static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
8748 Vector *vp1, *vp2, *vp3;
8753 for (n1 = 0; n1 < 3; n1++) {
8754 if (cp->flags[n1] != 0)
8758 /* All directions are non-periodic */
8759 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
8760 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
8764 vp1 = &(cp->axes[n1]);
8765 vp2 = &(cp->axes[n2]);
8766 vp3 = &(cp->axes[n3]);
8767 cp->tr[n1*3] = vp1->x;
8768 cp->tr[n1*3+1] = vp1->y;
8769 cp->tr[n1*3+2] = vp1->z;
8770 cp->tr[9] = cp->origin.x;
8771 cp->tr[10] = cp->origin.y;
8772 cp->tr[11] = cp->origin.z;
8773 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
8774 /* 1-dimensional or 2-dimensional system */
8775 /* Create "dummy" axes, so that transforms between internal and cartesian coordinates are
8776 possible with a single matrix */
8777 if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
8779 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
8780 VecCross(v1, *vp1, xvec);
8781 VecCross(v2, *vp1, yvec);
8782 if (VecLength2(v1) < VecLength2(v2))
8784 VecCross(v2, *vp1, v1);
8785 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
8786 return -1; /* Non-regular transform */
8787 } else if (cp->flags[n2] == 0) {
8789 VecCross(v1, v2, *vp1);
8790 if (NormalizeVec(&v1, &v1))
8791 return -1; /* Non-regular transform */
8794 VecCross(v2, *vp1, v1);
8795 if (NormalizeVec(&v2, &v2))
8796 return -1; /* Non-regular transform */
8798 cp->tr[n2*3] = v1.x;
8799 cp->tr[n2*3+1] = v1.y;
8800 cp->tr[n2*3+2] = v1.z;
8801 cp->tr[n3*3] = v2.x;
8802 cp->tr[n3*3+1] = v2.y;
8803 cp->tr[n3*3+2] = v2.z;
8805 VecCross(v1, *vp1, *vp2);
8806 if (fabs(VecDot(v1, *vp3)) < 1e-7)
8807 return -1; /* Non-regular transform */
8808 cp->tr[n2*3] = vp2->x;
8809 cp->tr[n2*3+1] = vp2->y;
8810 cp->tr[n2*3+2] = vp2->z;
8811 cp->tr[n3*3] = vp3->x;
8812 cp->tr[n3*3+1] = vp3->y;
8813 cp->tr[n3*3+2] = vp3->z;
8816 if (TransformInvert(cp->rtr, cp->tr))
8817 return -1; /* Non-regular transform */
8819 /* Calculate the reciprocal cell parameters */
8820 cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[1] * cp->rtr[1] + cp->rtr[2] * cp->rtr[2]);
8821 cp->rcell[1] = sqrt(cp->rtr[3] * cp->rtr[3] + cp->rtr[4] * cp->rtr[4] + cp->rtr[5] * cp->rtr[5]);
8822 cp->rcell[2] = sqrt(cp->rtr[6] * cp->rtr[6] + cp->rtr[7] * cp->rtr[7] + cp->rtr[8] * cp->rtr[8]);
8823 cp->rcell[3] = acos((cp->rtr[3] * cp->rtr[6] + cp->rtr[4] * cp->rtr[7] + cp->rtr[5] * cp->rtr[8]) / (cp->rcell[1] * cp->rcell[2])) * kRad2Deg;
8824 cp->rcell[4] = acos((cp->rtr[6] * cp->rtr[0] + cp->rtr[7] * cp->rtr[1] + cp->rtr[8] * cp->rtr[2]) / (cp->rcell[2] * cp->rcell[0])) * kRad2Deg;
8825 cp->rcell[5] = acos((cp->rtr[0] * cp->rtr[3] + cp->rtr[1] * cp->rtr[4] + cp->rtr[2] * cp->rtr[5]) / (cp->rcell[0] * cp->rcell[1])) * kRad2Deg;
8828 /* Calculate a, b, c, alpha, beta, gamma */
8829 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[1] * cp->tr[1] + cp->tr[2] * cp->tr[2]);
8830 cp->cell[1] = sqrt(cp->tr[3] * cp->tr[3] + cp->tr[4] * cp->tr[4] + cp->tr[5] * cp->tr[5]);
8831 cp->cell[2] = sqrt(cp->tr[6] * cp->tr[6] + cp->tr[7] * cp->tr[7] + cp->tr[8] * cp->tr[8]);
8832 cp->cell[3] = acos((cp->tr[3] * cp->tr[6] + cp->tr[4] * cp->tr[7] + cp->tr[5] * cp->tr[8]) / (cp->cell[1] * cp->cell[2])) * kRad2Deg;
8833 cp->cell[4] = acos((cp->tr[6] * cp->tr[0] + cp->tr[7] * cp->tr[1] + cp->tr[8] * cp->tr[2]) / (cp->cell[2] * cp->cell[0])) * kRad2Deg;
8834 cp->cell[5] = acos((cp->tr[0] * cp->tr[3] + cp->tr[1] * cp->tr[4] + cp->tr[2] * cp->tr[5]) / (cp->cell[0] * cp->cell[1])) * kRad2Deg;
8841 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
8850 memset(&cmat, 0, sizeof(Transform));
8851 if (mp->cell != NULL)
8852 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
8854 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
8856 if (mp->cell != NULL) {
8858 mp->needsMDRebuild = 1;
8864 cp = (XtalCell *)calloc(sizeof(XtalCell), 1);
8866 Panic("Low memory during setting cell parameters");
8868 mp->needsMDRebuild = 1;
8870 /* alpha, beta, gamma are in degree */
8874 cp->cell[3] = alpha;
8876 cp->cell[5] = gamma;
8877 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
8878 /* c unique (hexagonal etc.) */
8879 Double cosa, cosb, sinb, cosg;
8880 cosa = cos(alpha * kDeg2Rad);
8881 cosb = cos(beta * kDeg2Rad);
8882 sinb = sin(beta * kDeg2Rad);
8883 cosg = cos(gamma * kDeg2Rad);
8884 cp->axes[0].x = a * sinb;
8886 cp->axes[0].z = a * cosb;
8887 cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
8888 cp->axes[1].z = b * cosa;
8889 cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
8895 Double cosg, sing, cosa, cosb;
8896 cosa = cos(alpha * kDeg2Rad);
8897 cosb = cos(beta * kDeg2Rad);
8898 cosg = cos(gamma * kDeg2Rad);
8899 sing = sin(gamma * kDeg2Rad);
8900 cp->axes[0].x = a * sing;
8901 cp->axes[0].y = a * cosg;
8906 cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
8907 cp->axes[2].y = c * cosa;
8908 cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
8910 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
8911 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
8912 MoleculeCalculateCellFromAxes(cp, 0);
8913 TransformMul(cmat, cp->tr, cmat);
8916 /* Update the coordinates (if requested) */
8917 if (convertCoordinates) {
8918 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8919 TransformVec(&(ap->r), cmat, &(ap->r));
8923 /* Update the anisotropic parameters */
8924 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8925 Aniso *anp = ap->aniso;
8927 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
8930 __MoleculeUnlock(mp);
8931 sMoleculeNotifyChangeAppearance(mp);
8935 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x13, Double x23, const Double *sigmaptr)
8939 const Double log2 = 0.693147180559945;
8940 const Double pi22 = 19.7392088021787; /* 2*pi**2 */
8946 if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
8948 anp = mp->atoms[n1].aniso;
8951 anp = (Aniso *)calloc(sizeof(Aniso), 1);
8953 __MoleculeUnlock(mp);
8954 Panic("Low memory during setting anisotropic atom parameters");
8956 mp->atoms[n1].aniso = anp;
8959 case 1: d = 1; dx = 0.5; break;
8960 case 2: d = log2; dx = log2; break;
8961 case 3: d = log2; dx = log2 * 0.5; break;
8962 case 4: u = 1; d = 0.25; dx = 0.25; break;
8963 case 5: u = 1; d = 0.25; dx = 0.125; break;
8964 case 8: u = 1; d = pi22; dx = pi22; break;
8965 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
8966 case 10: d = pi22; dx = pi22; break;
8967 default: d = dx = 1; break;
8969 anp->bij[0] = x11 * d;
8970 anp->bij[1] = x22 * d;
8971 anp->bij[2] = x33 * d;
8972 anp->bij[3] = x12 * dx;
8973 anp->bij[4] = x13 * dx;
8974 anp->bij[5] = x23 * dx;
8975 if (sigmaptr != NULL) {
8977 anp->bsig[0] = sigmaptr[0] * d;
8978 anp->bsig[1] = sigmaptr[1] * d;
8979 anp->bsig[2] = sigmaptr[2] * d;
8980 anp->bsig[3] = sigmaptr[3] * dx;
8981 anp->bsig[4] = sigmaptr[4] * dx;
8982 anp->bsig[5] = sigmaptr[5] * dx;
8985 anp->bsig[0] = anp->bsig[1] = anp->bsig[2] = anp->bsig[3] = anp->bsig[4] = anp->bsig[5] = 0.0;
8988 if (cp != NULL && u == 1) {
8989 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
8990 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
8991 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
8992 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
8993 anp->bij[4] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[3] * kDeg2Rad); */
8994 anp->bij[5] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[4] * kDeg2Rad); */
8995 if (sigmaptr != NULL) {
8996 anp->bsig[0] *= cp->rcell[0] * cp->rcell[0];
8997 anp->bsig[1] *= cp->rcell[1] * cp->rcell[1];
8998 anp->bsig[2] *= cp->rcell[2] * cp->rcell[2];
8999 anp->bsig[3] *= cp->rcell[0] * cp->rcell[1];
9000 anp->bsig[4] *= cp->rcell[2] * cp->rcell[0];
9001 anp->bsig[5] *= cp->rcell[1] * cp->rcell[2];
9005 /* Calculate the principal axes (in Cartesian coordinates) */
9006 /* The principal axes are the eigenvectors of matrix At(B^-1)A, where
9007 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
9008 in which x and z are the crystal-space and cartesian coordinates. */
9009 m1[0] = anp->bij[0] / pi22;
9010 m1[4] = anp->bij[1] / pi22;
9011 m1[8] = anp->bij[2] / pi22;
9012 m1[1] = m1[3] = anp->bij[3] / pi22;
9013 m1[2] = m1[6] = anp->bij[4] / pi22;
9014 m1[5] = m1[7] = anp->bij[5] / pi22;
9015 MatrixInvert(m1, m1);
9017 memmove(m2, cp->rtr, sizeof(Mat33));
9018 MatrixMul(m1, m1, m2);
9019 MatrixTranspose(m2, m2);
9020 MatrixMul(m1, m2, m1);
9022 MatrixSymDiagonalize(m1, val, axis);
9023 for (u = 0; u < 3; u++) {
9025 fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
9028 val[u] = 1 / sqrt(val[u]);
9030 anp->pmat[u*3] = axis[u].x * val[u];
9031 anp->pmat[u*3+1] = axis[u].y * val[u];
9032 anp->pmat[u*3+2] = axis[u].z * val[u];
9034 __MoleculeUnlock(mp);
9037 /* Set the anisotropic parameter for atom idx according to the symop. If symop is not alive, nothing is done. */
9039 MoleculeSetAnisoBySymop(Molecule *mp, int idx)
9043 if (mp == NULL || idx < 0 || idx >= mp->natoms)
9045 ap = ATOM_AT_INDEX(mp->atoms, idx);
9046 if (!SYMOP_ALIVE(ap->symop))
9048 ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
9049 if (ap2->aniso == NULL) {
9050 if (ap->aniso != NULL) {
9056 if (ap->aniso == NULL)
9057 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
9058 if (ap->symop.sym == 0 || ap->symop.sym >= mp->nsyms) {
9059 /* Just copy the aniso parameters */
9060 memmove(ap->aniso, ap2->aniso, sizeof(Aniso));
9063 memmove(t1, SYMMETRY_AT_INDEX(mp->syms, ap->symop.sym), sizeof(Transform));
9064 t1[9] = t1[10] = t1[11] = 0.0;
9065 memset(t2, 0, sizeof(Transform));
9066 t2[0] = ap2->aniso->bij[0];
9067 t2[4] = ap2->aniso->bij[1];
9068 t2[8] = ap2->aniso->bij[2];
9069 t2[1] = t2[3] = ap2->aniso->bij[3];
9070 t2[2] = t2[6] = ap2->aniso->bij[4];
9071 t2[5] = t2[7] = ap2->aniso->bij[5];
9072 TransformMul(t2, t1, t2);
9073 TransformInvert(t1, t1);
9074 TransformMul(t2, t2, t1);
9075 MoleculeSetAniso(mp, idx, 0, t2[0], t2[4], t2[8], t2[1], t2[2], t2[5], (ap2->aniso->has_bsig ? ap2->aniso->bsig : NULL));
9079 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic, int convertCoordinates)
9081 static Vector zeroVec = {0, 0, 0};
9088 if (mp->cell != NULL)
9089 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
9091 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
9093 if (mp->cell != NULL) {
9095 mp->needsMDRebuild = 1;
9100 memset(&b, 0, sizeof(b));
9101 b.axes[0] = (ax != NULL ? *ax : zeroVec);
9102 b.axes[1] = (ay != NULL ? *ay : zeroVec);
9103 b.axes[2] = (az != NULL ? *az : zeroVec);
9105 memmove(b.flags, periodic, 3);
9106 if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
9109 if (mp->cell == NULL) {
9110 mp->needsMDRebuild = 1;
9112 if (mp->cell->has_sigma) {
9113 /* Keep the sigma */
9115 memmove(b.cellsigma, mp->cell->cellsigma, sizeof(mp->cell->cellsigma));
9117 if ((b.flags[0] != mp->cell->flags[0]) || (b.flags[1] != mp->cell->flags[1]) || (b.flags[2] != mp->cell->flags[2])) {
9118 mp->needsMDRebuild = 1;
9122 mp->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
9123 if (mp->cell != NULL) {
9124 memmove(mp->cell, &b, sizeof(XtalCell));
9125 TransformMul(cmat, b.tr, cmat);
9126 /* Update the coordinates (if requested) */
9127 if (convertCoordinates) {
9128 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9129 TransformVec(&(ap->r), cmat, &(ap->r));
9133 /* Update the anisotropic parameters */
9134 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9135 Aniso *anp = ap->aniso;
9137 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
9141 } else n = -2; /* Out of memory */
9142 __MoleculeUnlock(mp);
9143 sMoleculeNotifyChangeAppearance(mp);
9147 #pragma mark ====== Fragment manipulation ======
9150 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
9154 if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
9156 IntGroupAdd(result, idx, 1);
9157 ap = ATOM_AT_INDEX(mp->atoms, idx);
9158 cp = AtomConnectData(&ap->connect);
9159 for (i = 0; i < ap->connect.count; i++) {
9161 if (IntGroupLookup(result, idx2, NULL))
9163 sMoleculeFragmentSub(mp, idx2, result, exatoms);
9167 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
9168 not containing the atoms in exatoms */
9170 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
9173 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9175 result = IntGroupNew();
9176 sMoleculeFragmentSub(mp, n1, result, exatoms);
9180 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
9181 not containing the atoms n2, n3, ... (terminated by -1) */
9183 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
9186 IntGroup *exatoms, *result;
9187 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9189 exatoms = IntGroupNew();
9190 for (i = 0; i < argc; i++)
9191 IntGroupAdd(exatoms, argv[i], 1);
9192 result = IntGroupNew();
9193 sMoleculeFragmentSub(mp, n1, result, exatoms);
9194 IntGroupRelease(exatoms);
9198 /* The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
9199 not containing the atoms in exatoms */
9201 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
9203 IntGroupIterator iter;
9206 if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
9208 IntGroupIteratorInit(inatoms, &iter);
9209 result = IntGroupNew();
9210 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9211 sMoleculeFragmentSub(mp, i, result, exatoms);
9213 IntGroupIteratorRelease(&iter);
9217 /* Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
9218 group is bound to the rest of the molecule via only one bond.
9219 If the result is true, then the atoms belonging to the (only) bond are returned
9220 in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
9221 and n2 can be NULL, if those informations are not needed. */
9223 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
9225 Int i, i1, i2, j, k, bond_count, nval1, nval2, *cp;
9227 if (mp == NULL || mp->natoms == 0 || group == NULL)
9228 return 0; /* Invalid arguments */
9230 for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
9231 i2 = IntGroupGetEndPoint(group, i);
9232 for (j = i1; j < i2; j++) {
9233 if (j < 0 || j >= mp->natoms)
9234 return 0; /* Invalid atom group */
9235 ap = ATOM_AT_INDEX(mp->atoms, j);
9236 cp = AtomConnectData(&ap->connect);
9237 for (k = 0; k < ap->connect.count; k++) {
9238 if (IntGroupLookup(group, cp[k], NULL) == 0) {
9243 return 0; /* Too many bonds */
9248 if (bond_count == 1) {
9259 /* Returns non-zero if the given group is 'rotatable' in the molecule. The group
9260 is said to be 'rotatable' when either of the following conditions are met; (1)
9261 the group is detachable, or (2) the group consists of two bonded atoms that define
9262 a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
9263 (either a new IntGroup or 'group' with incremented reference count; thus the caller
9264 is responsible for releasing the returned value). */
9266 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
9269 if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
9270 if (rotGroup != NULL) {
9271 IntGroupRetain(group);
9276 if (group != NULL && IntGroupGetCount(group) == 2) {
9277 i1 = IntGroupGetNthPoint(group, 0);
9278 i2 = IntGroupGetNthPoint(group, 1);
9279 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
9280 IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
9283 i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
9285 IntGroupRelease(frag);
9286 if (rotGroup != NULL)
9290 if (rotGroup != NULL)
9292 else if (frag != NULL)
9293 IntGroupRelease(frag);
9300 #pragma mark ====== Multiple frame ======
9303 MoleculeGetNumberOfFrames(Molecule *mp)
9307 if (mp->nframes <= 0) {
9311 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9312 if (ap->nframes > n)
9323 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
9325 int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
9328 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
9331 n_old = MoleculeGetNumberOfFrames(mp);
9332 n_new = n_old + count;
9333 last_inserted = IntGroupGetNthPoint(group, count - 1);
9334 if (n_new <= last_inserted) {
9335 exframes = last_inserted - n_new + 1; /* number of extra frames that will be silently inserted */
9337 } else exframes = 0;
9339 tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4); /* "*4" for handling cells */
9345 /* Copy back the current coordinates */
9346 /* No change in the current coordinates, but the frame buffer is updated */
9347 MoleculeSelectFrame(mp, mp->cframe, 1);
9349 /* Expand ap->frames for all atoms */
9350 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9351 if (ap->frames == NULL)
9352 vp = (Vector *)calloc(sizeof(Vector), n_new);
9354 vp = (Vector *)realloc(ap->frames, sizeof(Vector) * n_new);
9356 __MoleculeUnlock(mp);
9359 for (j = ap->nframes; j < n_new; j++)
9363 if (mp->cell != NULL && (mp->useFlexibleCell || inFrameCell != NULL)) {
9364 mp->useFlexibleCell = 1;
9365 vp = mp->frame_cells;
9366 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, n_new - 1, NULL);
9368 /* Set the first cell parameters */
9369 mp->frame_cells[0] = mp->cell->axes[0];
9370 mp->frame_cells[1] = mp->cell->axes[1];
9371 mp->frame_cells[2] = mp->cell->axes[2];
9372 mp->frame_cells[3] = mp->cell->origin;
9376 /* group = [n0..n1-1, n2..n3-1, ...] */
9378 /* tempv[0..n0-1] <- ap[0..n0-1], s += n0,
9379 tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
9380 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
9381 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
9383 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
9384 At last, s will become n_old and t will become count. */
9385 for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9386 int s, t, ns, ne, mult;
9389 if (i == mp->natoms) {
9390 if (mp->cell == NULL || mp->frame_cells == NULL)
9392 vp = mp->frame_cells;
9399 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
9401 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
9404 ne = IntGroupGetEndPoint(group, j);
9406 if (i == mp->natoms) {
9407 if (inFrameCell != NULL) {
9408 tempv[ns * 4] = inFrameCell[t * 4];
9409 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
9410 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
9411 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
9413 tempv[ns * 4] = mp->cell->axes[0];
9414 tempv[ns * 4 + 1] = mp->cell->axes[1];
9415 tempv[ns * 4 + 2] = mp->cell->axes[2];
9416 tempv[ns * 4 + 3] = mp->cell->origin;
9419 if (inFrame != NULL)
9420 tempv[ns] = inFrame[natoms * t + i];
9429 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
9433 ap->nframes = n_new;
9434 memmove(vp, tempv, sizeof(Vector) * mult * n_new);
9437 mp->nframes = n_new;
9438 MoleculeSelectFrame(mp, last_inserted, 0);
9439 MoleculeIncrementModifyCount(mp);
9440 __MoleculeUnlock(mp);
9445 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
9447 int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
9450 IntGroup *group, *group2;
9452 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
9455 /* outFrame[] should have enough size for Vector * natoms * group.count */
9456 memset(outFrame, 0, sizeof(Vector) * natoms * count);
9457 if (mp->cell != NULL && mp->frame_cells != NULL)
9458 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
9460 n_old = MoleculeGetNumberOfFrames(mp);
9462 return -2; /* Cannot delete last frame */
9464 group = IntGroupNew();
9465 group2 = IntGroupNewWithPoints(0, n_old, -1);
9466 IntGroupIntersect(inGroup, group2, group);
9467 IntGroupRelease(group2);
9468 count = IntGroupGetCount(group);
9469 n_new = n_old - count;
9471 IntGroupRelease(group);
9472 return -2; /* Trying to delete too many frames */
9474 tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4); /* "*4" for handling cells */
9475 if (tempv == NULL) {
9476 IntGroupRelease(group);
9482 /* Copy back the current coordinates */
9483 /* No change in the current coordinates, but the frame buffer is updated */
9484 MoleculeSelectFrame(mp, mp->cframe, 1);
9486 /* Determine which frame should be selected after removal is completed */
9489 if (IntGroupLookup(group, mp->cframe, &i)) {
9490 /* cframe will be removed */
9491 n1 = IntGroupGetStartPoint(group, i) - 1;
9493 n1 = IntGroupGetEndPoint(group, i);
9494 } else n1 = mp->cframe;
9495 /* Change to that frame */
9496 MoleculeSelectFrame(mp, n1, 0);
9497 group2 = IntGroupNewFromIntGroup(group);
9498 IntGroupReverse(group2, 0, n_old);
9499 new_cframe = IntGroupLookupPoint(group2, n1);
9501 return -3; /* This cannot happen */
9502 IntGroupRelease(group2);
9505 /* group = [n0..n1-1, n2..n3-1, ...] */
9507 /* tempv[0..n0-1] -> ap[0..n0-1], s += n0,
9508 tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
9509 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
9510 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
9512 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
9513 At last, s will become n_new and t will become count. */
9515 for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9516 int s, t, j, ns, ne;
9518 /* if i == mp->natoms, mp->frame_cells is handled */
9519 if (i == mp->natoms) {
9520 if (mp->cell == NULL || mp->frame_cells == NULL)
9523 vp = mp->frame_cells;
9529 ap->frames = vp = (Vector *)calloc(sizeof(Vector), n_old);
9531 __MoleculeUnlock(mp);
9535 old_count = ap->nframes;
9538 /* Copy vp to tempv */
9539 memset(tempv, 0, sizeof(Vector) * mult * n_old);
9540 memmove(tempv, vp, sizeof(Vector) * mult * (old_count > n_old ? n_old : old_count));
9541 ne = ns = s = t = 0;
9542 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
9546 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (ns - ne));
9549 ne = IntGroupGetEndPoint(group, j);
9554 outFrame[natoms * t + i] = tempv[ns];
9555 else if (outFrameCell != NULL) {
9556 outFrameCell[t * 4] = tempv[ns * 4];
9557 outFrameCell[t * 4 + 1] = tempv[ns * 4 + 1];
9558 outFrameCell[t * 4 + 2] = tempv[ns * 4 + 2];
9559 outFrameCell[t * 4 + 3] = tempv[ns * 4 + 3];
9566 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (n_old - ne));
9574 if (i < mp->natoms) {
9579 free(mp->frame_cells);
9580 mp->frame_cells = NULL;
9581 mp->nframe_cells = 0;
9585 ap->frames = (Vector *)realloc(ap->frames, sizeof(Vector) * s);
9587 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, s - 1, NULL);
9588 mp->nframe_cells = s;
9593 mp->nframes = nframes;
9595 /* Select the "last" frame; do not "copy back" the coordinates to the frame table */
9596 /* i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
9597 MoleculeSelectFrame(mp, new_cframe, 0);
9599 IntGroupRelease(group);
9601 MoleculeIncrementModifyCount(mp);
9602 __MoleculeUnlock(mp);
9607 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
9609 int i, cframe, nframes, modified;
9611 cframe = mp->cframe;
9612 nframes = MoleculeGetNumberOfFrames(mp);
9615 if (mp == NULL || mp->natoms == 0 || frame < 0 || frame >= nframes)
9619 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9620 if (copyback && cframe >= 0 && cframe < ap->nframes) {
9621 /* Write the current coordinate back to the frame array */
9622 ap->frames[cframe] = ap->r;
9624 if (frame != cframe && frame >= 0 && frame < ap->nframes) {
9625 /* Read the coordinate from the frame array */
9626 ap->r = ap->frames[frame];
9631 if (mp->cell != NULL && mp->frame_cells != NULL) {
9632 /* Write the current cell back to the frame_cells array */
9633 if (copyback && cframe >= 0) {
9634 Vector *vp = (Vector *)AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, cframe, NULL);
9635 vp[0] = mp->cell->axes[0];
9636 vp[1] = mp->cell->axes[1];
9637 vp[2] = mp->cell->axes[2];
9638 vp[3] = mp->cell->origin;
9640 /* Set the cell from the frame array */
9641 if (frame != cframe && frame >= 0 && frame < mp->nframe_cells) {
9642 MoleculeSetPeriodicBox(mp, &mp->frame_cells[frame * 4], &mp->frame_cells[frame * 4 + 1], &mp->frame_cells[frame * 4 + 2], &mp->frame_cells[frame * 4 + 3], mp->cell->flags, 0);
9644 MoleculeAmendBySymmetry(mp, NULL, NULL, NULL);
9649 mp->needsMDCopyCoordinates = 1;
9650 __MoleculeUnlock(mp);
9651 sMoleculeNotifyChangeAppearance(mp);
9655 /* If molecule is multi-frame, then flush the current information to the frame buffer.
9656 Returns the number of frames. */
9658 MoleculeFlushFrames(Molecule *mp)
9660 int nframes = MoleculeGetNumberOfFrames(mp);
9662 MoleculeSelectFrame(mp, mp->cframe, 1);
9666 #pragma mark ====== MO calculation ======
9668 /* Calculate an MO value for a single point. */
9669 /* Index is the MO number (1-based) */
9670 /* tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom. */
9672 sCalcMOPoint(const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
9676 Double val, tval, *cnp, *tmpp, *mobasep, *mop;
9678 /* Cache dr and |dr|^2 */
9679 for (i = 0; i < bset->natoms; i++) {
9680 Vector r = bset->pos[i];
9681 tmp[i * 4] = r.x = vp->x - r.x;
9682 tmp[i * 4 + 1] = r.y = vp->y - r.y;
9683 tmp[i * 4 + 2] = r.z = vp->z - r.z;
9684 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
9686 /* Iterate over all shells */
9688 mobasep = bset->mo + (index - 1) * bset->ncomps;
9689 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
9690 pp = bset->priminfos + sp->p_idx;
9691 cnp = bset->cns + sp->cn_idx;
9692 tmpp = tmp + sp->a_idx * 4;
9693 mop = mobasep + sp->m_idx;
9697 for (j = 0; j < sp->nprim; j++) {
9698 tval += *cnp++ * exp(-pp->A * tmpp[3]);
9701 val += mop[0] * tval;
9707 for (j = 0; j < sp->nprim; j++) {
9708 tval = exp(-pp->A * tmpp[3]);
9714 x *= mop[0] * tmpp[0];
9715 y *= mop[1] * tmpp[1];
9716 z *= mop[2] * tmpp[2];
9723 for (j = 0; j < sp->nprim; j++) {
9724 tval = exp(-pp->A * tmpp[3]);
9732 x *= mop[1] * tmpp[0];
9733 y *= mop[2] * tmpp[1];
9734 z *= mop[3] * tmpp[2];
9735 val += t + x + y + z;
9739 Double xx, yy, zz, xy, xz, yz;
9740 xx = yy = zz = xy = xz = yz = 0;
9741 for (j = 0; j < sp->nprim; j++) {
9742 tval = exp(-pp->A * tmpp[3]);
9743 xx += *cnp++ * tval;
9744 yy += *cnp++ * tval;
9745 zz += *cnp++ * tval;
9746 xy += *cnp++ * tval;
9747 xz += *cnp++ * tval;
9748 yz += *cnp++ * tval;
9751 xx *= mop[0] * tmpp[0] * tmpp[0];
9752 yy *= mop[1] * tmpp[1] * tmpp[1];
9753 zz *= mop[2] * tmpp[2] * tmpp[2];
9754 xy *= mop[3] * tmpp[0] * tmpp[1];
9755 xz *= mop[4] * tmpp[0] * tmpp[2];
9756 yz *= mop[5] * tmpp[1] * tmpp[2];
9757 val += xx + yy + zz + xy + xz + yz;
9761 Double d0, d1p, d1n, d2p, d2n;
9762 d0 = d1p = d1n = d2p = d2n = 0;
9763 for (j = 0; j < sp->nprim; j++) {
9764 tval = exp(-pp->A * tmpp[3]);
9765 d0 += *cnp++ * tval;
9766 d1p += *cnp++ * tval;
9767 d1n += *cnp++ * tval;
9768 d2p += *cnp++ * tval;
9769 d2n += *cnp++ * tval;
9772 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
9773 d1p *= mop[1] * tmpp[0] * tmpp[2];
9774 d1n *= mop[2] * tmpp[1] * tmpp[2];
9775 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
9776 d2n *= mop[4] * tmpp[0] * tmpp[1];
9777 val += d0 + d1p + d1n + d2p + d2n;
9785 /* Calculate one MO. The input vectors should be in bohr unit (angstrom * 1.889725989 = kAngstrom2Bohr). */
9786 /* mono is the MO number (1-based) */
9788 MoleculeCalcMO(Molecule *mp, Int mono, const Vector *op, const Vector *dxp, const Vector *dyp, const Vector *dzp, Int nx, Int ny, Int nz, int (*callback)(double progress, void *ref), void *ref)
9790 int ix, iy, iz, n, nn;
9793 if (mp == NULL || mp->bset == NULL)
9795 if (mp->bset->cns == NULL) {
9796 if (sSetupGaussianCoefficients(mp->bset) != 0)
9799 cp = (Cube *)calloc(sizeof(Cube), 1);
9803 cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
9804 if (cp->dp == NULL) {
9817 /* TODO: use multithread */
9818 tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms * 4);
9825 for (ix = 0; ix < nx; ix++) {
9827 for (iy = 0; iy < ny; iy++) {
9828 for (iz = 0; iz < nz; iz++) {
9829 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
9830 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
9831 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
9832 cp->dp[n++] = sCalcMOPoint(mp->bset, mono, &p, tmp);
9834 if (callback != NULL && n - nn > 100) {
9836 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
9840 return -2; /* User interrupt */
9847 AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
9848 return mp->bset->ncubes - 1;
9852 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
9855 Vector rmin, rmax, *vp;
9856 Double dr, dx, dy, dz;
9857 if (mp == NULL || mp->bset == NULL || mp->bset->natoms == 0)
9861 rmin.x = rmin.y = rmin.z = 1e10;
9862 rmax.x = rmax.y = rmax.z = -1e10;
9863 for (i = 0, vp = mp->bset->pos; i < mp->bset->natoms; i++, vp++) {
9864 dr = RadiusForAtomicNumber(ATOM_AT_INDEX(mp->atoms, i)->atomicNumber);
9867 dr = dr * kAngstrom2Bohr * 3.0 + 2.0;
9868 if (rmin.x > vp->x - dr)
9869 rmin.x = vp->x - dr;
9870 if (rmin.y > vp->y - dr)
9871 rmin.y = vp->y - dr;
9872 if (rmin.z > vp->z - dr)
9873 rmin.z = vp->z - dr;
9874 if (rmax.x < vp->x + dr)
9875 rmax.x = vp->x + dr;
9876 if (rmax.y < vp->y + dr)
9877 rmax.y = vp->y + dr;
9878 if (rmax.z < vp->z + dr)
9879 rmax.z = vp->z + dr;
9881 dx = rmax.x - rmin.x;
9882 dy = rmax.y - rmin.y;
9883 dz = rmax.z - rmin.z;
9884 dr = pow(dx * dy * dz / npoints, 1.0/3.0);
9885 *nx = floor(dx / dr + 0.5);
9886 *ny = floor(dy / dr + 0.5);
9887 *nz = floor(dz / dr + 0.5);
9895 xp->x = yp->y = zp->z = dr;
9896 xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
9901 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
9903 if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
9905 return mp->bset->cubes[index];
9909 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
9912 if (mp == NULL || mp->bset == NULL)
9914 for (i = 0; i < mp->bset->ncubes; i++) {
9915 if (mp->bset->cubes[i]->idn == mono)
9922 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
9925 if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
9927 CubeRelease(mp->bset->cubes[index]);
9929 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
9930 if (--(mp->bset->ncubes) == 0) {
9931 free(mp->bset->cubes);
9932 mp->bset->cubes = NULL;
9934 return mp->bset->ncubes;
9938 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
9943 if (mp == NULL || mp->bset == NULL)
9944 return -1; /* Molecule or the basis set information is empty */
9945 cp = MoleculeGetCubeAtIndex(mp, index);
9947 return -2; /* MO not yet calculated */
9948 fp = fopen(fname, "wb");
9950 return -3; /* Cannot create file */
9953 fprintf(fp, "%s MO=%d\n", comment, cp->idn);
9954 fprintf(fp, " MO coefficients\n");
9956 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms), cp->origin.x, cp->origin.y, cp->origin.z);
9957 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx, cp->dx.x, cp->dx.y, cp->dx.z);
9958 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny, cp->dy.x, cp->dy.y, cp->dy.z);
9959 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz, cp->dz.x, cp->dz.y, cp->dz.z);
9961 /* Atomic information */
9962 for (i = 0; i < mp->bset->natoms; i++) {
9963 Vector *vp = mp->bset->pos + i;
9964 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
9965 /* The second number should actually be the effective charge */
9966 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber, vp->x, vp->y, vp->z);
9968 fprintf(fp, "%5d%5d\n", 1, 1);
9971 for (i = n = 0; i < cp->nx; i++) {
9972 for (j = 0; j < cp->ny; j++) {
9973 for (k = 0; k < cp->nz; k++) {
9974 fprintf(fp, " %12.5e", cp->dp[n++]);
9975 if (k == cp->nz - 1 || k % 6 == 5)