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 #pragma mark ====== Utility function ======
36 strlen_limit(const char *s, int limit)
39 for (len = 0; *s != 0 && (limit < 0 || len < limit); s++, len++);
43 #pragma mark ====== Atom handling ======
46 s_AtomDuplicate(Atom *dst, const Atom *src, Int copy_frame)
49 dst = (Atom *)malloc(gSizeOfAtomRecord);
53 memmove(dst, src, gSizeOfAtomRecord);
54 if (src->aniso != NULL) {
55 dst->aniso = (Aniso *)malloc(sizeof(Aniso));
56 if (dst->aniso != NULL)
57 memmove(dst->aniso, src->aniso, sizeof(Aniso));
59 if (src->frames != NULL && copy_frame) {
60 dst->frames = (Vector *)malloc(sizeof(Vector) * src->nframes);
61 if (dst->frames != NULL) {
62 memmove(dst->frames, src->frames, sizeof(Vector) * src->nframes);
63 dst->nframes = src->nframes;
68 if (src->nconnects > ATOM_CONNECTS_LIMIT) {
69 dst->connects.ptr = NULL;
71 NewArray(&(dst->connects.ptr), &(dst->nconnects), sizeof(Int), src->nconnects);
72 memmove(dst->connects.ptr, src->connects.ptr, sizeof(Int) * src->nconnects);
78 AtomDuplicate(Atom *dst, const Atom *src)
80 return s_AtomDuplicate(dst, src, 1);
84 AtomDuplicateNoFrame(Atom *dst, const Atom *src)
86 return s_AtomDuplicate(dst, src, 0);
92 if (ap->aniso != NULL) {
96 if (ap->frames != NULL) {
104 CubeRelease(Cube *cp)
114 BasisSetRelease(BasisSet *bset)
119 if (bset->shells != NULL)
121 if (bset->priminfos != NULL)
122 free(bset->priminfos);
123 if (bset->mo != NULL)
125 if (bset->cns != NULL)
127 if (bset->moenergies != NULL)
128 free(bset->moenergies);
129 if (bset->scfdensities != NULL)
130 free(bset->scfdensities);
131 if (bset->pos != NULL)
133 if (bset->nuccharges != NULL)
134 free(bset->nuccharges);
135 if (bset->cubes != NULL) {
136 for (i = 0; i < bset->ncubes; i++) {
137 CubeRelease(bset->cubes[i]);
144 #define ATOM_CONNECTS_PTR(ap) ((ap)->nconnects > ATOM_CONNECTS_LIMIT ? (ap)->connects.ptr : (ap)->connects.data)
147 AtomConnects(Atom *ap)
151 return ATOM_CONNECTS_PTR(ap);
155 AtomConnectEntryAtIndex(Atom *ap, Int idx)
159 if (idx < 0 || idx >= ap->nconnects)
161 return ATOM_CONNECTS_PTR(ap)[idx];
165 AtomResizeConnects(Atom *ap, Int nconnects)
170 if (nconnects <= ATOM_CONNECTS_LIMIT) {
171 if (ap->nconnects > ATOM_CONNECTS_LIMIT) {
172 p = ap->connects.ptr;
173 memmove(ap->connects.data, p, sizeof(Int) * nconnects);
177 if (ap->nconnects <= ATOM_CONNECTS_LIMIT) {
180 NewArray(&p, &(ap->nconnects), sizeof(Int), nconnects);
181 memmove(p, ap->connects.data, sizeof(Int) * ap->nconnects);
182 ap->connects.ptr = p;
183 } else if (ap->nconnects < nconnects) {
185 AssignArray(&(ap->connects.ptr), &(ap->nconnects), sizeof(Int), nconnects - 1, NULL);
188 ap->nconnects = nconnects;
192 AtomSetConnectEntry(Atom *ap, Int idx, Int connect)
196 if (idx >= ap->nconnects) {
197 /* Insert a new entry at the last */
198 AtomResizeConnects(ap, idx + 1);
200 ATOM_CONNECTS_PTR(ap)[idx] = connect;
204 AtomInsertConnectEntry(Atom *ap, Int idx, Int connect)
209 if (idx < 0 || idx >= ap->nconnects)
211 AtomResizeConnects(ap, ap->nconnects + 1);
212 n = ap->nconnects - idx - 1; /* Number of entries to be moved towards the bottom */
213 p = ATOM_CONNECTS_PTR(ap);
215 memmove(p + idx + 1, p + idx, sizeof(Int) * n);
221 AtomDeleteConnectEntry(Atom *ap, Int idx)
226 if (idx < 0 || idx >= ap->nconnects)
228 n = ap->nconnects - idx - 1; /* Number of entries to be moved towards the top */
229 p = ATOM_CONNECTS_PTR(ap);
231 memmove(p + idx, p + idx + 1, sizeof(Int) * n);
233 AtomResizeConnects(ap, ap->nconnects - 1);
236 #pragma mark ====== Accessor types ======
239 MolEnumerableNew(Molecule *mol, int kind)
241 MolEnumerable *mseq = (MolEnumerable *)calloc(sizeof(MolEnumerable), 1);
243 mseq->mol = MoleculeRetain(mol);
250 MolEnumerableRelease(MolEnumerable *mseq)
253 MoleculeRelease(mseq->mol);
259 AtomRefNew(Molecule *mol, int idx)
261 AtomRef *aref = (AtomRef *)calloc(sizeof(AtomRef), 1);
263 aref->mol = MoleculeRetain(mol);
270 AtomRefRelease(AtomRef *aref)
273 MoleculeRelease(aref->mol);
278 #pragma mark ====== Creation of molecules ======
284 Molecule *mp = (Molecule *)calloc(sizeof(Molecule), 1);
286 Panic("Cannot allocate new molecule record");
287 snprintf(name, sizeof name, "Untitled %d", sMoleculeUntitledCount++);
288 ObjectInit((Object *)mp, (Object **)&sMoleculeRoot, name);
293 MoleculeNewWithName(const char *name)
295 Molecule *mp = MoleculeNew();
296 MoleculeSetName(mp, name);
301 MoleculeInitWithAtoms(Molecule *mp, const Atom *atoms, int natoms)
308 if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
309 Panic("Cannot allocate memory for atoms");
310 for (i = 0; i < natoms; i++)
311 AtomDuplicate(mp->atoms + i, atoms + i);
312 mp->nframes = -1; /* Should be recalculated later */
317 MoleculeInitWithMolecule(Molecule *mp2, const Molecule *mp)
319 MoleculeInitWithAtoms(mp2, mp->atoms, mp->natoms);
320 if (mp->nbonds > 0) {
321 if (NewArray(&mp2->bonds, &mp2->nbonds, sizeof(Int)*2, mp->nbonds) == NULL)
323 memmove(mp2->bonds, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
325 if (mp->nangles > 0) {
326 if (NewArray(&mp2->angles, &mp2->nangles, sizeof(Int)*3, mp->nangles) == NULL)
328 memmove(mp2->angles, mp->angles, sizeof(Int) * 3 * mp->nangles);
330 if (mp->ndihedrals > 0) {
331 if (NewArray(&mp2->dihedrals, &mp2->ndihedrals, sizeof(Int)*4, mp->ndihedrals) == NULL)
333 memmove(mp2->dihedrals, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
335 if (mp->nimpropers > 0) {
336 if (NewArray(&mp2->impropers, &mp2->nimpropers, sizeof(Int)*4, mp->nimpropers) == NULL)
338 memmove(mp2->impropers, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
340 if (mp->nresidues > 0) {
341 if (NewArray(&mp2->residues, &mp2->nresidues, sizeof(mp->residues[0]), mp->nresidues) == NULL)
343 memmove(mp2->residues, mp->residues, sizeof(mp->residues[0]) * mp->nresidues);
345 if (mp->cell != NULL) {
346 mp2->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
347 memmove(mp2->cell, mp->cell, sizeof(XtalCell));
350 mp2->nsyms = mp->nsyms;
351 mp2->syms = (Transform *)calloc(sizeof(Transform), mp2->nsyms);
352 memmove(mp2->syms, mp->syms, sizeof(Transform) * mp2->nsyms);
355 mp2->par = ParameterDuplicate(mp->par);
356 if (mp->arena != NULL) {
358 md_arena_init_from_arena(mp2->arena, mp->arena);
363 Panic("Cannot allocate memory for duplicate molecule");
364 return NULL; /* Not reached */
367 /* Assign a unique name to this parameter record */
369 MoleculeSetName(Molecule *mp, const char *name)
371 ObjectSetName((Object *)mp, name, (Object *)sMoleculeRoot);
375 MoleculeGetName(Molecule *mp)
377 return ObjectGetName((Object *)mp);
381 MoleculeWithName(const char *name)
383 return (Molecule *)ObjectWithName(name, (Object *)sMoleculeRoot);
387 MoleculeSetPath(Molecule *mol, const char *fname)
390 if (mol == NULL || fname == NULL)
392 if (fname[0] == '/' || (isalpha(fname[0]) && fname[1] == ':')) {
396 cwd = getcwd(NULL, 0);
397 asprintf(&buf, "%s/%s", cwd, fname);
400 if (mol->path != NULL) {
401 if (strcmp(mol->path, buf) == 0) {
406 free((void *)(mol->path));
409 if (mol->arena != NULL) {
410 md_close_output_files(mol->arena);
415 MoleculeGetPath(Molecule *mol)
423 MoleculeRetain(Molecule *mp)
425 ObjectIncrRefCount((Object *)mp);
430 MoleculeClear(Molecule *mp)
434 if (mp->arena != NULL) {
435 md_arena_set_molecule(mp->arena, NULL);
438 if (mp->par != NULL) {
439 ParameterRelease(mp->par);
442 if (mp->bset != NULL) {
443 BasisSetRelease(mp->bset);
446 if (mp->atoms != NULL) {
448 for (i = 0; i < mp->natoms; i++)
449 AtomClean(mp->atoms + i);
454 if (mp->bonds != NULL) {
459 if (mp->angles != NULL) {
464 if (mp->dihedrals != NULL) {
466 mp->dihedrals = NULL;
469 if (mp->impropers != NULL) {
471 mp->impropers = NULL;
474 if (mp->residues != NULL) {
479 if (mp->elpots != NULL) {
484 if (mp->path != NULL) {
485 free((void *)mp->path);
491 MoleculeRelease(Molecule *mp)
495 if (mp->exmolobj != NULL)
496 MoleculeReleaseExternalHook(mp);
497 if (ObjectDecrRefCount((Object *)mp) == 0) {
499 ObjectDealloc((Object *)mp, (Object **)&sMoleculeRoot);
504 MoleculeExchange(Molecule *mp1, Molecule *mp2)
507 struct MainView *mview1, *mview2;
508 struct MDArena *arena1, *arena2;
509 /* mview and arena must be kept as they are */
514 /* 'natoms' is the first member to be copied */
515 int ofs = offsetof(Molecule, natoms);
516 memmove((char *)(&mp_temp) + ofs, (char *)mp1 + ofs, sizeof(Molecule) - ofs);
517 memmove((char *)mp1 + ofs, (char *)mp2 + ofs, sizeof(Molecule) - ofs);
518 memmove((char *)mp2 + ofs, (char *)(&mp_temp) + ofs, sizeof(Molecule) - ofs);
523 /* if (mp1->arena != NULL && mp1->arena->mol == mp2)
524 mp1->arena->mol = mp1;
525 if (mp1->arena != NULL && mp1->arena->xmol == mp2)
526 mp1->arena->xmol = mp1;
527 if (mp2->arena != NULL && mp2->arena->mol == mp1)
528 mp2->arena->mol = mp2;
529 if (mp2->arena != NULL && mp2->arena->xmol == mp1)
530 mp2->arena->xmol = mp2; */
533 #pragma mark ====== Mutex ======
536 MoleculeLock(Molecule *mol)
538 if (mol == NULL || mol->mutex == NULL)
540 MoleculeCallback_lockMutex(mol->mutex);
544 MoleculeUnlock(Molecule *mol)
546 if (mol == NULL || mol->mutex == NULL)
548 MoleculeCallback_unlockMutex(mol->mutex);
551 #pragma mark ====== Modify count ======
554 MoleculeIncrementModifyCount(Molecule *mp)
557 if (++(mp->modifyCount) == 1)
558 MoleculeCallback_notifyModification(mp, 0);
559 /* fprintf(stderr, "MoleculeIncrementModifyCount: %d\n", mp->modifyCount); */
564 MoleculeClearModifyCount(Molecule *mp)
568 /* fprintf(stderr, "MoleculeClearModifyCount: %d\n", mp->modifyCount); */
572 #pragma mark ====== File handling functions ======
575 guessMoleculeType(const char *fname)
579 const char *retval = NULL;
580 fp = fopen(fname, "rb");
582 memset(buf, 0, sizeof buf);
583 if (fread(buf, 1, sizeof buf - 1, fp) > 0) {
584 if (strncmp(buf, "PSF", 3) == 0)
586 else if (((p = strstr(buf, "ATOM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r'))
587 || ((p = strstr(buf, "HETATM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r')))
590 retval = "???"; /* unknown */
598 guessElement(Atom *ap)
600 int atomicNumber = -1;
601 if (ap->atomicNumber > 0)
602 atomicNumber = ap->atomicNumber;
604 atomicNumber = GuessAtomicNumber(ap->element, ap->weight);
605 if (atomicNumber <= 0 && ap->aname[0] != 0)
606 atomicNumber = GuessAtomicNumber(ap->aname, ap->weight);
608 if (atomicNumber >= 0) {
609 ap->atomicNumber = atomicNumber;
611 ap->weight = WeightForAtomicNumber(atomicNumber);
612 if (ap->element[0] == 0)
613 ElementToString(atomicNumber, ap->element);
619 sReadLineWithInterrupt(char *buf, int size, FILE *stream, int *lineNumber)
621 static int lastLineNumber = 0;
622 if (lineNumber != NULL) {
623 if (*lineNumber == 0)
625 else if (*lineNumber >= lastLineNumber + 1000) {
626 if (MyAppCallback_checkInterrupt() != 0)
627 return -1; /* User interrupt */
628 lastLineNumber = *lineNumber;
631 return ReadLine(buf, size, stream, lineNumber);
635 MoleculeLoadFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
638 if (ftype == NULL || *ftype == 0) {
640 cp = strrchr(fname, '.');
644 cp = guessMoleculeType(fname);
645 if (strcmp(cp, "???") != 0)
649 if (strcasecmp(ftype, "psf") == 0) {
650 retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
651 } else if (strcasecmp(ftype, "pdb") == 0) {
652 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
653 } else if (strcasecmp(ftype, "tep") == 0) {
654 retval = MoleculeLoadTepFile(mp, fname, errbuf, errbufsize);
655 } else if (strcasecmp(ftype, "res") == 0 || strcasecmp(ftype, "ins") == 0) {
656 retval = MoleculeLoadShelxFile(mp, fname, errbuf, errbufsize);
657 } else if (strcasecmp(ftype, "fchk") == 0 || strcasecmp(ftype, "fch") == 0) {
658 retval = MoleculeLoadGaussianFchkFile(mp, fname, errbuf, errbufsize);
660 snprintf(errbuf, errbufsize, "Unknown format %s", ftype);
663 /* if (retval != 0) {
664 retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
667 MoleculeSetPath(mp, fname);
672 MoleculeLoadMbsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
676 int i, j, k, n, err, fn, nframes;
685 char *bufp, *valp, *comp;
690 const int kUndefined = -10000000;
692 if (errbuf == NULL) {
697 if (mp->natoms != 0 || mp->par != NULL || mp->arena != NULL) {
698 snprintf(errbuf, errbufsize, "The molecule must be empty");
701 fp = fopen(fname, "rb");
703 snprintf(errbuf, errbufsize, "Cannot open file");
706 for (i = 0; i < 8; i++)
707 mview_fbuf[i] = kUndefined;
708 for (i = 0; i < 16; i++)
709 mview_ibuf[i] = kUndefined;
714 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
715 if (strncmp(buf, "!:", 2) != 0)
716 continue; /* Skip until section header is found */
718 strsep(&bufp, " \t\n");
719 if (strcmp(buf, "!:atoms") == 0) {
720 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
725 /* idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge */
726 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) {
727 snprintf(errbuf, errbufsize, "line %d: coordinates cannot be read for atom %d", lineNumber, mp->natoms + 1);
730 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
731 strncpy(ap->segName, cbuf[0], 4);
732 ap->resSeq = ibuf[1];
733 strncpy(ap->resName, cbuf[1], 4);
734 strncpy(ap->aname, cbuf[2], 4);
735 ap->type = AtomTypeEncodeToUInt(cbuf[3]);
736 ap->charge = dbuf[0];
737 ap->weight = dbuf[1];
738 strncpy(ap->element, cbuf[4], 2);
739 ap->atomicNumber = ibuf[2];
740 ap->occupancy = dbuf[2];
741 ap->tempFactor = dbuf[3];
742 ap->intCharge = ibuf[3];
745 } else if (strcmp(buf, "!:atoms_symop") == 0) {
747 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
752 /* idx symop symbase */
753 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
754 snprintf(errbuf, errbufsize, "line %d: symmetry operations cannot be read for atom %d", lineNumber, i + 1);
757 if (i >= mp->natoms) {
758 snprintf(errbuf, errbufsize, "line %d: too many atomic symmetry info\n", lineNumber);
761 ap = ATOM_AT_INDEX(mp->atoms, i);
762 ap->symop.sym = ibuf[1] / 1000000;
763 ap->symop.dx = (ibuf[1] % 1000000) / 10000;
764 ap->symop.dy = (ibuf[1] % 10000) / 100;
765 ap->symop.dz = ibuf[1] % 100;
766 ap->symbase = ibuf[2];
770 } else if (strcmp(buf, "!:atoms_fix") == 0) {
772 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
777 /* idx fix_force fix_pos */
778 if (sscanf(buf, "%d %lf %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]) < 5) {
779 snprintf(errbuf, errbufsize, "line %d: fix atom info cannot be read for atom %d", lineNumber, i + 1);
782 if (i >= mp->natoms) {
783 snprintf(errbuf, errbufsize, "line %d: too many fix atom info\n", lineNumber);
786 ap = ATOM_AT_INDEX(mp->atoms, i);
787 ap->fix_force = dbuf[0];
788 ap->fix_pos.x = dbuf[1];
789 ap->fix_pos.y = dbuf[2];
790 ap->fix_pos.z = dbuf[3];
794 } else if (strcmp(buf, "!:mm_exclude") == 0) {
796 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
801 /* idx mm_exclude periodic_exclude */
802 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
803 snprintf(errbuf, errbufsize, "line %d: mm_exclude flags cannot be read for atom %d", lineNumber, i + 1);
806 if (i >= mp->natoms) {
807 snprintf(errbuf, errbufsize, "line %d: too many mm_exclude flags\n", lineNumber);
810 ap = ATOM_AT_INDEX(mp->atoms, i);
811 ap->mm_exclude = (ibuf[1] != 0);
812 ap->periodic_exclude = (ibuf[2] != 0);
816 } else if (strcmp(buf, "!:positions") == 0) {
818 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
824 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) {
825 snprintf(errbuf, errbufsize, "line %d: atom position cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
828 if (j > 4 && nframes != 0) {
829 snprintf(errbuf, errbufsize, "line %d: atom position sigma can only be given for frame 0", lineNumber);
832 if (j > 4 && j != 7) {
833 snprintf(errbuf, errbufsize, "line %d: atom position sigma cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
836 if (i >= mp->natoms) {
837 snprintf(errbuf, errbufsize, "line %d: too many atom position records\n", lineNumber);
843 ap = ATOM_AT_INDEX(mp->atoms, i);
845 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes, &v);
847 ap->frames[0] = ap->r;
851 ap->sigma.x = dbuf[3];
852 ap->sigma.y = dbuf[4];
853 ap->sigma.z = dbuf[5];
859 mp->nframes = nframes;
860 mp->cframe = nframes - 1;
862 mp->nframes = mp->cframe = 0;
865 } else if (strcmp(buf, "!:bonds") == 0) {
866 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
871 /* from1 to1 from2 to2 from3 to3 from4 to4 */
872 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]);
873 if (i < 2 || i % 2 != 0) {
874 snprintf(errbuf, errbufsize, "line %d: bad bond format", lineNumber);
877 for (j = 0; j < i; j += 2) {
879 iibuf[1] = ibuf[j + 1];
880 if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[0] == iibuf[1]) {
881 snprintf(errbuf, errbufsize, "line %d: bad bond format", lineNumber);
884 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, iibuf);
885 for (k = 0; k < 2; k++) {
887 ap = ATOM_AT_INDEX(mp->atoms, iibuf[k]);
888 cp = AtomConnects(ap);
889 for (n = 0; n < ap->nconnects; n++, cp++) {
890 if (*cp == iibuf[1 - k])
893 if (n >= ap->nconnects) {
894 /* if (ap->nconnects >= ATOMS_MAX_CONNECTS - 1) {
895 snprintf(errbuf, errbufsize, "line %d: too many bonds on atom %d", lineNumber, iibuf[k]);
898 AtomInsertConnectEntry(ap, ap->nconnects, iibuf[1 - k]);
904 } else if (strcmp(buf, "!:angles") == 0) {
905 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
910 /* a1 b1 c1 a2 b2 c2 a3 b3 c3 */
911 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]);
912 if (i == 0 || i % 3 != 0) {
913 snprintf(errbuf, errbufsize, "line %d: bad angle format", lineNumber);
916 for (j = 0; j < i; j += 3) {
918 iibuf[1] = ibuf[j + 1];
919 iibuf[2] = ibuf[j + 2];
920 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]) {
921 snprintf(errbuf, errbufsize, "line %d: bad angle format", lineNumber);
924 AssignArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, mp->nangles, iibuf);
928 } else if (strcmp(buf, "!:dihedrals") == 0) {
929 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
934 /* a1 b1 c1 d1 a2 b2 c2 d2 */
935 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]);
936 if (i == 0 || i % 4 != 0) {
937 snprintf(errbuf, errbufsize, "line %d: bad dihedral format", lineNumber);
940 for (j = 0; j < i; j += 4) {
942 iibuf[1] = ibuf[j + 1];
943 iibuf[2] = ibuf[j + 2];
944 iibuf[3] = ibuf[j + 3];
945 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]) {
946 snprintf(errbuf, errbufsize, "line %d: bad dihedral format", lineNumber);
949 AssignArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, mp->ndihedrals, iibuf);
953 } else if (strcmp(buf, "!:impropers") == 0) {
954 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
959 /* a1 b1 c1 d1 a2 b2 c2 d2 */
960 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]);
961 if (i == 0 || i % 4 != 0) {
962 snprintf(errbuf, errbufsize, "line %d: bad improper format", lineNumber);
965 for (j = 0; j < i; j += 4) {
967 iibuf[1] = ibuf[j + 1];
968 iibuf[2] = ibuf[j + 2];
969 iibuf[3] = ibuf[j + 3];
970 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]) {
971 snprintf(errbuf, errbufsize, "line %d: bad improper format", lineNumber);
974 AssignArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, mp->nimpropers, iibuf);
978 } else if (strcmp(buf, "!:xtalcell") == 0 && mp->cell == NULL) {
979 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
984 /* a b c alpha beta gamma [sigmaflag] */
985 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) {
986 snprintf(errbuf, errbufsize, "line %d: bad xtalcell format", lineNumber);
989 MoleculeSetCell(mp, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], 0);
990 if (j == 7 && ibuf[0] != 0) {
991 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
992 snprintf(errbuf, errbufsize, "line %d: sigma for xtalcell are missing", lineNumber);
995 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
996 snprintf(errbuf, errbufsize, "line %d: bad xtalcell sigma format", lineNumber);
999 if (mp->cell != NULL) {
1000 mp->cell->has_sigma = 1;
1001 for (i = 0; i < 6; i++) {
1002 mp->cell->cellsigma[i] = dbuf[i];
1005 snprintf(errbuf, errbufsize, "line %d: cell sigma are given while cell is not given", lineNumber);
1010 } else if (strcmp(buf, "!:symmetry_operations") == 0) {
1012 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1018 /* a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3 */
1019 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1020 snprintf(errbuf, errbufsize, "line %d: bad symmetry_operation format", lineNumber);
1025 tr[i + 3] = dbuf[1];
1026 tr[i + 6] = dbuf[2];
1034 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1039 } else if (strcmp(buf, "!:anisotropic_thermal_parameters") == 0) {
1041 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1046 /* b11 b22 b33 b12 b13 b23 [has_sigma] */
1047 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) {
1048 snprintf(errbuf, errbufsize, "line %d: anisotropic thermal parameters cannot be read for atom %d", lineNumber, i + 1);
1051 if (i >= mp->natoms) {
1052 snprintf(errbuf, errbufsize, "line %d: too many anisotropic thermal parameters\n", lineNumber);
1055 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) {
1058 MoleculeSetAniso(mp, i, 0, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], NULL);
1060 if (j == 7 && ibuf[0] != 0) {
1061 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1062 snprintf(errbuf, errbufsize, "line %d: anisotropic thermal parameters sigma missing", lineNumber);
1065 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1066 snprintf(errbuf, errbufsize, "line %d: anisotropic thermal parameters sigma cannot be read for atom %d", lineNumber, i + 1);
1069 ap = ATOM_AT_INDEX(mp->atoms, i);
1070 if (ap->aniso == NULL) {
1071 snprintf(errbuf, errbufsize, "line %d: anisotropic thermal parameters sigma are given while the parameters are not given", lineNumber);
1074 ap->aniso->has_bsig = 1;
1075 for (j = 0; j < 6; j++)
1076 ap->aniso->bsig[j] = dbuf[j];
1081 } else if (strcmp(buf, "!:periodic_box") == 0) {
1085 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1090 /* 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] */
1092 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1093 snprintf(errbuf, errbufsize, "line %d: bad periodic_box format", lineNumber);
1102 if ((j = sscanf(buf, "%d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3])) < 3) {
1103 snprintf(errbuf, errbufsize, "line %d: bad periodic_box format", lineNumber);
1106 if (j == 4 && ibuf[3] != 0)
1108 cbuf[0][0] = ibuf[0];
1109 cbuf[0][1] = ibuf[1];
1110 cbuf[0][2] = ibuf[2];
1111 MoleculeSetPeriodicBox(mp, vs, vs + 1, vs + 2, vs + 3, cbuf[0]);
1113 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1114 snprintf(errbuf, errbufsize, "line %d: sigma for cell parameters are missing", lineNumber);
1117 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1118 snprintf(errbuf, errbufsize, "line %d: bad periodic_box sigma format", lineNumber);
1121 if (mp->cell != NULL) {
1122 mp->cell->has_sigma = 1;
1123 for (i = 0; i < 6; i++) {
1124 mp->cell->cellsigma[i] = dbuf[i];
1127 snprintf(errbuf, errbufsize, "line %d: cell sigma are given while cell is not given", lineNumber);
1133 } else if (strcmp(buf, "!:frame_periodic_boxes") == 0) {
1136 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1141 if (sscanf(buf, "%lf %lf %f", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1142 snprintf(errbuf, errbufsize, "line %d: bad frame_periodic_box format", lineNumber);
1150 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells, vs);
1155 } else if (strcmp(buf, "!:md_parameters") == 0) {
1157 if (mp->arena == NULL)
1158 mp->arena = md_arena_new(NULL);
1160 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1166 comp = strsep(&bufp, " \t");
1168 while (*bufp == ' ' || *bufp == '\t')
1170 valp = strsep(&bufp, "\n");
1172 if (strcmp(comp, "alchem_flags") == 0) {
1173 j = (valp == NULL ? 0 : atoi(valp));
1175 valp = (char *)malloc(j);
1177 while ((k = fgetc(fp)) >= 0) {
1179 if (k < '0' || k > '9') {
1180 snprintf(errbuf, errbufsize, "line %d: too few flags in alchem_flags block", lineNumber + 1);
1184 ReadLine(buf, sizeof buf, fp, &lineNumber);
1186 while (*bufp != 0) {
1187 if (*bufp >= '0' && *bufp <= '2') {
1189 snprintf(errbuf, errbufsize, "line %d: too many flags in alchem_flags block", lineNumber);
1193 valp[i++] = *bufp - '0';
1194 } else if (*bufp != ' ' && *bufp != '\t' && *bufp != '\n') {
1195 snprintf(errbuf, errbufsize, "line %d: strange character (0x%02x) in alchem_flags block", lineNumber, (int)*bufp);
1204 md_set_alchemical_flags(arena, j, valp);
1209 /* In the following, the redundant "!= NULL" is to suppress suprious warning */
1210 if ((strcmp(comp, "log_file") == 0 && (pp = &arena->log_result_name) != NULL)
1211 || (strcmp(comp, "coord_file") == 0 && (pp = &arena->coord_result_name) != NULL)
1212 || (strcmp(comp, "vel_file") == 0 && (pp = &arena->vel_result_name) != NULL)
1213 || (strcmp(comp, "force_file") == 0 && (pp = &arena->force_result_name) != NULL)
1214 || (strcmp(comp, "debug_file") == 0 && (pp = &arena->debug_result_name) != NULL)) {
1215 if (*valp == 0 || strstr(valp, "(null)") == valp)
1218 valp = strdup(valp);
1220 char *valp1 = strchr(valp, '\n');
1226 } else if ((strcmp(comp, "debug_output_level") == 0 && (ip = &arena->debug_output_level) != NULL)
1227 || (strcmp(comp, "coord_output_freq") == 0 && (ip = &arena->coord_output_freq) != NULL)
1228 || (strcmp(comp, "energy_output_freq") == 0 && (ip = &arena->energy_output_freq) != NULL)
1229 || (strcmp(comp, "coord_frame") == 0 && (ip = &arena->coord_result_frame) != NULL)
1230 || (strcmp(comp, "andersen_freq") == 0 && (ip = &arena->andersen_thermo_freq) != NULL)
1231 || (strcmp(comp, "random_seed") == 0 && (ip = &arena->random_seed) != NULL)
1232 || (strcmp(comp, "use_xplor_shift") == 0 && (ip = &arena->use_xplor_shift) != NULL)
1233 || (strcmp(comp, "relocate_center") == 0 && (ip = &arena->relocate_center) != NULL)
1234 || (strcmp(comp, "surface_potential_freq") == 0 && (ip = &arena->surface_potential_freq) != NULL)
1235 || (strcmp(comp, "use_graphite") == 0 && (ip = &arena->use_graphite) != NULL)) {
1236 *ip = (valp == NULL ? 0 : atoi(valp));
1237 } else if ((strcmp(comp, "timestep") == 0 && (dp = &arena->timestep) != NULL)
1238 || (strcmp(comp, "cutoff") == 0 && (dp = &arena->cutoff) != NULL)
1239 || (strcmp(comp, "electro_cutoff") == 0 && (dp = &arena->electro_cutoff) != NULL)
1240 || (strcmp(comp, "pairlist_distance") == 0 && (dp = &arena->pairlist_distance) != NULL)
1241 || (strcmp(comp, "temperature") == 0 && (dp = &arena->temperature) != NULL)
1242 || (strcmp(comp, "andersen_coupling") == 0 && (dp = &arena->andersen_thermo_coupling) != NULL)
1243 || (strcmp(comp, "dielectric") == 0 && (dp = &arena->dielectric) != NULL)
1244 || (strcmp(comp, "gradient_convergence") == 0 && (dp = &arena->gradient_convergence) != NULL)
1245 || (strcmp(comp, "coordinate_convergence") == 0 && (dp = &arena->coordinate_convergence) != NULL)
1246 || (strcmp(comp, "scale14_vdw") == 0 && (dp = &arena->scale14_vdw) != NULL)
1247 || (strcmp(comp, "scale14_elect") == 0 && (dp = &arena->scale14_elect) != NULL)
1248 || (strcmp(comp, "surface_probe_radius") == 0 && (dp = &arena->probe_radius) != NULL)
1249 || (strcmp(comp, "surface_tension") == 0 && (dp = &arena->surface_tension) != NULL)
1250 || (strcmp(comp, "alchemical_lambda") == 0 && (dp = &arena->alchem_lambda) != NULL)
1251 || (strcmp(comp, "alchemical_delta_lambda") == 0 && (dp = &arena->alchem_dlambda) != NULL)) {
1252 *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1256 } else if (strcmp(buf, "!:pressure_control_parameters") == 0) {
1257 MDPressureArena *pressure;
1258 if (mp->arena == NULL)
1259 mp->arena = md_arena_new(mp);
1260 if (mp->arena->pressure == NULL)
1261 mp->arena->pressure = pressure_new();
1262 pressure = mp->arena->pressure;
1263 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1269 comp = strsep(&bufp, " \t");
1271 while (*bufp == ' ' || *bufp == '\t')
1273 valp = strsep(&bufp, "\n");
1275 if (strcmp(comp, "pressure") == 0) {
1276 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) {
1277 snprintf(errbuf, errbufsize, "line %d: bad format", lineNumber);
1280 for (i = 0; i < 9; i++)
1281 pressure->apply[i] = dbuf[i];
1282 } else if (strcmp(comp, "pressure_cell_flexibility") == 0) {
1283 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) {
1284 snprintf(errbuf, errbufsize, "line %d: bad format", lineNumber);
1287 for (i = 0; i < 8; i++)
1288 pressure->cell_flexibility[i] = dbuf[i];
1289 } else if ((strcmp(comp, "pressure_freq") == 0 && (ip = &pressure->freq) != NULL)) {
1290 *ip = (valp == NULL ? 0 : atoi(valp));
1291 } else if ((strcmp(comp, "pressure_coupling") == 0 && (dp = &pressure->coupling) != NULL)
1292 || (strcmp(comp, "pressure_fluctuate_cell_origin") == 0 && (dp = &pressure->fluctuate_cell_origin) != NULL)
1293 || (strcmp(comp, "pressure_fluctuate_cell_orientation") == 0 && (dp = &pressure->fluctuate_cell_orientation) != NULL)) {
1294 *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1298 } else if (strcmp(buf, "!:velocity") == 0) {
1300 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1306 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1307 snprintf(errbuf, errbufsize, "line %d: atom velocity cannot be read for atom %d", lineNumber, i + 1);
1310 if (i >= mp->natoms) {
1311 snprintf(errbuf, errbufsize, "line %d: too many atom velocity records\n", lineNumber);
1314 ap = ATOM_AT_INDEX(mp->atoms, i);
1321 } else if (strcmp(buf, "!:force") == 0) {
1323 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1329 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1330 snprintf(errbuf, errbufsize, "line %d: atom force cannot be read for atom %d", lineNumber, i + 1);
1333 if (i >= mp->natoms) {
1334 snprintf(errbuf, errbufsize, "line %d: too many atom force records\n", lineNumber);
1337 ap = ATOM_AT_INDEX(mp->atoms, i);
1344 } else if (strcmp(buf, "!:parameter") == 0 || strcmp(buf, "!:parameters") == 0) {
1345 Parameter *par = mp->par;
1347 mp->par = ParameterNew();
1352 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1357 j = ParameterReadFromString(par, buf, &bufp, fname, lineNumber, 0);
1359 snprintf(errbuf, errbufsize, "%s", bufp);
1365 MyAppCallback_setConsoleColor(1);
1366 MyAppCallback_showScriptMessage("%s", bufp);
1367 MyAppCallback_setConsoleColor(0);
1371 } else if (strcmp(buf, "!:trackball") == 0) {
1373 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1378 /* scale; trx try trz; theta_deg x y z */
1379 if ((i == 0 && sscanf(buf, "%f", &mview_fbuf[0]) < 1)
1380 || (i == 1 && sscanf(buf, "%f %f %f",
1381 &mview_fbuf[1], &mview_fbuf[2], &mview_fbuf[3]) < 3)
1382 || (i == 2 && sscanf(buf, "%f %f %f %f",
1383 &mview_fbuf[4], &mview_fbuf[5], &mview_fbuf[6], &mview_fbuf[7]) < 4)) {
1384 snprintf(errbuf, errbufsize, "line %d: bad trackball format", lineNumber);
1390 } else if (strcmp(buf, "!:view") == 0) {
1391 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1397 comp = strsep(&bufp, " \t");
1399 while (*bufp == ' ' || *bufp == '\t')
1401 valp = strsep(&bufp, "\n");
1403 /* In the following, the redundant "!= NULL" is to suppress suprious warning */
1404 if ((strcmp(comp, "show_unit_cell") == 0 && (i = 1))
1405 || (strcmp(comp, "show_periodic_box") == 0 && (i = 2))
1406 || (strcmp(comp, "show_expanded_atoms") == 0 && (i = 3))
1407 || (strcmp(comp, "show_ellipsoids") == 0 && (i = 4))
1408 || (strcmp(comp, "show_hydrogens") == 0 && (i = 5))
1409 || (strcmp(comp, "show_dummy_atoms") == 0 && (i = 6))
1410 || (strcmp(comp, "show_rotation_center") == 0 && (i = 7))
1411 || (strcmp(comp, "show_graphite_flag") == 0 && (i = 8))
1412 || (strcmp(comp, "show_periodic_image_flag") == 0 && (i = 9))
1413 || (strcmp(comp, "show_graphite") == 0 && (i = 10))) {
1414 mview_ibuf[i - 1] = atoi(valp);
1415 } else if (strcmp(comp, "show_periodic_image") == 0) {
1416 sscanf(valp, "%d %d %d %d %d %d",
1417 &mview_ibuf[10], &mview_ibuf[11], &mview_ibuf[12],
1418 &mview_ibuf[13], &mview_ibuf[14], &mview_ibuf[15]);
1423 /* Unknown sections are silently ignored */
1426 MoleculeCleanUpResidueTable(mp);
1427 if (mp->arena != NULL)
1428 md_arena_set_molecule(mp->arena, mp);
1432 if (errbuf[0] != 0) {
1433 /* The content of mp may be broken, so make it empty */
1437 MainView *mview = mp->mview;
1438 if (mview != NULL) {
1439 if (mview_ibuf[0] != kUndefined)
1440 mview->showUnitCell = mview_ibuf[0];
1441 if (mview_ibuf[1] != kUndefined)
1442 mview->showPeriodicBox = mview_ibuf[1];
1443 if (mview_ibuf[2] != kUndefined)
1444 mview->showExpandedAtoms = mview_ibuf[2];
1445 if (mview_ibuf[3] != kUndefined)
1446 mview->showEllipsoids = mview_ibuf[3];
1447 if (mview_ibuf[4] != kUndefined)
1448 mview->showHydrogens = mview_ibuf[4];
1449 if (mview_ibuf[5] != kUndefined)
1450 mview->showDummyAtoms = mview_ibuf[5];
1451 if (mview_ibuf[6] != kUndefined)
1452 mview->showRotationCenter = mview_ibuf[6];
1453 if (mview_ibuf[7] != kUndefined)
1454 mview->showGraphiteFlag = mview_ibuf[7];
1455 if (mview_ibuf[8] != kUndefined)
1456 mview->showPeriodicImageFlag = mview_ibuf[8];
1457 if (mview_ibuf[9] != kUndefined)
1458 mview->showGraphite = mview_ibuf[9];
1459 for (i = 0; i < 6; i++) {
1460 if (mview_ibuf[10 + i] != kUndefined)
1461 mview->showPeriodicImage[i] = mview_ibuf[10 + i];
1463 if (mview->track != NULL) {
1464 if (mview_fbuf[0] != kUndefined)
1465 TrackballSetScale(mview->track, mview_fbuf[0]);
1466 if (mview_fbuf[1] != kUndefined)
1467 TrackballSetTranslate(mview->track, mview_fbuf + 1);
1468 if (mview_fbuf[4] != kUndefined)
1469 TrackballSetRotate(mview->track, mview_fbuf + 4);
1477 MoleculeLoadPsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
1486 Vector *frames = NULL;
1489 if (errbuf == NULL) {
1496 fp = fopen(fname, "rb");
1498 snprintf(errbuf, errbufsize, "Cannot open file");
1501 /* flockfile(fp); */
1504 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1505 if (strncmp(buf, "PSF", 3) == 0) {
1509 for (p = buf; *p != 0 && isspace(*p); p++) {}
1515 if (strstr(buf, "!COORD") != NULL) {
1516 /* Extended psf file with coordinates */
1518 /* Allocate a temporary storage for frames */
1519 size_t size = sizeof(Vector) * mp->natoms * fn;
1521 frames = (Vector *)malloc(size);
1523 frames = (Vector *)realloc(frames, size);
1528 /* Copy the coordinates of the first frame */
1529 for (i = 0; i < mp->natoms; i++) {
1530 ap = ATOM_AT_INDEX(mp->atoms, i);
1534 /* Copy the coordinates of the last frame to the newly created frame */
1535 memmove(frames + sizeof(Vector) * mp->natoms * fn, frames + sizeof(Vector) * mp->natoms * (fn - 1), sizeof(Vector) * mp->natoms);
1538 /* Read coordinates */
1539 for (i = 0; i < mp->natoms; i++) {
1542 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1544 snprintf(errbuf, errbufsize, "line %d: premature end of file while reading coordinates (frame %d)", lineNumber, fn);
1547 if (sscanf(buf, "%lg %lg %lg", dval, dval + 1, dval + 2) != 3) {
1549 snprintf(errbuf, errbufsize, "line %d: coordinates cannot be read for atom %d", lineNumber, i + 1);
1556 ATOM_AT_INDEX(mp->atoms, i)->r = r;
1558 frames[mp->natoms * (fn - 1) + i] = r;
1567 ReadFormat(buf, "I8", &natoms);
1568 if (mp->atoms != NULL)
1570 if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
1573 for (i = 0; i < natoms; i++) {
1575 char segName[5], resName[4], atomName[5], atomType[3], element[3];
1578 memset(&w, 0, sizeof(w));
1579 ap = ATOM_AT_INDEX(mp->atoms, i);
1580 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1582 snprintf(errbuf, errbufsize, "line %d: premature end of file while reading atoms", lineNumber);
1585 ReadFormat(buf, "I8 x1 S4 I5 x1 S3 x2 S4 x1 S4 F16 F10",
1586 &w.serial, w.segName, &ap->resSeq, w.resName, w.atomName,
1587 w.atomType, &ap->charge, &ap->weight);
1588 strncpy(ap->segName, w.segName, 4);
1589 strncpy(ap->resName, w.resName, 3);
1590 strncpy(ap->aname, w.atomName, 4);
1591 ap->type = AtomTypeEncodeToUInt(w.atomType);
1592 /* $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl */
1593 ap->atomicNumber = GuessAtomicNumber(w.atomName, ap->weight);
1594 ElementToString(ap->atomicNumber, w.element);
1595 strncpy(ap->element, w.element, 2);
1596 /* w.element[0] = 0;
1597 for (p = w.atomName; *p != 0; p++) {
1598 if (isalpha(*p) && *p != '_') {
1599 w.element[0] = toupper(*p);
1600 if (isalpha(p[1]) && p[1] != '_') {
1601 w.element[1] = toupper(p[1]);
1609 strncpy(ap->element, w.element, 2);
1610 ap->atomicNumber = ElementToInt(w.element); */
1611 if (w.resName[0] == 0)
1612 strncpy(ap->resName, "XXX", 3);
1613 if (ap->resSeq > mp->nresidues)
1614 mp->nresidues = ap->resSeq;
1616 if (mp->residues != NULL)
1618 if (NewArray(&mp->residues, &mp->nresidues, sizeof(char (*)[4]), mp->nresidues + 1) == 0)
1620 for (i = 0; i < mp->natoms; i++) {
1621 j = mp->atoms[i].resSeq;
1622 if (mp->residues[j][0] == 0)
1623 strncpy(mp->residues[j], mp->atoms[i].resName, 4);
1626 } else if (section == 3) {
1630 ReadFormat(buf, "I8", &nbonds);
1631 if (mp->bonds != NULL)
1633 if (NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, nbonds) == NULL)
1636 for (i = 0; i < nbonds; i += 4) {
1637 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1638 snprintf(errbuf, errbufsize, "line %d: premature end of file while reading bonds", lineNumber);
1642 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1643 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1644 for (j = 0; j < 4 && i + j < nbonds; j++) {
1647 b1 = ibuf[j * 2] - 1; /* Internal atom number is 0-based */
1648 b2 = ibuf[j * 2 + 1] - 1;
1649 if (b1 < 0 || b1 >= mp->natoms || b2 < 0 || b2 >= mp->natoms) {
1650 snprintf(errbuf, errbufsize, "line %d: The bond %d-%d includes non-existent atom", lineNumber, b1+1, b2+1);
1656 ap = ATOM_AT_INDEX(mp->atoms, b1);
1657 AtomInsertConnectEntry(ap, ap->nconnects, b2);
1658 /* if (ap->nconnects < ATOMS_MAX_CONNECTS)
1659 ap->connects[ap->nconnects++] = b2;
1661 snprintf(errbuf, errbufsize, "line %d: The atom %d has more than %d bonds", lineNumber, b1+1, ATOMS_MAX_CONNECTS);
1665 ap = ATOM_AT_INDEX(mp->atoms, b2);
1666 AtomInsertConnectEntry(ap, ap->nconnects, b1);
1667 /* if (ap->nconnects < ATOMS_MAX_CONNECTS)
1668 ap->connects[ap->nconnects++] = b1;
1670 snprintf(errbuf, errbufsize, "line %d: The atom %d has more than %d bonds", lineNumber, b2+1, ATOMS_MAX_CONNECTS);
1677 } else if (section == 4) {
1681 ReadFormat(buf, "I8", &nangles);
1682 if (mp->angles != NULL)
1684 if (NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, nangles) == NULL)
1687 for (i = 0; i < nangles; i += 3) {
1688 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1689 snprintf(errbuf, errbufsize, "line %d: premature end of file while reading angles", lineNumber);
1693 ReadFormat(buf, "I8I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1694 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7, ibuf + 8);
1695 for (j = 0; j < 3 && i + j < nangles; j++) {
1697 a1 = ibuf[j * 3] - 1; /* Internal atom number is 0-based */
1698 a2 = ibuf[j * 3 + 1] - 1;
1699 a3 = ibuf[j * 3 + 2] - 1;
1700 if (a1 < 0 || a1 >= mp->natoms || a2 < 0 || a2 >= mp->natoms || a3 < 0 || a3 >= mp->natoms) {
1701 snprintf(errbuf, errbufsize, "line %d: The angle %d-%d-%d includes non-existent atom", lineNumber, a1+1, a2+1, a3+1);
1711 } else if (section == 5 || section == 6) {
1712 /* Dihedrals and Impropers */
1715 ReadFormat(buf, "I8", &ndihedrals);
1717 if (NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, ndihedrals) == NULL)
1721 if (NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, ndihedrals) == NULL)
1725 for (i = 0; i < ndihedrals; i += 2) {
1726 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1728 snprintf(errbuf, errbufsize, "line %d: premature end of file while reading %s", lineNumber, (section == 5 ? "dihedral" : "improper"));
1732 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3, ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1733 for (j = 0; j < 2 && i + j < ndihedrals; j++) {
1735 d1 = ibuf[j * 4] - 1; /* Internal atom number is 0-based */
1736 d2 = ibuf[j * 4 + 1] - 1;
1737 d3 = ibuf[j * 4 + 2] - 1;
1738 d4 = ibuf[j * 4 + 3] - 1;
1739 if (d1 < 0 || d1 >= mp->natoms || d2 < 0 || d2 >= mp->natoms || d3 < 0 || d3 >= mp->natoms || d4 < 0 || d4 >= mp->natoms) {
1740 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);
1754 /* Create frames for each atom if necessary */
1756 for (i = 0; i < mp->natoms; i++) {
1757 ap = ATOM_AT_INDEX(mp->atoms, i);
1758 ap->frames = (Vector *)malloc(sizeof(Vector) * fn);
1759 if (ap->frames == NULL)
1762 for (j = 0; j < fn; j++)
1763 ap->frames[j] = frames[mp->natoms * j + i];
1770 /* funlockfile(fp); */
1772 mp->nframes = -1; /* Should be recalculated later */
1775 else if (section == -1)
1779 Panic("low memory while reading structure file %s", fname);
1780 return 1; /* not reached */
1783 /* ("-x", "y", "-z+0.5") -> (-1,0,0,0,0,1,0,0,0,0,-1,0.5) */
1785 sMoleculeSymopStringsToTransform(char **symops, Transform tr)
1789 memset(tr, 0, sizeof(Transform));
1790 for (i = 0; i < 3; i++) {
1794 while (*symop != 0) {
1796 while (isspace(*symop))
1798 if (*symop == 0 || *symop == '\r' || *symop == 'n')
1800 if (*symop == '-') {
1803 } else if (*symop == '+') {
1807 while (isspace(*symop))
1809 if (*symop == '.' || isdigit(*symop)) {
1810 /* Numerical offset */
1811 double d = strtod(symop, &symop);
1812 if (*symop == '/') {
1813 double dd = strtod(symop + 1, &symop);
1817 return 1; /* Bad format */
1820 } else if (*symop == 'x' || *symop == 'X') {
1823 } else if (*symop == 'y' || *symop == 'Y') {
1826 } else if (*symop == 'z' || *symop == 'Z') {
1829 } else return 1; /* Bad format */
1830 } /* end while (*symop != 0) */
1836 sMoleculeGenerateSymopWithTransform(Molecule *mp, Transform gtr, int num)
1842 for (i = 0; i < num; i++) {
1843 memmove(tr, mp->syms[i], sizeof(Transform));
1844 TransformMul(tr, gtr, tr);
1845 for (j = 9; j < 12; j++) {
1848 else if (tr[j] <= 0.0)
1851 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1858 char *p = buf + strlen(buf) - 1;
1859 if (p >= buf && (*p == '\n' || *p == '\r')) {
1861 if (--p >= buf && (*p == '\n' || *p == '\r')) {
1869 MoleculeLoadTepFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
1879 if (errbuf == NULL) {
1886 fp = fopen(fname, "rb");
1888 snprintf(errbuf, errbufsize, "Cannot open file");
1892 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1893 if (section == -1) {
1900 ReadFormat(buf, "I1F8F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5);
1902 MoleculeSetCell(mp, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], 0);
1909 if (cellType == 0) {
1910 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);
1924 char *symops[3], *brks;
1926 memset(tr, 0, sizeof(Transform));
1927 ReadFormat(buf, "I1", ibuf);
1928 symops[0] = strtok_r(buf + 1, ", ", &brks);
1929 symops[1] = strtok_r(NULL, ", ", &brks);
1930 symops[2] = strtok_r(NULL, ", ", &brks);
1931 if (sMoleculeSymopStringsToTransform(symops, tr)) {
1932 snprintf(errbuf, errbufsize, "line %d: bad symmetry specification", lineNumber);
1936 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
1942 if (section == 2) { /* Atoms */
1946 int atomIndex = mp->natoms;
1947 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
1948 memset(ap, 0, gSizeOfAtomRecord);
1949 ReadFormat(buf, "S6x3x9x9F9F9F9F9", name, fbuf, fbuf+1, fbuf+2, fbuf+3);
1950 strncpy(ap->aname, name, 4);
1954 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
1955 /* ap->atomicNumber = AtomNameToElement(ap->name);
1956 ElementToString(ap->atomicNumber, ap->element); */
1957 /* sAtomSetElement(ap, -1, ap->name); */
1960 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1961 snprintf(errbuf, errbufsize, "unexpected end of file");
1964 ReadFormat(buf, "I1F8F9F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
1966 if ((atomType >= 0 && atomType <= 5) || (atomType >= 8 && atomType <= 10)) {
1967 /* Anisotropic thermal parameters */
1968 MoleculeSetAniso(mp, atomIndex, atomType, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[5], fbuf[4], NULL);
1976 MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
1978 MoleculeAddBonds(mp, nbonds, bonds);
1981 mp->nframes = -1; /* Should be recalculated later */
1984 Panic("low memory while reading structure file %s", fname);
1985 return -1; /* not reached */
1989 MoleculeLoadShelxFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
1997 int currentResSeq = 0;
1998 char currentResName[6];
2005 char (*sfacs)[4] = NULL;
2007 if (errbuf == NULL) {
2014 currentResName[0] = 0;
2015 fp = fopen(fname, "rb");
2017 snprintf(errbuf, errbufsize, "Cannot open file");
2021 tr[0] = tr[4] = tr[8] = 1;
2022 tr[1] = tr[2] = tr[3] = tr[5] = tr[6] = tr[7] = tr[9] = tr[10] = tr[11] = 0;
2023 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), 0, tr) == 0)
2025 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2026 if (strncmp(buf, "CELL", 4) == 0) {
2028 sscanf(buf + 4, " %f %f %f %f %f %f %f", fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2029 MoleculeSetCell(mp, fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], fbuf[6], 0);
2031 } else if (strncmp(buf, "SFAC", 4) == 0) {
2033 for (p1 = strtok_r(buf + 4, " ", &p2); p1 != NULL; p1 = strtok_r(NULL, " ", &p2)) {
2034 char *pp = (char *)AssignArray(&sfacs, &nsfacs, 4, nsfacs, NULL);
2041 } else if (strncmp(buf, "LATT", 4) == 0) {
2042 sscanf(buf + 4, " %d", &latticeType);
2044 } else if (strncmp(buf, "SYMM", 4) == 0) {
2045 char *symops[3], *brks;
2046 memset(tr, 0, sizeof(Transform));
2047 // ReadFormat(buf + 4, "I1", ibuf);
2049 symops[0] = strtok_r(buf + 4, ",", &brks);
2050 symops[1] = strtok_r(NULL, ",", &brks);
2051 symops[2] = strtok_r(NULL, ",", &brks);
2052 if (sMoleculeSymopStringsToTransform(symops, tr)) {
2053 snprintf(errbuf, errbufsize, "line %d: bad symmetry specification", lineNumber);
2056 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2059 } else if (strncmp(buf, "RESI", 4) == 0) {
2060 for (p1 = buf + 4; isspace(*p1); p1++);
2062 for (p2 = p1 + 1; isalnum(*p2); p2++);
2064 strncpy(currentResName, p1, 4);
2065 currentResName[4] = 0;
2067 } else currentResName[0] = 0;
2068 sscanf(buf + 4, " %d", ¤tResSeq);
2071 /* Atom name: [A-Za-z]{1,2}[0-9]* */
2072 for (p1 = buf; p1 < buf + 2 && (isalpha(*p1) && *p1 != '_'); p1++);
2074 while (isdigit(*p1))
2077 if (p1 > buf && p1 <= buf + 4 && isspace(*p1)) {
2081 int atomIndex = mp->natoms;
2082 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2083 memset(ap, 0, gSizeOfAtomRecord);
2084 strncpy(ap->aname, buf, 4);
2085 n = sscanf(p1, " %d %f %f %f %f %f %f %2s", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, cont);
2086 if (n == 8 && strcmp(cont, "=") == 0) {
2087 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2088 snprintf(errbuf, errbufsize, "line %d: unexpected end of file within the atom cards", lineNumber);
2091 sscanf(buf, " %f %f %f %f", fbuf+6, fbuf+7, fbuf+8, fbuf+9);
2093 } else n = 5; /* Iso */
2097 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2098 ap->occupancy = fbuf[3];
2099 if (ap->aname[0] != 'Q' && ibuf[0] >= 1 && ibuf[0] <= nsfacs) {
2100 strncpy(ap->element, sfacs[ibuf[0] - 1], 2);
2102 /* sAtomSetElement(ap, -1, sfacs[ibuf[0] - 1]); */
2103 /* strncpy(ap->element, sfacs[ibuf[0] - 1], 4);
2104 ap->atomicNumber = ElementToInt(ap->element); */
2106 sAtomSetElement(ap, -1, ap->name); */
2107 /* ap->atomicNumber = AtomNameToElement(ap->name);
2108 ElementToString(ap->atomicNumber, ap->element); */
2111 if (n == 10 || fbuf[4] >= 0.0) {
2113 /* Read in the standard deviations */
2114 ReadLine(buf, sizeof buf, fp, &lineNumber);
2115 for (i = 0; i < 9; i++) {
2119 dbuf[i] = strtod(buf + j, NULL);
2122 ap->sigma.x = dbuf[0];
2123 ap->sigma.y = dbuf[1];
2124 ap->sigma.z = dbuf[2];
2127 ap->tempFactor = fbuf[4] * 78.9568352087147; /* 8*pi*pi */
2129 MoleculeSetAniso(mp, atomIndex, 8, fbuf[4], fbuf[5], fbuf[6], fbuf[9], fbuf[7], fbuf[8], dbuf);
2130 ap->resSeq = currentResSeq;
2131 strncpy(ap->resName, currentResName, 4);
2138 /* Add symmetry operations according to the lattice type */
2139 switch (latticeType < 0 ? -latticeType : latticeType) {
2140 static Transform tr_i = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0.5};
2141 static Transform tr_c = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0};
2142 static Transform tr_a = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.5};
2143 static Transform tr_b = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5};
2144 static Transform tr_r1 = {0, 1, 0, -1, -1, 0, 0, 0, 1, 0, 0, 0};
2145 static Transform tr_r2 = {-1, -1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0};
2149 sMoleculeGenerateSymopWithTransform(mp, tr_i, 0);
2151 case 3: /* Rhombohedral obverse on hexagonal axes */
2153 sMoleculeGenerateSymopWithTransform(mp, tr_r1, n);
2154 sMoleculeGenerateSymopWithTransform(mp, tr_r2, n);
2158 sMoleculeGenerateSymopWithTransform(mp, tr_a, n);
2159 sMoleculeGenerateSymopWithTransform(mp, tr_b, n);
2160 sMoleculeGenerateSymopWithTransform(mp, tr_c, n);
2163 sMoleculeGenerateSymopWithTransform(mp, tr_a, 0);
2166 sMoleculeGenerateSymopWithTransform(mp, tr_b, 0);
2169 sMoleculeGenerateSymopWithTransform(mp, tr_c, 0);
2173 if (latticeType > 0) {
2174 static Transform tr_inv = {-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0};
2175 sMoleculeGenerateSymopWithTransform(mp, tr_inv, 0);
2178 MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
2180 MoleculeAddBonds(mp, nbonds, bonds);
2183 mp->nframes = -1; /* Should be recalculated later */
2186 Panic("low memory while reading structure file %s", fname);
2187 return -1; /* not reached */
2190 /* Add one gaussian orbital shell information (not undoable) */
2192 MoleculeAddGaussianOrbitalShell(Molecule *mol, Int sym, Int nprims, Int a_idx)
2197 return -1; /* Molecule is empty */
2200 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2202 return -2; /* Low memory */
2204 shellp = AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), bset->nshells, NULL);
2206 return -2; /* Low memory */
2208 case 0: shellp->sym = kGTOType_S; shellp->ncomp = 1; break;
2209 case 1: shellp->sym = kGTOType_P; shellp->ncomp = 3; break;
2210 case -1: shellp->sym = kGTOType_SP; shellp->ncomp = 4; break;
2211 case 2: shellp->sym = kGTOType_D; shellp->ncomp = 6; break;
2212 case -2: shellp->sym = kGTOType_D5; shellp->ncomp = 5; break;
2213 /* TODO: Support F/F7 type orbitals */
2214 /* case 3: sp->sym = kGTOtype_F; sp->ncomp = 10; break;
2215 case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break; */
2217 return -3; /* Unsupported shell type */
2219 shellp->nprim = nprims;
2220 shellp->a_idx = a_idx;
2221 if (bset->shells < shellp) {
2222 shellp->m_idx = shellp[-1].m_idx + shellp[-1].ncomp;
2223 shellp->p_idx = shellp[-1].p_idx + shellp[-1].nprim;
2231 /* Add a set of gaussian primitive coefficients (not undoable) */
2233 MoleculeAddGaussianPrimitiveCoefficients(Molecule *mol, Double exponent, Double contraction, Double contraction_sp)
2238 return -1; /* Molecule is empty */
2241 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2243 return -2; /* Low memory */
2245 primp = AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), bset->npriminfos, NULL);
2247 return -2; /* Low memory */
2248 primp->A = exponent;
2249 primp->C = contraction;
2250 primp->Csp = contraction_sp;
2254 /* Set MO coefficients for idx-th MO */
2256 MoleculeSetMOCoefficients(Molecule *mol, Int idx, Double energy, Int ncomps, Double *coeffs)
2261 return -1; /* Molecule is empty */
2264 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2266 return -2; /* Low memory */
2268 if (bset->nmos == 0) {
2269 if (bset->nshells > 0) {
2270 /* Shell info is already set: calculate the number of MOs from there */
2271 for (i = n = 0; i < bset->nshells; i++)
2272 n += bset->shells[i].ncomp;
2274 } else if (ncomps > 0) {
2275 bset->ncomps = ncomps;
2277 if (bset->rflag == 0)
2278 bset->nmos = bset->ncomps * 2;
2280 bset->nmos = bset->ncomps;
2281 if (bset->nmos <= 0)
2282 return -3; /* Bad or inconsistent number of MOs */
2283 bset->mo = (Double *)calloc(sizeof(Double), bset->nmos * bset->ncomps);
2284 bset->moenergies = (Double *)calloc(sizeof(Double), bset->nmos);
2285 if (bset->mo == NULL || bset->moenergies == NULL) {
2286 if (bset->mo != NULL)
2288 if (bset->moenergies != NULL)
2289 free(bset->moenergies);
2291 bset->moenergies = NULL;
2293 return -2; /* Low memory */
2296 if (idx < 0 || idx >= bset->nmos)
2297 return -4; /* Bad MO index */
2298 if (energy != -1000000)
2299 bset->moenergies[idx] = energy;
2300 if (ncomps < bset->ncomps)
2301 return -5; /* Insufficient number of data provided */
2302 memmove(bset->mo + (idx * bset->ncomps), coeffs, sizeof(Double) * bset->ncomps);
2303 if (bset->cns != NULL) {
2304 /* Clear the cached values */
2312 /* Allocate BasisSet record. rflag: UHF, 0; RHF, 1; ROHF, 2
2313 ne_alpha: number of alpha electrons, ne_beta: number of beta electrons
2314 The natoms and pos are copied from mol. */
2316 MoleculeAllocateBasisSetRecord(Molecule *mol, Int rflag, Int ne_alpha, Int ne_beta)
2321 if (mol == NULL || mol->natoms == 0)
2322 return -1; /* Molecule is empty */
2325 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2327 return -2; /* Low memory */
2329 if (bset->pos != NULL) {
2333 bset->natoms = mol->natoms;
2334 bset->pos = (Vector *)calloc(sizeof(Vector), bset->natoms);
2335 if (bset->pos == NULL)
2336 return -2; /* Low memory */
2337 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
2338 bset->pos[i].x = ap->r.x * kAngstrom2Bohr;
2339 bset->pos[i].y = ap->r.y * kAngstrom2Bohr;
2340 bset->pos[i].z = ap->r.z * kAngstrom2Bohr;
2342 bset->ne_alpha = ne_alpha;
2343 bset->ne_beta = ne_beta;
2344 bset->rflag = rflag;
2349 sSeparateTokens(char *inString, char **outPtr, int size)
2353 for (i = 0; i < size; i++) {
2354 p = strtok((i == 0 ? inString : NULL), " \r\n");
2365 sReadNumberArray(void *basep, Int *countp, Int size, Int num, FILE *fp, int *lnp)
2369 *((void **)basep) = NULL;
2371 if (AssignArray(basep, countp, size, num - 1, NULL) == NULL)
2372 return 4; /* Out of memory */
2374 while (ReadLine(buf, sizeof buf, fp, lnp) > 0) {
2375 char *tokens[16], *p;
2376 sSeparateTokens(buf, tokens, 16);
2377 for (i = 0; i < 16; i++) {
2378 if (tokens[i] == NULL)
2380 if (size == sizeof(Int)) {
2381 (*((Int **)basep))[n] = strtol(tokens[i], &p, 0);
2382 } else if (size == sizeof(Double)) {
2383 (*((Double **)basep))[n] = strtod(tokens[i], &p);
2384 } else return -1; /* Internal error */
2385 if (tokens[i] == p || *p != 0)
2386 return 1; /* Non-digit character */
2388 if (i < 15 && tokens[i + 1] != NULL)
2389 return 2; /* Too many data */
2390 return 0; /* All data are successfully read */
2394 return 3; /* Unexpected EOF */
2398 sSetupGaussianCoefficients(BasisSet *bset)
2405 /* Cache the contraction coefficients for efficient calculation */
2406 /* Sum up the number of components for all primitives */
2407 for (i = k = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2409 k += sp->nprim * sp->ncomp;
2411 /* Allocate memory for the cached values */
2412 if (AssignArray(&bset->cns, &bset->ncns, sizeof(Double), k - 1, NULL) == NULL)
2414 /* Iterate over all primitives */
2416 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2417 for (j = 0, pp = bset->priminfos + sp->p_idx; j < sp->nprim; j++, pp++) {
2420 // (8 alpha^3/pi^3)^0.25 exp(-alpha r^2)
2421 *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2424 // (128 alpha^5/pi^3)^0.25 [x|y|z]exp(-alpha r^2)
2425 d = pp->C * pow(pp->A, 1.25) * 1.425410941;
2431 *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2432 d = pp->Csp * pow(pp->A, 1.25) * 1.425410941;
2438 // xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2)
2439 // xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2440 d = pp->C * pow(pp->A, 1.75);
2441 dp[0] = dp[1] = dp[2] = d * 1.645922781;
2442 dp[3] = dp[4] = dp[5] = d * 2.850821881;
2446 // 3zz-rr: (128 alpha^7/9pi^3)^0.25 (3zz-rr)exp(-alpha r^2)
2447 // xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2448 // xx-yy: (128 alpha^7/pi^3)^0.25 (xx-yy)exp(-alpha r^2)
2449 d = pp->C * pow(pp->A, 1.75);
2450 dp[0] = d * 0.822961390;
2451 dp[1] = dp[2] = dp[4] = d * 2.850821881;
2452 dp[3] = d * 1.425410941;
2462 MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
2467 int natoms, nbasis, i, j, k, n, mxbond, retval, ncomps, nprims, nelec;
2478 if (errbuf == NULL) {
2485 bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2489 fp = fopen(fname, "rb");
2491 snprintf(errbuf, errbufsize, "Cannot open file");
2495 natoms = nbasis = -1;
2503 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2506 if (lineNumber == 2) {
2509 bset->rflag = 0; /* UHF */
2510 else if (buf[11] == 'O')
2511 bset->rflag = 2; /* ROHF */
2512 else bset->rflag = 1; /* RHF */
2515 while (p > buf && *p == ' ')
2518 sSeparateTokens(buf + 42, tokens, 16);
2519 if (strcmp(buf, "Number of atoms") == 0) {
2520 if (tokens[1] == NULL || (natoms = atoi(tokens[1])) <= 0) {
2521 snprintf(errbuf, errbufsize, "Line %d: strange number of atoms: %s", lineNumber, tokens[1]);
2525 /* Allocate atom records (all are empty for now) */
2526 AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms - 1, NULL);
2527 /* Also allocate atom position array for MO calculations */
2528 AssignArray(&bset->pos, &bset->natoms, sizeof(Vector), natoms - 1, NULL);
2529 /* Also allocate nuclear charge array */
2530 bset->nuccharges = (Double *)calloc(sizeof(Double), natoms);
2531 } else if (strcmp(buf, "Number of electrons") == 0) {
2532 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2533 snprintf(errbuf, errbufsize, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
2538 } else if (strcmp(buf, "Number of alpha electrons") == 0) {
2539 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2540 snprintf(errbuf, errbufsize, "Line %d: strange number of alpha electrons: %s", lineNumber, tokens[1]);
2545 } else if (strcmp(buf, "Number of beta electrons") == 0) {
2546 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2547 snprintf(errbuf, errbufsize, "Line %d: strange number of beta electrons: %s", lineNumber, tokens[1]);
2552 if (bset->ne_alpha + bset->ne_beta != nelec) {
2553 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);
2557 } else if (strcmp(buf, "Number of basis functions") == 0) {
2558 if (tokens[1] == NULL || (nbasis = atoi(tokens[1])) <= 0) {
2559 snprintf(errbuf, errbufsize, "Line %d: strange number of basis functions: %s", lineNumber, tokens[1]);
2563 } else if (strcmp(buf, "Atomic numbers") == 0) {
2564 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2565 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2569 if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms, fp, &lineNumber) != 0) {
2570 snprintf(errbuf, errbufsize, "Line %d: cannot read atomic numbers", lineNumber);
2574 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2575 ap->atomicNumber = iary[i];
2576 bset->nuccharges[i] = iary[i];
2577 ElementToString(ap->atomicNumber, ap->element);
2578 memmove(ap->aname, ap->element, 4);
2579 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
2584 } else if (strcmp(buf, "Nuclear charges") == 0) {
2585 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2586 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2590 if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms, fp, &lineNumber) != 0) {
2591 snprintf(errbuf, errbufsize, "Line %d: cannot read nuclear charges", lineNumber);
2595 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2596 bset->nuccharges[i] = dary[i];
2600 } else if (strcmp(buf, "Current cartesian coordinates") == 0) {
2601 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * 3) {
2602 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of cartesian coordinates: %s", lineNumber, tokens[2]);
2606 if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms * 3, fp, &lineNumber) != 0) {
2607 snprintf(errbuf, errbufsize, "Line %d: cannot read cartesian coordinates", lineNumber);
2611 for (i = 0, ap = mp->atoms, vp = bset->pos; i < natoms; i++, ap = ATOM_NEXT(ap), vp++) {
2612 vp->x = dary[i * 3];
2613 vp->y = dary[i * 3 + 1];
2614 vp->z = dary[i * 3 + 2];
2615 ap->r.x = vp->x * kBohr2Angstrom;
2616 ap->r.y = vp->y * kBohr2Angstrom;
2617 ap->r.z = vp->z * kBohr2Angstrom;
2621 } else if (strcmp(buf, "MxBond") == 0) {
2622 if (tokens[1] == NULL || (mxbond = atoi(tokens[1])) <= 0) {
2623 snprintf(errbuf, errbufsize, "Line %d: strange number of bonds per atom: %s", lineNumber, tokens[1]);
2627 } else if (strcmp(buf, "IBond") == 0) {
2629 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * mxbond) {
2630 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of bonds: %s", lineNumber, tokens[2]);
2634 if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms * mxbond, fp, &lineNumber) != 0) {
2635 snprintf(errbuf, errbufsize, "Line %d: cannot read bond information", lineNumber);
2639 bonds = (Int *)malloc(sizeof(Int) * (mxbond * 2 + 1));
2640 for (i = 0; i < natoms; i++) {
2641 for (j = k = 0; j < mxbond; j++) {
2642 n = iary[i * mxbond + j] - 1;
2644 /* Connect atom i and atom n */
2650 bonds[k] = kInvalidIndex;
2651 MoleculeAddBonds(mp, k / 2, bonds);
2657 } else if (strcmp(buf, "Shell types") == 0) {
2658 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0) {
2659 snprintf(errbuf, errbufsize, "Line %d: wrong number of shell types: %s", lineNumber, tokens[2]);
2663 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2664 snprintf(errbuf, errbufsize, "Line %d: cannot read shell types", lineNumber);
2668 /* Allocate ShellInfo table and store shell type information */
2669 AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), nary - 1, NULL);
2670 for (i = n = 0, sp = bset->shells; i < nary; i++, sp++) {
2672 case 0: sp->sym = kGTOType_S; sp->ncomp = 1; break;
2673 case 1: sp->sym = kGTOType_P; sp->ncomp = 3; break;
2674 case -1: sp->sym = kGTOType_SP; sp->ncomp = 4; break;
2675 case 2: sp->sym = kGTOType_D; sp->ncomp = 6; break;
2676 case -2: sp->sym = kGTOType_D5; sp->ncomp = 5; break;
2677 /* TODO: Support F/F7 type orbitals */
2678 /* case 3: sp->sym = kGTOtype_F; sp->ncomp = 10; break;
2679 case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break; */
2681 snprintf(errbuf, errbufsize, "Line %d: unsupported shell type %d", lineNumber, iary[i]);
2688 bset->ncomps = ncomps = n;
2691 } else if (strcmp(buf, "Number of primitives per shell") == 0) {
2692 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2693 snprintf(errbuf, errbufsize, "Line %d: wrong size of the primitive table: %s", lineNumber, tokens[2]);
2697 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2698 snprintf(errbuf, errbufsize, "Line %d: cannot read primitive table", lineNumber);
2702 for (i = n = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2703 sp->nprim = iary[i];
2710 } else if (strcmp(buf, "Shell to atom map") == 0) {
2711 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2712 snprintf(errbuf, errbufsize, "Line %d: wrong size of the shell-to-atom map: %s", lineNumber, tokens[2]);
2716 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2717 snprintf(errbuf, errbufsize, "Line %d: cannot read shell-to-atom table", lineNumber);
2721 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2722 sp->a_idx = iary[i] - 1;
2726 } else if (strcmp(buf, "Primitive exponents") == 0) {
2727 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nprims) {
2728 snprintf(errbuf, errbufsize, "Line %d: wrong number of primitive exponents: %s", lineNumber, tokens[2]);
2732 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2733 snprintf(errbuf, errbufsize, "Line %d: cannot read primitive exponents", lineNumber);
2737 /* Allocate PrimInfo table */
2738 AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), nprims - 1, NULL);
2739 for (i = 0, pp = bset->priminfos; i < nprims; i++, pp++) {
2744 } else if (strcmp(buf, "Contraction coefficients") == 0) {
2745 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2746 snprintf(errbuf, errbufsize, "Line %d: wrong number of contraction coefficients: %s", lineNumber, tokens[2]);
2750 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2751 snprintf(errbuf, errbufsize, "Line %d: cannot read contraction coefficients", lineNumber);
2755 for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2760 } else if (strcmp(buf, "P(S=P) Contraction coefficients") == 0) {
2761 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2762 snprintf(errbuf, errbufsize, "Line %d: wrong number of P(S=P) contraction coefficients: %s", lineNumber, tokens[2]);
2766 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2767 snprintf(errbuf, errbufsize, "Line %d: cannot read P(S=P) contraction coefficients", lineNumber);
2771 for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2776 } else if (strcmp(buf, "Alpha Orbital Energies") == 0) {
2777 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2778 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of alpha orbitals: %s", lineNumber, tokens[2]);
2782 if (sReadNumberArray(&bset->moenergies, &bset->nmos, sizeof(Double), i, fp, &lineNumber) != 0) {
2783 snprintf(errbuf, errbufsize, "Line %d: cannot read alpha orbital energies", lineNumber);
2787 } else if (strcmp(buf, "Alpha MO coefficients") == 0) {
2788 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2789 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of alpha MO coefficients: %s", lineNumber, tokens[2]);
2793 if (sReadNumberArray(&bset->mo, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2794 snprintf(errbuf, errbufsize, "Line %d: cannot read MO coefficients", lineNumber);
2798 } else if (strcmp(buf, "Beta Orbital Energies") == 0) {
2799 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2800 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of beta orbitals: %s", lineNumber, tokens[2]);
2804 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2805 snprintf(errbuf, errbufsize, "Line %d: cannot read beta orbital energies", lineNumber);
2809 bset->moenergies = (Double *)realloc(bset->moenergies, sizeof(Double) * 2 * ncomps);
2810 bset->nmos = ncomps * 2;
2811 bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);
2812 memmove(bset->moenergies + ncomps, dary, sizeof(Double) * ncomps);
2813 memset(bset->mo + ncomps * ncomps, 0, sizeof(Double) * ncomps * ncomps);
2816 } else if (strcmp(buf, "Beta MO coefficients") == 0) {
2817 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2818 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of beta MO coefficients: %s", lineNumber, tokens[2]);
2822 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2823 snprintf(errbuf, errbufsize, "Line %d: cannot read alpha MO coefficients", lineNumber);
2827 bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps); /* Should be unnecessary, just in case */
2828 memmove(bset->mo + ncomps * ncomps, dary, sizeof(Double) * ncomps * ncomps);
2831 } else if (strcmp(buf, "Total SCF Density") == 0) {
2832 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * (ncomps + 1) / 2) {
2833 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of SCF densities: %s", lineNumber, tokens[2]);
2837 if (sReadNumberArray(&bset->scfdensities, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2838 snprintf(errbuf, errbufsize, "Line %d: cannot read SCF densities", lineNumber);
2844 if (mp->natoms == 0) {
2845 snprintf(errbuf, errbufsize, "Atom information is missing");
2849 if (bset->shells == NULL || bset->priminfos == NULL) {
2850 snprintf(errbuf, errbufsize, "Gaussian primitive information is missing");
2854 if (bset->mo == NULL) {
2855 snprintf(errbuf, errbufsize, "MO coefficients were not found");
2859 if (sSetupGaussianCoefficients(bset) != 0) {
2860 snprintf(errbuf, errbufsize, "Internal error during setup MO calculation");
2873 if (mp->bset != NULL) {
2874 BasisSetRelease(mp->bset);
2880 Panic("low memory while reading fchk file %s", fname);
2881 return -1; /* not reached */
2885 MoleculeLoadGamessDatFile(Molecule *mol, const char *fname, char *errbuf, int errbufsize)
2890 int lineNumber, i, j, k, len, natoms = 0;
2896 Vector *vbuf = NULL;
2898 int optimizing = 0, status = 0;
2900 if (errbuf == NULL) {
2906 mol = MoleculeNew();
2908 if (mol->natoms == 0)
2911 fp = fopen(fname, "rb");
2913 snprintf(errbuf, errbufsize, "Cannot open file");
2917 /* ESP is cleared (not undoable!) */
2918 if (mol->elpots != NULL) {
2925 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2928 if (strncmp(buf, " $DATA", 6) == 0) {
2929 /* Initial geometry */
2931 vbuf = (Vector *)calloc(sizeof(Vector), mol->natoms);
2934 ReadLine(buf, sizeof buf, fp, &lineNumber); /* Title */
2935 ReadLine(buf, sizeof buf, fp, &lineNumber); /* Symmetry */
2936 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2937 if (strncmp(buf, " $END", 5) == 0)
2939 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
2940 snprintf(errbuf, errbufsize, "Line %d: bad format in $DATA section", lineNumber);
2945 memset(&a, 0, sizeof(a));
2946 strncpy(a.aname, sval, 4);
2950 a.atomicNumber = (Int)dval[0];
2951 strncpy(a.element, ElementToString(a.atomicNumber, sval), 3);
2952 a.type = AtomTypeEncodeToUInt(a.element);
2953 a.weight = WeightForAtomicNumber(a.atomicNumber);
2954 MoleculeCreateAnAtom(mol, &a, mol->natoms);
2957 if (i >= mol->natoms) {
2958 snprintf(errbuf, errbufsize, "Line %d: too many atoms", lineNumber);
2961 if ((ap = ATOM_AT_INDEX(mol->atoms, i))->atomicNumber != dval[0]) {
2962 snprintf(errbuf, errbufsize, "Line %d: atomic number does not match", lineNumber);
2965 vbuf[i].x = dval[1];
2966 vbuf[i].y = dval[2];
2967 vbuf[i].z = dval[3];
2969 /* Skip until a blank line is found */
2970 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2971 for (j = 0; buf[j] == ' '; j++);
2979 /* Set atom positions */
2981 if (natoms < mol->natoms) {
2982 snprintf(errbuf, errbufsize, "Line %d: too few atoms", lineNumber);
2985 ig = IntGroupNewWithPoints(0, natoms, -1);
2986 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, natoms, vbuf);
2987 IntGroupRelease(ig);
2990 vbuf = (Vector *)calloc(sizeof(Vector), natoms);
2991 nframes = MoleculeGetNumberOfFrames(mol);
2995 } else if (strstr(buf, "DATA FROM NSERCH") != NULL || (strstr(buf, "RESULTS FROM SUCCESSFUL") != NULL && (n1 = 1))) {
2996 /* Skip until the separator line is read (three or four lines) */
3000 snprintf(errbuf, errbufsize, "Line %d: the separator line at the top of the coordinates is not found: bad format?", lineNumber);
3003 ReadLine(buf, sizeof buf, fp, &lineNumber);
3004 } while (strstr(buf, "----------------------------") == NULL);
3005 for (i = 0; i < natoms; i++) {
3006 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3007 snprintf(errbuf, errbufsize, "Unexpected end of file in reading NSERCH data");
3010 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3011 snprintf(errbuf, errbufsize, "Line %d: bad format in NSERCH coordinate data", lineNumber);
3014 vbuf[i].x = dval[1];
3015 vbuf[i].y = dval[2];
3016 vbuf[i].z = dval[3];
3018 ig = IntGroupNewWithPoints(nframes, 1, -1);
3019 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, natoms, vbuf, 0, NULL);
3020 IntGroupRelease(ig);
3023 optimizing = 1; /* Flag to skip reading the VEC group */
3027 } else if (strstr(buf, "E(UHF)") != NULL || (strstr(buf, "E(RHF)") != NULL && (n1 = 1)) || (strstr(buf, "E(ROHF)") != NULL && (n1 = 2))) {
3028 if (mol->bset == NULL) {
3029 i = MoleculeAllocateBasisSetRecord(mol, n1, 0, 0);
3031 snprintf(errbuf, errbufsize, "Line %d: cannot allocate basis set internal buffer", lineNumber);
3035 } else if (strncmp(buf, " $VEC", 5) == 0) {
3037 /* Read the vec group */
3038 if (mol->bset == NULL || mol->bset->ncomps == 0)
3039 continue; /* Just ignore */
3041 continue; /* Ignore VEC group during optimization */
3042 coeffs = (Double *)calloc(sizeof(Double), mol->bset->ncomps);
3043 if (coeffs == NULL) {
3044 snprintf(errbuf, errbufsize, "Line %d: low memory during $VEC", lineNumber);
3048 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3050 if (strncmp(buf, " $END", 5) == 0)
3052 while ((j = 5 + (k % 5) * 15) <= len && buf[j] != 0 && buf[j] != '\n') {
3053 strncpy(sval, buf + j, 15);
3055 coeffs[k] = strtod(sval, NULL);
3060 if (k < mol->bset->ncomps)
3062 j = MoleculeSetMOCoefficients(mol, i, -1000000, k, coeffs);
3064 snprintf(errbuf, errbufsize, "Line %d: cannot set coefficients for MO %d", lineNumber, i + 1);
3074 } else if ((strstr(buf, "ELECTRIC POTENTIAL") != NULL || strstr(buf, "ELECTROSTATIC POTENTIAL") != NULL) && strstr(buf, "ELPOTT") != NULL) {
3076 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3078 if (strstr(buf, "TOTAL NUMBER OF GRID POINTS") != NULL)
3080 if (sscanf(buf, "%d %lf %lf %lf %lf", &ival[0], &dval[0], &dval[1], &dval[2], &dval[3]) < 5)
3082 ep = AssignArray(&mol->elpots, &mol->nelpots, sizeof(Elpot), i, NULL);
3083 ep->pos.x = dval[0];
3084 ep->pos.y = dval[1];
3085 ep->pos.z = dval[2];
3090 goto redo; /* This section has no end line, so the last line should be processed again */
3091 else break; /* End of file encountered or interrupted */
3092 } /* TODO: read MOLPLT info if present */
3095 snprintf(errbuf, errbufsize, "User interrupt at line %d", lineNumber);
3100 if (newmol && mol->nbonds == 0) {
3103 MoleculeGuessBonds(mol, 1.2, &nbonds, &bonds);
3105 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds);
3113 MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
3116 if (ftype == NULL || *ftype == 0) {
3118 cp = strrchr(fname, '.');
3122 cp = guessMoleculeType(fname);
3123 if (strcmp(cp, "???") != 0)
3127 if (strcasecmp(ftype, "pdb") == 0) {
3128 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
3131 /* Try all formats once again */
3132 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
3138 MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3144 int i, j, new_unit, retval;
3151 if (errbuf == NULL) {
3156 fp = fopen(fname, "rb");
3158 snprintf(errbuf, errbufsize, "Cannot open file");
3161 /* flockfile(fp); */
3162 if (mp->natoms == 0)
3165 /* Allocate buffer for undo-capable modification */
3166 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
3167 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3168 /* Retain current position if the atom info is missing in the input file */
3174 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3175 if (strncmp(buf, "END", 3) == 0)
3177 if (strncmp(buf, "HETATM", 6) == 0 || strncmp(buf, "ATOM", 4) == 0) {
3179 Int serial, intCharge, resSeq;
3182 char segName[5], resName[4], atomName[5], resSeqStr[5], atomType[3], element[3], occStr[6];
3184 memset(&w, 0, sizeof(w));
3185 ReadFormat(buf, "x6 I5 x1 S4 x1 S3 x1 x1 S4 x1 x3 F8 F8 F8 S6 F6 x6 S4 S2 I2",
3186 &w.serial, w.atomName, w.resName, w.resSeqStr, &w.r.x, &w.r.y, &w.r.z,
3187 w.occStr, &w.temp, w.segName, w.element, &w.intCharge);
3188 if (w.atomName[0] == 0) {
3189 continue; /* Atom name is empty */
3191 /* A workaround for residue number >= 10000 (XPLOR style) */
3192 if (w.resSeqStr[0] >= 'A' && w.resSeqStr[0] <= 'Z') {
3193 w.resSeq = (w.resSeqStr[0] - 'A' + 10) * 1000 + atoi(w.resSeqStr + 1);
3195 w.resSeq = atoi(w.resSeqStr);
3197 if (w.element[0] == 0) {
3198 /* $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl */
3199 for (p = w.atomName; *p != 0; p++) {
3200 if (isalpha(*p) && *p != '_') {
3201 w.element[0] = toupper(*p);
3202 if (isalpha(p[1]) && p[1] != '_') {
3203 w.element[1] = toupper(p[1]);
3212 if (w.occStr[0] == 0)
3215 w.occ = atof(w.occStr);
3216 if (w.serial <= 0) {
3217 snprintf(errbuf, errbufsize, "line %d: non-positive atom number %d", lineNumber, w.serial);
3221 w.serial--; /* The internal atom number is 0-based */
3222 if (w.serial >= mp->natoms) {
3224 /* Create a new atom entry */
3225 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, w.serial, NULL);
3227 snprintf(errbuf, errbufsize, "line %d: the atom number %d does not exist in the structure file", lineNumber, w.serial+1);
3233 ap = ATOM_AT_INDEX(mp->atoms, w.serial);
3235 ap->occupancy = w.occ;
3236 ap->tempFactor = w.temp;
3237 if (w.segName[0] == 0)
3238 strncpy(w.segName, "MAIN", 4);
3239 strncpy(ap->segName, w.segName, 4);
3240 ap->resSeq = w.resSeq;
3241 strncpy(ap->resName, w.resName, 4);
3242 strncpy(ap->aname, w.atomName, 4);
3243 strncpy(ap->element, w.element, 2);
3244 ap->intCharge = w.intCharge;
3245 if (ap->resSeq > 0) {
3246 if (ap->resSeq < mp->nresidues) {
3247 /* Update the resName according to residues[] */
3248 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
3250 /* Register the resName to residues[] */
3251 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, w.resName);
3255 strcpy(ap->resName, "XXX");
3256 if (mp->nresidues == 0)
3257 AssignArray(&mp->residues, &mp->nresidues, 4, 0, ap->resName);
3259 i = ElementToInt(ap->element);
3261 ap->weight = gElementParameters[i].weight;
3263 /* Not a new unit: only the atom position is updated */
3267 } else if (strncmp(buf, "CONECT", 6) == 0 && new_unit) {
3268 i = ReadFormat(buf, "x6 I5I5I5I5I5I5I5I5I5I5I5I5",
3269 ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
3270 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7,
3271 ibuf + 8, ibuf + 9, ibuf + 10, ibuf + 11);
3275 for (j = 0; j < i; j++) {
3276 if (ibuf[j] < 0 || ibuf[j] > mp->natoms) {
3277 snprintf(errbuf, errbufsize, "line %d: The CONECT record contains non-existent atom %d", lineNumber, ibuf[j]);
3280 } else if (ibuf[j] == 0)
3286 for (j = 1, bi = 0; j < i; j++) {
3287 if (ibuf[0] < ibuf[j]) {
3288 bbuf[bi * 2] = ibuf[0] - 1;
3289 bbuf[bi * 2 + 1] = ibuf[j] - 1;
3296 retval = MoleculeAddBonds(mp, bi, bbuf);
3298 snprintf(errbuf, errbufsize, "line %d: bad bond specification", lineNumber);
3305 /* funlockfile(fp); */
3308 /* Renumber atoms if some atom number is unoccupied */
3309 int *old2new, oldidx, newidx;
3310 old2new = (int *)calloc(sizeof(int), mp->natoms);
3311 if (old2new == NULL) {
3312 snprintf(errbuf, errbufsize, "Out of memory");
3316 for (oldidx = newidx = 0; oldidx < mp->natoms; oldidx++) {
3317 ap = ATOM_AT_INDEX(mp->atoms, oldidx);
3318 if (ap->aname[0] != 0) {
3319 old2new[oldidx] = newidx;
3320 if (oldidx > newidx)
3321 memmove(ATOM_AT_INDEX(mp->atoms, newidx), ap, gSizeOfAtomRecord);
3325 mp->natoms = newidx;
3326 if (oldidx > newidx) {
3327 /* Renumber the connects and bonds */
3329 for (i = 0; i < mp->natoms; i++) {
3330 ap = ATOM_AT_INDEX(mp->atoms, i);
3331 cp = AtomConnects(ap);
3332 for (j = 0; j < ap->nconnects; j++) {
3333 cp[j] = old2new[cp[j]];
3336 for (i = 0; i < mp->nbonds * 2; i++) {
3337 mp->bonds[i] = old2new[mp->bonds[i]];
3340 retval = MoleculeRebuildTablesFromConnects(mp);
3342 /* This error may not happen */
3343 snprintf(errbuf, errbufsize, "Cannot build angle/dihedral/improper tables");
3347 /* Undo action: delete all atoms */
3350 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3351 act = MolActionNew(gMolActionUnmergeMolecule, ig);
3352 act->frame = mp->cframe;
3353 MolActionCallback_registerUndo(mp, act);
3354 MolActionRelease(act);
3355 IntGroupRelease(ig);
3358 /* Set the new atom positions */
3359 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3360 MolActionCreateAndPerform(mp, gMolActionSetAtomPositions, ig, mp->natoms, vp);
3361 IntGroupRelease(ig);
3365 mp->nframes = -1; /* Should be recalculated later */
3367 return 1; /* No atoms */
3371 /* funlockfile(fp); */
3377 return 1; /* Maybe different format? */
3382 MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3385 SFloat32 *xp, *yp, *zp;
3390 if (mp == NULL || mp->natoms == 0) {
3391 snprintf(errbuf, errbufsize, "Molecule is empty");
3394 n = DcdOpen(fname, &dcd);
3397 case -2: snprintf(errbuf, errbufsize, "Cannot open file"); break;
3398 case 1: snprintf(errbuf, errbufsize, "Premature EOF encountered"); break;
3399 case 2: snprintf(errbuf, errbufsize, "Bad block length of the first section"); break;
3400 case 3: snprintf(errbuf, errbufsize, "\"CORD\" signature is missing"); break;
3401 case 4: snprintf(errbuf, errbufsize, "Bad termination of the first section"); break;
3402 case 5: snprintf(errbuf, errbufsize, "The title section is not correct"); break;
3403 case 6: snprintf(errbuf, errbufsize, "The atom number section is not correct"); break;
3404 default: snprintf(errbuf, errbufsize, "Read error in dcd file"); break;
3407 if (dcd.natoms == 0)
3408 snprintf(errbuf, errbufsize, "No atoms were found in the dcd file");
3409 else if (dcd.nframes == 0)
3410 snprintf(errbuf, errbufsize, "No frames were found in the dcd file");
3412 if (errbuf[0] != 0) {
3418 vp = (Vector *)calloc(sizeof(Vector), mp->natoms * dcd.nframes);
3420 cp = (Vector *)calloc(sizeof(Vector), dcd.nframes * 4);
3422 xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3423 yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3424 zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3425 ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mp), dcd.nframes, -1);
3426 if (vp == NULL || xp == NULL || yp == NULL || zp == NULL || ig == NULL) {
3427 snprintf(errbuf, errbufsize, "Cannot allocate memory");
3433 if (ig) IntGroupRelease(ig);
3436 for (n = 0; n < dcd.nframes; n++) {
3439 SFloat32 dcdcell[6];
3440 if (DcdReadFrame(&dcd, n, xp, yp, zp, dcdcell)) {
3441 snprintf(errbuf, errbufsize, "Read error in dcd file");
3444 for (i = 0, vpp = &vp[n * mp->natoms]; i < dcd.natoms && i < mp->natoms; i++, vpp++) {
3452 /* dcdcell = {a, gamma, b, beta, alpha, c} */
3453 /* angles are described either in cosines (Charmm and NAMD > 2.5) or degrees (NAMD 2.5) */
3454 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) {
3455 dcdcell[4] = cos(dcdcell[4] * kDeg2Rad); /* cos(alpha) */
3456 dcdcell[3] = cos(dcdcell[3] * kDeg2Rad); /* cos(beta) */
3457 dcdcell[1] = cos(dcdcell[1] * kDeg2Rad); /* cos(gamma) */
3459 /* a axis lies along the cartesian x axis */
3460 sing = sqrt(1 - dcdcell[1] * dcdcell[1]);
3461 vpp[0].x = dcdcell[0];
3464 vpp[1].x = dcdcell[2] * dcdcell[1];
3465 vpp[1].y = dcdcell[2] * sing;
3467 vpp[2].x = dcdcell[5] * dcdcell[3];
3468 vpp[2].y = dcdcell[5] * (dcdcell[4] - dcdcell[3] * dcdcell[1]) / sing;
3469 vpp[2].z = sqrt(dcdcell[5] * dcdcell[5] - vpp[2].x * vpp[2].x - vpp[2].y * vpp[2].y);
3470 vpp[3].x = vpp[3].y = vpp[3].z = 0.0;
3471 if (mp->cell == NULL) {
3472 /* Create periodicity if not present */
3473 MolActionCreateAndPerform(mp, gMolActionSetBox, &vpp[0], &vpp[1], &vpp[2], &vpp[3], 7);
3477 if (MolActionCreateAndPerform(mp, gMolActionInsertFrames, ig, mp->natoms * dcd.nframes, vp, (cp == NULL ? 0 : dcd.nframes * 4), cp) != 0)
3478 snprintf(errbuf, errbufsize, "Cannot insert frames");
3479 mp->startStep = dcd.nstart;
3480 mp->stepsPerFrame = dcd.ninterval;
3481 mp->psPerStep = dcd.delta;
3490 IntGroupRelease(ig);
3497 MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3507 fp = fopen(fname, "rb");
3509 snprintf(errbuf, errbufsize, "Cannot open file");
3515 flags[0] = flags[1] = flags[2] = 0;
3516 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3517 if (strncmp(buf, "Bounding box:", 13) == 0) {
3518 for (i = 0; i < 3; i++) {
3519 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3520 snprintf(errbuf, errbufsize, "line %d: missing %d component of the bounding box", lineNumber, i + 1);
3524 n = sscanf(buf, "%lf %lf %lf %d", &d[0], &d[1], &d[2], &flag);
3526 vv.x = vv.y = vv.z = 0.0;
3528 case 0: vv.x = d[0]; break;
3529 case 1: vv.y = d[0]; break;
3530 case 2: vv.z = d[0]; break;
3532 if (n == 1 || (n == 2 && d[1] != 0.0))
3539 flags[i] = (flag != 0);
3541 flags[i] = (VecLength2(vv) != 0);
3545 if (mp->cell != NULL)
3546 vv = mp->cell->origin;
3548 vv.x = vv.y = vv.z = 0.0;
3549 MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags);
3550 } else if (strncmp(buf, "Bounding box origin:", 20) == 0) {
3551 if (mp->cell != NULL) {
3552 v[0] = mp->cell->axes[0];
3553 v[1] = mp->cell->axes[1];
3554 v[2] = mp->cell->axes[2];
3555 memmove(flags, mp->cell->flags, 3);
3557 v[0].x = 1.0; v[0].y = v[0].z = 0.0;
3558 v[1].y = 1.0; v[1].x = v[1].z = 0.0;
3559 v[2].z = 1.0; v[2].x = v[2].y = 0.0;
3560 flags[0] = flags[1] = flags[2] = 1.0;
3562 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0 || (n = sscanf(buf, "%lf %lf %lf", &d[0], &d[1], &d[2]) < 3)) {
3563 snprintf(errbuf, errbufsize, "line %d: wrong format for the bounding box origin", lineNumber);
3570 MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags);
3582 MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
3585 if (ftype == NULL || *ftype == 0) {
3587 cp = strrchr(fname, '.');
3591 cp = guessMoleculeType(fname);
3592 if (strcmp(cp, "???") != 0)
3596 if (strcasecmp(ftype, "psf") == 0) {
3597 retval = MoleculeWriteToPsfFile(mp, fname, errbuf, errbufsize);
3598 } else if (strcasecmp(ftype, "pdb") == 0) {
3599 retval = MoleculeWriteToPdbFile(mp, fname, errbuf, errbufsize);
3600 } else if (strcasecmp(ftype, "tep") == 0) {
3601 retval = MoleculeWriteToTepFile(mp, fname, errbuf, errbufsize);
3603 snprintf(errbuf, errbufsize, "The file format should be specified");
3607 MoleculeSetPath(mp, fname);
3612 MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3615 int i, j, k, n1, n2, n3, n_aniso;
3619 fp = fopen(fname, "wb");
3621 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
3626 fprintf(fp, "!:atoms\n");
3627 fprintf(fp, "! idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge\n");
3628 n1 = n2 = n3 = n_aniso = 0;
3629 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3630 strncpy(bufs[0], ap->segName, 4);
3632 strncpy(bufs[1], ap->resName, 4);
3634 strncpy(bufs[2], ap->aname, 4);
3636 AtomTypeDecodeToString(ap->type, bufs[3]);
3638 strncpy(bufs[4], ap->element, 4);
3640 for (j = 0; j < 5; j++) {
3641 if (bufs[j][0] == 0) {
3645 for (k = 0; k < 6; k++) {
3646 if (bufs[j][k] == 0)
3648 if (bufs[j][k] > 0 && bufs[j][k] < ' ')
3652 if (SYMOP_ALIVE(ap->symop))
3654 if (ap->fix_force != 0)
3656 if (ap->mm_exclude || ap->periodic_exclude)
3658 if (ap->aniso != NULL)
3660 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);
3665 fprintf(fp, "!:atoms_symop\n");
3666 fprintf(fp, "! idx symop symbase\n");
3667 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3669 n = ap->symop.sym * 1000000 + ap->symop.dx * 10000 + ap->symop.dy * 100 + ap->symop.dz;
3670 fprintf(fp, "%d %d %d\n", i, n, ap->symbase);
3676 fprintf(fp, "!:atoms_fix\n");
3677 fprintf(fp, "! idx fix_force fix_pos\n");
3678 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3679 fprintf(fp, "%d %f %f %f %f\n", i, ap->fix_force, ap->fix_pos.x, ap->fix_pos.y, ap->fix_pos.z);
3685 fprintf(fp, "!:mm_exclude\n");
3686 fprintf(fp, "! idx mm_exclude periodic_exclude\n");
3687 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3688 fprintf(fp, "%d %d %d\n", i, ap->mm_exclude, ap->periodic_exclude);
3693 if ((n1 = MoleculeGetNumberOfFrames(mp)) > 0)
3697 for (i = 0; (i == n2 || i < n1); i++) {
3698 fprintf(fp, "!:positions ; frame %d\n", i);
3699 fprintf(fp, "! idx x y z [sx sy sz]\n");
3700 for (j = 0, ap = mp->atoms; j < mp->natoms; j++, ap = ATOM_NEXT(ap)) {
3703 if (i != n2 && i < ap->nframes)
3704 vp = ap->frames + i;
3707 if (ap->sigma.x != 0.0 || ap->sigma.y != 0.0 || ap->sigma.z != 0.0)
3710 fprintf(fp, "%d %.8f %.8f %.8f", j, vp->x, vp->y, vp->z);
3712 fprintf(fp, " %.8f %.8f %.8f", ap->sigma.x, ap->sigma.y, ap->sigma.z);
3719 if (mp->nbonds > 0) {
3720 fprintf(fp, "!:bonds\n");
3721 fprintf(fp, "! from1 to1 from2 to2 from3 to3 from4 to4\n");
3722 for (i = 0; i < mp->nbonds; i++) {
3723 fprintf(fp, "%d %d%c", mp->bonds[i * 2], mp->bonds[i * 2 + 1], (i % 4 == 3 || i == mp->nbonds - 1 ? '\n' : ' '));
3728 if (mp->nangles > 0) {
3729 fprintf(fp, "!:angles\n");
3730 fprintf(fp, "! a1 b1 c1 a2 b2 c2 a3 b3 c3\n");
3731 for (i = 0; i < mp->nangles; i++) {
3732 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' : ' '));
3737 if (mp->ndihedrals > 0) {
3738 fprintf(fp, "!:dihedrals\n");
3739 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3740 for (i = 0; i < mp->ndihedrals; i++) {
3741 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' : ' '));
3746 if (mp->nimpropers > 0) {
3747 fprintf(fp, "!:impropers\n");
3748 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3749 for (i = 0; i < mp->nimpropers; i++) {
3750 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' : ' '));
3755 if (mp->cell != NULL) {
3756 fprintf(fp, "!:xtalcell\n");
3757 fprintf(fp, "! a b c alpha beta gamma\n");
3758 fprintf(fp, "! This information is redundant and overridden by the following periodic_box info\n");
3759 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]);
3762 fprintf(fp, "!:periodic_box\n");
3763 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");
3764 for (i = 0; i < 3; i++)
3765 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->axes[i].x, mp->cell->axes[i].y, mp->cell->axes[i].z);
3766 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->origin.x, mp->cell->origin.y, mp->cell->origin.z);
3767 fprintf(fp, "%d %d %d%s\n", mp->cell->flags[0], mp->cell->flags[1], mp->cell->flags[2], (mp->cell->has_sigma ? " 1" : ""));
3768 if (mp->cell->has_sigma) {
3769 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]);
3774 if (mp->frame_cells != NULL) {
3775 fprintf(fp, "!:frame_periodic_boxes\n");
3776 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz\n");
3777 for (i = 0; i < mp->nframe_cells * 4; i++) {
3778 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->frame_cells[i].x, mp->frame_cells[i].y, mp->frame_cells[i].z);
3783 if (mp->nsyms > 0) {
3784 fprintf(fp, "!:symmetry_operations\n");
3785 fprintf(fp, "! a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3\n");
3786 for (i = 0; i < mp->nsyms; i++) {
3787 Transform *tp = mp->syms + i;
3788 const unsigned char s_index_order[12] = {0, 3, 6, 1, 4, 7, 2, 5, 8, 9, 10, 11};
3789 for (j = 0; j < 12; j++)
3790 fprintf(fp, "%11.6f%c", (*tp)[s_index_order[j]], (j % 3 == 2 ? '\n' : ' '));
3796 fprintf(fp, "!:anisotropic_thermal_parameters\n");
3797 fprintf(fp, "! b11 b22 b33 b12 b13 b23 [sigma; sb11 sb22 sb33 sb12 sb13 sb23]\n");
3798 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3799 if (ap->aniso != NULL) {
3800 Double *bp = ap->aniso->bij;
3801 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" : ""));
3802 if (ap->aniso->has_bsig) {
3803 bp = ap->aniso->bsig;
3804 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]);
3807 fprintf(fp, "0 0 0 0 0 0\n");
3813 if (mp->arena != NULL) {
3814 MDArena *arena = mp->arena;
3815 fprintf(fp, "!:md_parameters\n");
3816 fprintf(fp, "log_file %s\n", arena->log_result_name);
3817 fprintf(fp, "coord_file %s\n", arena->coord_result_name);
3818 fprintf(fp, "vel_file %s\n", arena->vel_result_name);
3819 fprintf(fp, "force_file %s\n", arena->force_result_name);
3820 fprintf(fp, "debug_file %s\n", arena->debug_result_name);
3821 fprintf(fp, "debug_output_level %d\n", arena->debug_output_level);
3822 fprintf(fp, "step %d\n", arena->step);
3823 fprintf(fp, "coord_output_freq %d\n", arena->coord_output_freq);
3824 fprintf(fp, "energy_output_freq %d\n", arena->energy_output_freq);
3825 fprintf(fp, "coord_frame %d\n", arena->coord_result_frame);
3826 fprintf(fp, "timestep %g\n", arena->timestep);
3827 fprintf(fp, "cutoff %g\n", arena->cutoff);
3828 fprintf(fp, "electro_cutoff %g\n", arena->electro_cutoff);
3829 fprintf(fp, "pairlist_distance %g\n", arena->pairlist_distance);
3830 fprintf(fp, "temperature %g\n", arena->temperature);
3831 fprintf(fp, "andersen_freq %d\n", arena->andersen_thermo_freq);
3832 fprintf(fp, "andersen_coupling %g\n", arena->andersen_thermo_coupling);
3833 fprintf(fp, "random_seed %d\n", arena->random_seed);
3834 fprintf(fp, "dielectric %g\n", arena->dielectric);
3835 fprintf(fp, "gradient_convergence %g\n", arena->gradient_convergence);
3836 fprintf(fp, "coordinate_convergence %g\n", arena->coordinate_convergence);
3837 fprintf(fp, "use_xplor_shift %d\n", arena->use_xplor_shift);
3838 fprintf(fp, "scale14_vdw %g\n", arena->scale14_vdw);
3839 fprintf(fp, "scale14_elect %g\n", arena->scale14_elect);
3840 fprintf(fp, "relocate_center %d\n", arena->relocate_center);
3841 fprintf(fp, "surface_probe_radius %g\n", arena->probe_radius);
3842 fprintf(fp, "surface_tension %g\n", arena->surface_tension);
3843 fprintf(fp, "surface_potential_freq %d\n", arena->surface_potential_freq);
3844 fprintf(fp, "use_graphite %d\n", arena->use_graphite);
3845 fprintf(fp, "alchemical_lambda %g\n", arena->alchem_lambda);
3846 fprintf(fp, "alchemical_delta_lambda %g\n", arena->alchem_dlambda);
3847 if (arena->nalchem_flags > 0) {
3848 fprintf(fp, "alchem_flags %d", arena->nalchem_flags);
3849 for (i = 0; i < arena->nalchem_flags; i++) {
3852 else if (i % 10 == 0)
3854 fputc('0' + arena->alchem_flags[i], fp);
3858 if (arena->pressure != NULL) {
3860 fprintf(fp, "pressure_freq %d\n", arena->pressure->freq);
3861 fprintf(fp, "pressure_coupling %g\n", arena->pressure->coupling);
3862 dp = arena->pressure->apply;
3863 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]);
3864 dp = arena->pressure->cell_flexibility;
3865 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]);
3866 fprintf(fp, "pressure_fluctuate_cell_origin %g\n", arena->pressure->fluctuate_cell_origin);
3867 fprintf(fp, "pressure_fluctuate_cell_orientation %g\n", arena->pressure->fluctuate_cell_orientation);
3871 if (mp->par != NULL) {
3872 Parameter *par = mp->par;
3873 fprintf(fp, "!:parameters\n");
3874 ParameterAppendToFile(par, fp);
3878 fprintf(fp, "!:velocity\n");
3879 fprintf(fp, "! idx vx vy vz\n");
3880 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3881 fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->v.x, ap->v.y, ap->v.z);
3885 fprintf(fp, "!:force\n");
3886 fprintf(fp, "! idx fx fy fz\n");
3887 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3888 fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->f.x, ap->f.y, ap->f.z);
3893 if (mp->mview != NULL) {
3895 if (mp->mview->track != NULL) {
3896 fprintf(fp, "!:trackball\n");
3897 fprintf(fp, "! scale; trx try trz; theta_deg x y z\n");
3898 f[0] = TrackballGetScale(mp->mview->track);
3899 fprintf(fp, "%f\n", f[0]);
3900 TrackballGetTranslate(mp->mview->track, f);
3901 fprintf(fp, "%f %f %f\n", f[0], f[1], f[2]);
3902 TrackballGetRotate(mp->mview->track, f);
3903 fprintf(fp, "%f %f %f %f\n", f[0], f[1], f[2], f[3]);
3906 fprintf(fp, "!:view\n");
3907 fprintf(fp, "show_unit_cell %d\n", mp->mview->showUnitCell);
3908 fprintf(fp, "show_periodic_box %d\n", mp->mview->showPeriodicBox);
3909 fprintf(fp, "show_expanded_atoms %d\n", mp->mview->showExpandedAtoms);
3910 fprintf(fp, "show_ellipsoids %d\n", mp->mview->showEllipsoids);
3911 fprintf(fp, "show_hydrogens %d\n", mp->mview->showHydrogens);
3912 fprintf(fp, "show_dummy_atoms %d\n", mp->mview->showDummyAtoms);
3913 fprintf(fp, "show_rotation_center %d\n", mp->mview->showRotationCenter);
3914 fprintf(fp, "show_graphite_flag %d\n", mp->mview->showGraphiteFlag);
3915 fprintf(fp, "show_graphite %d\n", mp->mview->showGraphite);
3916 fprintf(fp, "show_periodic_image_flag %d\n", mp->mview->showPeriodicImageFlag);
3917 fprintf(fp, "show_periodic_image %d %d %d %d %d %d\n",
3918 mp->mview->showPeriodicImage[0], mp->mview->showPeriodicImage[1],
3919 mp->mview->showPeriodicImage[2], mp->mview->showPeriodicImage[3],
3920 mp->mview->showPeriodicImage[4], mp->mview->showPeriodicImage[5]);
3929 MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3934 fp = fopen(fname, "wb");
3936 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
3940 fprintf(fp, "PSF\n\n");
3941 fprintf(fp, " 1 !NTITLE\n");
3942 fprintf(fp, " REMARKS FILENAME=\n");
3946 fprintf(fp, "%8d !NATOM\n", mp->natoms);
3947 for (i = 0; i < mp->natoms; i++) {
3949 ap = ATOM_AT_INDEX(mp->atoms, i);
3950 fprintf(fp, "%8d ", i + 1);
3951 if (ap->resSeq >= 10000) {
3952 fmt = "%-3.3s %-5d ";
3954 fmt = "%-4.4s %-4d ";
3956 fprintf(fp, fmt, ap->segName, ap->resSeq);
3957 fprintf(fp, "%-3.3s %-4.4s %-4.4s %12.6f %8.4f 0\n",
3958 ap->resName, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->charge, ap->weight);
3963 fprintf(fp, "%8d !NBOND: bonds\n", mp->nbonds);
3964 for (i = 0; i < mp->nbonds * 2; i++) {
3965 fprintf(fp, "%8d", mp->bonds[i] + 1);
3974 fprintf(fp, "%8d !NTHETA: angles\n", mp->nangles);
3975 for (i = 0; i < mp->nangles * 3; i++) {
3976 fprintf(fp, "%8d", mp->angles[i] + 1);
3985 fprintf(fp, "%8d !NPHI: dihedrals\n", mp->ndihedrals);
3986 for (i = 0; i < mp->ndihedrals * 4; i++) {
3987 fprintf(fp, "%8d", mp->dihedrals[i] + 1);
3996 fprintf(fp, "%8d !NIMPHI: impropers\n", mp->nimpropers);
3997 for (i = 0; i < mp->nimpropers * 4; i++) {
3998 fprintf(fp, "%8d", mp->impropers[i] + 1);
4006 fprintf(fp, "%8d !NDON: donors\n\n", 0);
4007 fprintf(fp, "%8d !NACC: acceptors\n\n", 0);
4008 fprintf(fp, "%8d !NNB: non-bonding exclusions\n\n", 0);
4009 for (i = 0; i < mp->natoms; i++) {
4010 fprintf(fp, "%8d", 0);
4017 fprintf(fp, "%8d !NGRP: groups\n", 1);
4018 fprintf(fp, " 0 0 0\n");
4022 if (i > 5 && strcmp(fname + i - 5, ".psfx") == 0) {
4023 /* Extended psf (with coordinates and other info) */
4024 fprintf(fp, "%8d !COORD: coordinates\n", mp->natoms);
4025 for (i = 0; i < mp->natoms; i++) {
4027 ap = ATOM_AT_INDEX(mp->atoms, i);
4029 fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->aname);
4033 if (mp->nframes > 0) {
4034 int fn; /* Frame number */
4035 for (fn = 0; fn < ap->nframes; fn++) {
4036 fprintf(fp, "%8d !COORD: coordinates for frame %d\n", mp->natoms, fn);
4037 for (i = 0; i < mp->natoms; i++) {
4039 ap = ATOM_AT_INDEX(mp->atoms, i);
4040 if (ap->frames == NULL || fn >= ap->nframes)
4044 fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->name);
4057 MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
4062 fp = fopen(fname, "wb");
4064 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
4068 for (i = 0; i < mp->natoms; i++) {
4070 ap = ATOM_AT_INDEX(mp->atoms, i);
4071 if (ap->resSeq >= 10000) {
4072 snprintf(buf, sizeof buf, "%c%03d", 'A' + (ap->resSeq - 10000) / 1000, ap->resSeq % 1000);
4074 snprintf(buf, sizeof buf, "%4d", ap->resSeq);
4076 fprintf(fp, "ATOM %5d %-4.4s%1.1s%-3.3s %1.1s%4.4s%1.1s "
4077 "%8.3f%8.3f%8.3f %5.2f %5.2f "
4078 "%-4.4s%-2.2s%-2d\n",
4079 i + 1, ap->aname, " ", ap->resName, " ", buf, " ",
4080 ap->r.x, ap->r.y, ap->r.z, ap->occupancy, ap->tempFactor,
4081 ap->segName, ap->element, ap->intCharge);
4083 for (i = 0; i < mp->natoms; i++) {
4085 ap = ATOM_AT_INDEX(mp->atoms, i);
4086 cp = AtomConnects(ap);
4087 for (j = 0; j < ap->nconnects; j++) {
4091 fprintf(fp, "CONECT%5d", i + 1);
4093 fprintf(fp, "%5d", cp[j] + 1);
4098 fprintf(fp, "END\n");
4104 MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
4107 SFloat32 *xp, *yp, *zp;
4110 if (mp == NULL || mp->natoms == 0) {
4111 snprintf(errbuf, errbufsize, "Molecule is empty");
4114 memset(&dcd, 0, sizeof(dcd));
4115 dcd.natoms = mp->natoms;
4116 dcd.nframes = MoleculeGetNumberOfFrames(mp);
4117 if (dcd.nframes == 0) {
4118 snprintf(errbuf, errbufsize, "no frame is present");
4121 dcd.nstart = mp->startStep;
4122 dcd.ninterval = mp->stepsPerFrame;
4123 if (dcd.ninterval == 0)
4125 dcd.nend = dcd.nstart + (dcd.nframes - 1) * dcd.ninterval;
4126 if (mp->cell != NULL)
4128 dcd.delta = mp->psPerStep;
4129 if (dcd.delta == 0.0)
4132 n = DcdCreate(fname, &dcd);
4135 snprintf(errbuf, errbufsize, "Cannot create dcd file");
4137 snprintf(errbuf, errbufsize, "Cannot write dcd header");
4142 xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4143 yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4144 zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4145 if (xp == NULL || yp == NULL || zp == NULL) {
4146 snprintf(errbuf, errbufsize, "Cannot allocate memory");
4153 for (n = 0; n < dcd.nframes; n++) {
4156 for (i = 0, ap = mp->atoms; i < dcd.natoms && mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4158 if (ap->frames == NULL || n >= ap->nframes)
4166 if (i < dcd.natoms) {
4167 size_t sz = (dcd.natoms - i) * sizeof(SFloat32);
4168 memset(xp + i, 0, sz);
4169 memset(yp + i, 0, sz);
4170 memset(zp + i, 0, sz);
4172 if (n < mp->nframe_cells && mp->frame_cells != NULL) {
4173 Vector *cp = &(mp->frame_cells[n * 4]);
4174 dcd.globalcell[0] = VecLength(cp[0]);
4175 dcd.globalcell[2] = VecLength(cp[1]);
4176 dcd.globalcell[5] = VecLength(cp[2]);
4177 dcd.globalcell[1] = VecDot(cp[0], cp[1]) / (dcd.globalcell[0] * dcd.globalcell[2]);
4178 dcd.globalcell[3] = VecDot(cp[0], cp[2]) / (dcd.globalcell[0] * dcd.globalcell[5]);
4179 dcd.globalcell[4] = VecDot(cp[1], cp[2]) / (dcd.globalcell[2] * dcd.globalcell[5]);
4181 if (DcdWriteFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
4182 snprintf(errbuf, errbufsize, "Write error in dcd file");
4198 MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
4204 fp = fopen(fname, "wb");
4206 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
4209 if (mp->cell != NULL) {
4210 fprintf(fp, "Bounding box:\n");
4211 for (i = 0; i < 3; i++) {
4212 v = mp->cell->axes[i];
4213 fprintf(fp, "%.3f %.3f %.3f %d\n", v.x, v.y, v.z, mp->cell->flags[i]);
4215 fprintf(fp, "Bounding box origin:\n");
4216 v = mp->cell->origin;
4217 fprintf(fp, "%.3f %.3f %.3f\n", v.x, v.y, v.z);
4224 sCompareByElement(const void *ap, const void *bp)
4226 return ((*(Atom **)bp)->atomicNumber - (*(Atom **)ap)->atomicNumber);
4230 sMakeAdc(int n, int base, Symop symop)
4233 if (SYMOP_ALIVE(symop)) {
4235 sym = (symop.dx + 5) * 10000 + (symop.dy + 5) * 1000 + (symop.dz + 5) * 100 + symop.sym + 1;
4240 return (an + 1) * 100000 + sym;
4244 sCompareAdc(const void *ap, const void *bp)
4246 int n = *((Int *)ap) % 100000 - *((Int *)bp) % 100000;
4248 n = *((Int *)ap) / 100000 - *((Int *)bp) / 100000;
4253 sOutputAtomListInstructions(FILE *fp, int natoms, Atom *atoms)
4255 int i, j, k, an, sym;
4258 adc = (Int *)malloc(sizeof(Int) * natoms);
4261 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4262 if (ap->exflags & kAtomHiddenFlag)
4264 adc[i] = sMakeAdc(i, ap->symbase, ap->symop);
4266 mergesort(adc, natoms, sizeof(Int), sCompareAdc);
4268 /* Create the atom list */
4270 for (i = j = k = 0; i < natoms; i++) {
4271 int an1 = adc[i] / 100000;
4272 int sym1 = adc[i] % 100000;
4273 if (sym == sym1 && an1 == an + 1) {
4280 /* Output the last atom with a minus sign */
4281 adc[j++] = -(an * 100000 + sym);
4282 /* Output this atom */
4289 adc[j++] = -(an * 100000 + sym);
4291 /* Create the instruction cards */
4292 for (i = k = 0; i < j; i++) {
4294 fprintf(fp, " 401");
4295 fprintf(fp, "%9d", adc[i]);
4297 if (i == j - 1 || k == 6 || (k == 5 && i < j - 2 && adc[i + 2] < 0)) {
4306 sEllipsoidType(int an)
4308 return (an >= 18 ? 3 : (an >= 2 && an != 6 ? 2 : (an > 0 ? 1 : 0)));
4312 sOutputAtomTypeInstructions(FILE *fp, int natoms, Atom *atoms)
4316 int etype, elast, istart, ilast, n1, n2;
4317 elast = istart = ilast = -1;
4318 for (i = 0, ap = atoms; i <= natoms; i++, ap++) {
4320 if (SYMOP_ALIVE(ap->symop))
4322 if (ap->exflags & kAtomHiddenFlag)
4324 etype = sEllipsoidType(ap->atomicNumber);
4329 } else if (elast == etype && ilast == i - 1) {
4334 /* Output the instruction card for the 'last' block of atoms */
4337 n1 = 4; n2 = 0; break;
4339 n1 = 4; n2 = 5; break;
4341 n1 = 1; n2 = 0; break;
4343 fprintf(fp, " 1 715 %8d 0 %8d 0 0.100 0.000 0.000\n", n1, n2);
4344 fprintf(fp, " %9d%9d\n", istart + 1, ilast + 1);
4351 sCompareBondType(const void *ap, const void *bp)
4353 /* Descending order */
4354 return *((int *)bp) - *((int *)ap);
4358 sOutputBondInstructions(FILE *fp, int natoms, Atom *atoms, int overlap_correction)
4362 int i, j, n[5], an, count, n1, n2, k;
4366 static const float sBondRad[4] = {0.060, 0.060, 0.060, 0.040};
4367 static const int sBondShade[4] = {5, 3, 1, 1};
4369 n[0] = n[1] = n[2] = n[3] = 0; /* Start index of 3rd row atoms (and higher), 2nd row, 1st row, and H */
4371 for (i = natoms - 1, ap = atoms + i; i >= 0; i--, ap--) {
4372 an = ap->atomicNumber;
4384 if (overlap_correction)
4385 strcpy(buf, " 2 1001 0.000\n");
4387 strcpy(buf, " 2 812\n");
4389 for (i = 0; i < 4; i++) {
4390 for (j = i; j < 4; j++) {
4391 /* Examine bonds between "group i" and "group j" */
4394 double min_bond = 10000.0; /* Minimum distance between bound atoms */
4395 double min_nonbond = 10000.0; /* Minimum distance between non-bound atoms */
4396 double max_bond = -10000.0; /* Maximum distance between bound atoms */
4397 int count_exbond = 0; /* Number of explicit bonds in this group */
4398 for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
4399 for (n2 = n[j], ap2 = atoms + n2; n2 < n[j + 1]; n2++, ap2++) {
4402 VecSub(dr, ap->r, ap2->r);
4404 cp = AtomConnects(ap);
4405 for (k = ap->nconnects - 1; k >= 0; k--) {
4410 /* n1 and n2 are bound */
4416 /* n1 and n2 are not bound */
4417 if (d < min_nonbond)
4422 if (min_bond == 10000.0)
4423 continue; /* No bonds between these groups */
4425 if (max_bond + 0.002 < min_nonbond)
4428 max_bond = min_nonbond - 0.002;
4429 /* Some bonds may be omitted, so scan all bonds again */
4430 for (n1 = n[i], ap = ATOM_AT_INDEX(atoms, n1); n1 < n[i + 1]; n1++, ap = ATOM_NEXT(ap)) {
4431 cp = AtomConnects(ap);
4432 for (k = ap->nconnects - 1; k >= 0; k--) {
4434 if (n2 < n[j] || n2 >= n[j + 1])
4437 VecSub(dr, ap->r, ap2->r);
4440 /* This bond should be explicitly defined */
4442 if (count_exbond == 0) {
4443 adc1 = -(i + 1); /* Bond type */
4444 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4446 adc1 = sMakeAdc(n1, ap->symbase, ap->symop);
4447 adc2 = sMakeAdc(n2, ap2->symbase, ap2->symop);
4448 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4449 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc2);
4455 /* Output the last instruction card */
4457 /* Make a new trailer card */
4458 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]);
4463 /* Output the last trailer card */
4468 if (count == 0 && overlap_correction) {
4469 /* 1001 card is not yet written, so write it */
4473 snprintf(buf, sizeof(buf), " 1 %3d", (overlap_correction ? 821 : 811));
4474 k = -exbonds[0] - 1; /* Bond type for the first block */
4475 i = 1; /* Index for exbonds[] */
4476 j = 0; /* Count in this block */
4477 while (i <= nexbonds) {
4478 if (j >= 29 || i == nexbonds || exbonds[i] < 0) {
4482 /* The trailer card */
4483 fprintf(fp, " %3d %6.3f\n", sBondShade[k], sBondRad[k]);
4487 k = -exbonds[i++] - 1; /* The new bond type */
4489 } else if (j > 0 && j % 3 == 0) {
4495 snprintf(buf + 9 + (j % 3) * 18, sizeof(buf) - 9 - (j % 3) * 18, "%9d%9d\n", n1, n2);
4504 /* Explicit bond table, sorted by bond type */
4505 for (i = j = 0; i < mp->nbonds; i++) {
4506 n1 = mp->bonds[i * 2];
4507 n2 = mp->bonds[i * 2 + 1];
4508 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
4509 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
4510 if ((ap1->exflags & kAtomHiddenFlag) || (ap2->exflags & kAtomHiddenFlag))
4512 if (ap1->atomicNumber > 18 || ap2->atomicNumber > 18) {
4514 } else if (ap1->atomicNumber > 1 && ap1->atomicNumber > 1) {
4520 ip[j * 3 + 1] = sMakeAdc(n1, ap1->symbase, ap1->symop);
4521 ip[j * 3 + 2] = sMakeAdc(n2, ap2->symbase, ap2->symop);
4524 mergesort(ip, j, sizeof(int) * 3, sCompareBondType);
4526 /* Output instruction cards */
4527 strcpy(buf, " 1 811");
4528 for (i = n1 = 0; i < j; i++) {
4529 n2 = (n1 % 3) * 18 + 9;
4530 snprintf(buf + n2, 80 - n2, "%9d%9d\n", ip[i * 3 + 1], ip[i * 3 + 2]);
4531 if (i == j - 1 || n1 >= 29 || ip[i * 3] != ip[i * 3 + 3]) {
4532 /* End of this instruction */
4535 switch (ip[i * 3]) {
4536 case 3: rad = 0.06; nshades = 5; break;
4537 case 2: rad = 0.06; nshades = 1; break;
4538 default: rad = 0.04; nshades = 1; break;
4540 fprintf(fp, " %3d %6.3f\n", nshades, rad);
4541 strcpy(buf, " 1 811");
4544 } else if (n1 % 3 == 2) {
4555 MoleculeWriteToTepFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
4558 int i, j, natoms, *ip;
4560 Atom *ap, *atoms, **app;
4562 static Double sUnit[] = {1, 1, 1, 90, 90, 90};
4566 /* Create sorted array of atoms */
4567 natoms = mp->natoms;
4568 atoms = (Atom *)calloc(sizeof(Atom), natoms);
4569 app = (Atom **)calloc(sizeof(Atom *), natoms);
4570 ip = (int *)calloc(sizeof(int), natoms);
4571 if (atoms == NULL || app == NULL || ip == NULL) {
4572 snprintf(errbuf, errbufsize, "Cannot allocate memory");
4575 /* Sort the atom pointer by atomic number */
4576 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
4578 mergesort(app, natoms, sizeof(Atom *), sCompareByElement);
4579 for (i = 0; i < natoms; i++) {
4580 /* ip[old_index] is new_index */
4581 ip[app[i] - mp->atoms] = i;
4583 /* Copy the atom record to atoms[] */
4584 /* The 'v' member contains crystallographic coordinates */
4585 /* The connection table and symbase are renumbered */
4586 /* Hidden flags are modified to reflect the visibility in the MainView */
4587 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4588 AtomDuplicateNoFrame(ap, app[i]);
4589 /* memmove(ap, app[i], gSizeOfAtomRecord); */
4590 MoleculeCartesianToXtal(mp, &(ap->v), &(ap->r));
4591 cp = AtomConnects(ap);
4592 for (j = ap->nconnects - 1; j >= 0; j--) {
4595 if (SYMOP_ALIVE(ap->symop))
4596 ap->symbase = ip[ap->symbase];
4597 if (MainView_isAtomHidden(mp->mview, i)) {
4598 ap->exflags |= kAtomHiddenFlag;
4600 ap->exflags &= ~kAtomHiddenFlag;
4606 fp = fopen(fname, "wb");
4608 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
4614 fprintf(fp, "Generated by Molby\n");
4617 if (mp->cell != NULL) {
4618 dp = mp->cell->cell;
4622 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]);
4624 /* Symmetry operations */
4625 if (mp->nsyms > 0) {
4626 for (i = 0; i < mp->nsyms; i++) {
4628 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]);
4631 fprintf(fp, "1 0 1 0 0 0 0 1 0 0 0 0 1\n");
4635 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4636 /* The 'v' field contains crystallographic coordinates */
4637 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);
4638 if (ap->aniso != NULL) {
4639 dp = ap->aniso->bij;
4640 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);
4642 Double temp = ap->tempFactor;
4645 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", temp, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4648 /* Special points */
4650 Vector camera, lookat, up, xvec, yvec, zvec;
4651 MainView_getCamera(mp->mview, &camera, &lookat, &up);
4652 VecSub(zvec, lookat, camera);
4653 VecCross(xvec, zvec, up);
4654 NormalizeVec(&xvec, &xvec);
4655 NormalizeVec(&yvec, &up);
4656 VecInc(xvec, lookat);
4657 VecInc(yvec, lookat);
4658 MoleculeCartesianToXtal(mp, &lookat, &lookat);
4659 MoleculeCartesianToXtal(mp, &xvec, &xvec);
4660 MoleculeCartesianToXtal(mp, &yvec, &yvec);
4661 fprintf(fp, " ORGN %9g%9g%9g 0\n", 0.0, 0.0, 0.0);
4662 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4663 fprintf(fp, " CNTR %9g%9g%9g 0\n", lookat.x, lookat.y, lookat.z);
4664 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4665 fprintf(fp, " X %9g%9g%9g 0\n", xvec.x, xvec.y, xvec.z);
4666 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4667 fprintf(fp, " Y %9g%9g%9g 0\n", yvec.x, yvec.y, yvec.z);
4668 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);
4672 fprintf(fp, " 201\n");
4673 fprintf(fp, " 205 12\n");
4674 fprintf(fp, " 301 6.6 6.6 0 0.8\n");
4675 sOutputAtomListInstructions(fp, natoms, atoms);
4676 fprintf(fp, " 501%4d55501%4d55501%4d55501%4d55501%4d55501 1\n", natoms + 2, natoms + 2, natoms + 3, natoms + 2, natoms + 4);
4677 fprintf(fp, " 502 1 0.0 2 0.0 3 0.0\n");
4678 fprintf(fp, " 604 1.538\n");
4680 sOutputBondInstructions(fp, natoms, atoms, 1);
4681 sOutputAtomTypeInstructions(fp, natoms, atoms);
4682 sOutputBondInstructions(fp, natoms, atoms, 0);
4684 for (i = 0; i < natoms; i++) {
4685 AtomClean(atoms + i);
4689 fprintf(fp, " 202\n");
4690 fprintf(fp, " 0 -1\n");
4696 MoleculeDump(Molecule *mol)
4701 for (i = 0; i < mol->natoms; i++) {
4703 ap = ATOM_AT_INDEX(mol->atoms, i);
4704 snprintf(buf1, sizeof buf1, "%3.4s.%d", ap->resName, ap->resSeq);
4705 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);
4706 cp = AtomConnects(ap);
4707 for (j = 0; j < ap->nconnects; j++) {
4708 fprintf(stderr, "%s%d", (j > 0 ? "," : ""), cp[j]);
4710 fprintf(stderr, "]\n");
4714 #pragma mark ====== MD support (including modification of Molecule) ======
4716 /* Call md_prepare for the MDArena. If MDArena has not been created, a new arena is created.
4717 If something goes wrong, returns 1 (for missing parameters) or -1 (more serious error).
4718 If retmsg is not NULL, a message describing the problem is returned there. This message
4719 must be free'd by the caller. */
4721 MoleculePrepareMDArena(Molecule *mol, int check_only, char **retmsg)
4724 Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
4726 IntGroup *ig1, *ig2, *ig3;
4727 MDArena *arena = mol->arena;
4729 if (arena == NULL) {
4732 } else if (arena->xmol != mol)
4733 md_arena_set_molecule(arena, mol);
4735 arena->is_initialized = 0;
4737 /* Rebuild the tables */
4738 ig1 = ig2 = ig3 = NULL;
4739 nangles = MoleculeFindMissingAngles(mol, &angles);
4740 ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
4741 nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
4743 ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
4744 MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
4746 IntGroupRelease(ig1);
4748 if (ndihedrals > 0) {
4749 ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
4750 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
4752 IntGroupRelease(ig2);
4754 if (nimpropers > 0) {
4755 ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
4756 MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
4758 IntGroupRelease(ig3);
4761 /* Prepare parameters and internal information */
4762 msg = md_prepare(arena, check_only);
4764 /* Some parameters are missing? */
4766 if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
4770 asprintf(retmsg, "cannot initialize for MD: %s", msg);
4775 /* The local parameter list is updated */
4778 if (mol->par == NULL)
4779 mol->par = ParameterNew();
4780 for (parType = kFirstParType; parType <= kLastParType; parType++) {
4781 /* Delete global and undefined parameters */
4782 UnionPar *up, *upbuf;
4784 ig1 = IntGroupNew();
4785 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
4786 if (up->bond.src != 0)
4787 IntGroupAdd(ig1, idx, 1);
4789 if (IntGroupGetCount(ig1) > 0)
4790 MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
4791 IntGroupRelease(ig1);
4792 /* Copy global and undefined parameters from arena and insert to mol->par */
4793 nparams = ParameterGetCountForType(arena->par, parType);
4796 upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
4797 ig1 = IntGroupNew();
4798 ig2 = IntGroupNew();
4799 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
4800 if (up->bond.src > 0)
4801 IntGroupAdd(ig1, idx, 1); /* Global parameter */
4802 else if (up->bond.src < 0)
4803 IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
4805 if ((count = IntGroupGetCount(ig1)) > 0) {
4806 /* Insert global parameters (at the top) */
4807 ParameterCopy(arena->par, parType, upbuf, ig1);
4808 ig3 = IntGroupNewWithPoints(0, count, -1);
4809 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
4810 IntGroupRelease(ig3);
4812 if ((count = IntGroupGetCount(ig2)) > 0) {
4813 /* Insert undefined parameters (at the bottom) */
4814 ParameterCopy(arena->par, parType, upbuf, ig2);
4815 idx = ParameterGetCountForType(mol->par, parType);
4816 ig3 = IntGroupNewWithPoints(idx, count, -1);
4817 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
4818 IntGroupRelease(ig3);
4820 IntGroupRelease(ig2);
4821 IntGroupRelease(ig1);
4824 mol->needsMDRebuild = 0; /* We know the "modified" parameters are consistent with the MDArena */
4829 *retmsg = strdup(msg);
4834 #pragma mark ====== Serialize ======
4837 MoleculeDeserialize(const char *data, Int length, Int *timep)
4846 par = ParameterNew();
4850 while (length >= 12) {
4851 const char *ptr = data + 8 + sizeof(Int);
4852 int len = *((const Int *)(data + 8));
4854 if (strcmp(data, "ATOM") == 0) {
4855 n = len / gSizeOfAtomRecord;
4856 NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, n);
4857 memmove(mp->atoms, ptr, len);
4858 } else if (strcmp(data, "ANISO") == 0) {
4860 n = len / (sizeof(Int) + sizeof(Aniso));
4861 for (i = 0; i < n; i++) {
4862 j = *((const Int *)ptr);
4863 if (j < 0 || j >= mp->natoms)
4865 ap = ATOM_AT_INDEX(mp->atoms, j);
4866 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
4867 if (ap->aniso == NULL)
4869 *(ap->aniso) = *((Aniso *)(ptr + sizeof(Int)));
4870 ptr += sizeof(Int) + sizeof(Aniso);
4872 } else if (strcmp(data, "FRAME") == 0) {
4874 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4875 if (ap->nframes == 0)
4877 ap->frames = (Vector *)malloc(sizeof(Vector) * ap->nframes);
4878 if (ap->frames == NULL)
4880 memmove(ap->frames, ptr, sizeof(Vector) * ap->nframes);
4881 ptr += sizeof(Vector) * ap->nframes;
4883 } else if (strcmp(data, "BOND") == 0) {
4884 n = len / (sizeof(Int) * 2);
4885 NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, n);
4886 memmove(mp->bonds, ptr, len);
4887 } else if (strcmp(data, "ANGLE") == 0) {
4888 n = len / (sizeof(Int) * 3);
4889 NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, n);
4890 memmove(mp->angles, ptr, len);
4891 } else if (strcmp(data, "DIHED") == 0) {
4892 n = len / (sizeof(Int) * 4);
4893 NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, n);
4894 memmove(mp->dihedrals, ptr, len);
4895 } else if (strcmp(data, "IMPROP") == 0) {
4896 n = len / (sizeof(Int) * 4);
4897 NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, n);
4898 memmove(mp->impropers, ptr, len);
4899 } else if (strcmp(data, "RESIDUE") == 0) {
4901 NewArray(&mp->residues, &mp->nresidues, 4, n);
4902 memmove(mp->residues, ptr, len);
4903 } else if (strcmp(data, "CELL") == 0) {
4904 mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
4905 if (mp->cell == NULL)
4907 memmove(mp->cell, ptr, sizeof(XtalCell));
4908 } else if (strcmp(data, "SYMOP") == 0) {
4909 n = len / sizeof(Transform);
4910 NewArray(&mp->syms, &mp->nsyms, sizeof(Transform), n);
4911 memmove(mp->syms, ptr, len);
4912 } else if (strcmp(data, "EXATOM") == 0) {
4913 n = len / sizeof(ExAtom);
4914 NewArray(&mp->exatoms, &mp->nexatoms, sizeof(ExAtom), n);
4915 memmove(mp->exatoms, ptr, len);
4916 } else if (strcmp(data, "EXBOND") == 0) {
4917 n = len / (sizeof(Int) * 2);
4918 NewArray(&mp->exbonds, &mp->nexbonds, sizeof(Int) * 2, n);
4919 memmove(mp->exbonds, ptr, len);
4920 } else if (strcmp(data, "TIME") == 0) {
4922 *timep = *((Int *)ptr);
4923 } else if (strcmp(data, "BONDPAR") == 0) {
4925 n = len / sizeof(BondPar);
4926 NewArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), n);
4927 memmove(par->bondPars, ptr, len);
4928 } else if (strcmp(data, "ANGPAR") == 0) {
4930 n = len / sizeof(AnglePar);
4931 NewArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), n);
4932 memmove(par->anglePars, ptr, len);
4933 } else if (strcmp(data, "DIHEPAR") == 0) {
4935 n = len / sizeof(TorsionPar);
4936 NewArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), n);
4937 memmove(par->dihedralPars, ptr, len);
4938 } else if (strcmp(data, "IMPRPAR") == 0) {
4940 n = len / sizeof(TorsionPar);
4941 NewArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), n);
4942 memmove(par->improperPars, ptr, len);
4943 } else if (strcmp(data, "VDWPAR") == 0) {
4945 n = len / sizeof(VdwPar);
4946 NewArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), n);
4947 memmove(par->vdwPars, ptr, len);
4948 } else if (strcmp(data, "VDWPPAR") == 0) {
4950 n = len / sizeof(VdwPairPar);
4951 NewArray(&par->vdwpPars, &par->nvdwpPars, sizeof(VdwPairPar), n);
4952 memmove(par->vdwpPars, ptr, len);
4953 } else if (strcmp(data, "VCUTPAR") == 0) {
4955 n = len / sizeof(VdwCutoffPar);
4956 NewArray(&par->vdwCutoffPars, &par->nvdwCutoffPars, sizeof(VdwCutoffPar), n);
4957 memmove(par->vdwCutoffPars, ptr, len);
4959 len += 8 + sizeof(Int);
4963 if (mp->par == NULL)
4964 ParameterRelease(par);
4965 /* result = MoleculeRebuildTablesFromConnects(mp);
4971 Panic("Low memory while deserializing molecule data");
4972 return NULL; /* Not reached */
4975 Panic("internal error: bad format during deserializing molecule data");
4976 return NULL; /* Not reached */
4980 MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep)
4983 int len, len_all, i, naniso, nframes;
4985 /* Array of atoms */
4986 len = 8 + sizeof(Int) + gSizeOfAtomRecord * mp->natoms;
4987 ptr = (char *)malloc(len);
4990 memmove(ptr, "ATOM\0\0\0\0", 8);
4991 *((Int *)(ptr + 8)) = gSizeOfAtomRecord * mp->natoms;
4992 p = ptr + 8 + sizeof(Int);
4993 memmove(p, mp->atoms, gSizeOfAtomRecord * mp->natoms);
4994 naniso = nframes = 0;
4995 for (i = 0; i < mp->natoms; i++) {
4996 Atom *ap = ATOM_AT_INDEX(p, i);
4997 if (ap->aniso != NULL) {
5001 if (ap->frames != NULL) {
5002 nframes += ap->nframes;
5008 /* Array of aniso */
5010 len = 8 + sizeof(Int) + (sizeof(Int) + sizeof(Aniso)) * naniso;
5011 ptr = (char *)realloc(ptr, len_all + len);
5015 memmove(p, "ANISO\0\0\0", 8);
5016 *((Int *)(p + 8)) = (sizeof(Int) + sizeof(Aniso)) * naniso;
5017 p += 8 + sizeof(Int);
5018 for (i = 0; i < mp->natoms; i++) {
5019 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
5020 if (ap->aniso != NULL) {
5022 *((Aniso *)(p + sizeof(Int))) = *(ap->aniso);
5023 p += sizeof(Int) + sizeof(Aniso);
5029 /* Array of frames */
5031 len = 8 + sizeof(Int) + sizeof(Vector) * nframes;
5032 ptr = (char *)realloc(ptr, len_all + len);
5036 memmove(p, "FRAME\0\0\0", 8);
5037 *((Int *)(p + 8)) = sizeof(Vector) * nframes;
5038 p += 8 + sizeof(Int);
5039 for (i = 0; i < mp->natoms; i++) {
5040 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
5041 if (ap->frames != NULL) {
5042 memmove(p, ap->frames, sizeof(Vector) * ap->nframes);
5043 p += sizeof(Vector) * ap->nframes;
5049 /* Bonds, angles, dihedrals, impropers */
5050 if (mp->nbonds > 0) {
5051 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nbonds;
5052 ptr = (char *)realloc(ptr, len_all + len);
5056 memmove(p, "BOND\0\0\0\0", 8);
5057 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nbonds;
5058 p += 8 + sizeof(Int);
5059 memmove(p, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
5062 if (mp->nangles > 0) {
5063 len = 8 + sizeof(Int) + sizeof(Int) * 3 * mp->nangles;
5064 ptr = (char *)realloc(ptr, len_all + len);
5068 memmove(p, "ANGLE\0\0\0", 8);
5069 *((Int *)(p + 8)) = sizeof(Int) * 3 * mp->nangles;
5070 p += 8 + sizeof(Int);
5071 memmove(p, mp->angles, sizeof(Int) * 3 * mp->nangles);
5074 if (mp->ndihedrals > 0) {
5075 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->ndihedrals;
5076 ptr = (char *)realloc(ptr, len_all + len);
5080 memmove(p, "DIHED\0\0\0", 8);
5081 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->ndihedrals;
5082 p += 8 + sizeof(Int);
5083 memmove(p, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
5086 if (mp->nimpropers > 0) {
5087 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->nimpropers;
5088 ptr = (char *)realloc(ptr, len_all + len);
5092 memmove(p, "IMPROP\0\0", 8);
5093 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->nimpropers;
5094 p += 8 + sizeof(Int);
5095 memmove(p, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
5099 /* Array of residues */
5100 if (mp->nresidues > 0) {
5101 len = 8 + sizeof(Int) + 4 * mp->nresidues;
5102 ptr = (char *)realloc(ptr, len_all + len);
5106 memmove(p, "RESIDUE\0", 8);
5107 *((Int *)(p + 8)) = 4 * mp->nresidues;
5108 p += 8 + sizeof(Int);
5109 memmove(p, mp->residues, 4 * mp->nresidues);
5114 if (mp->cell != NULL) {
5115 len = 8 + sizeof(Int) + sizeof(XtalCell);
5116 ptr = (char *)realloc(ptr, len_all + len);
5120 memmove(p, "CELL\0\0\0\0", 8);
5121 *((Int *)(p + 8)) = sizeof(XtalCell);
5122 p += 8 + sizeof(Int);
5123 memmove(p, mp->cell, sizeof(XtalCell));
5127 /* Symmetry operations */
5128 if (mp->nsyms > 0) {
5129 len = 8 + sizeof(Int) + sizeof(Transform) * mp->nsyms;
5130 ptr = (char *)realloc(ptr, len_all + len);
5134 memmove(p, "SYMOP\0\0\0", 8);
5135 *((Int *)(p + 8)) = sizeof(Transform) * mp->nsyms;
5136 p += 8 + sizeof(Int);
5137 memmove(p, mp->syms, sizeof(Transform) * mp->nsyms);
5141 /* Expanded atoms */
5142 if (mp->nexatoms > 0) {
5143 len = 8 + sizeof(Int) + sizeof(ExAtom) * mp->nexatoms;
5144 ptr = (char *)realloc(ptr, len_all + len);
5148 memmove(p, "EXATOM\0\0", 8);
5149 *((Int *)(p + 8)) = sizeof(ExAtom) * mp->nexatoms;
5150 p += 8 + sizeof(Int);
5151 memmove(p, mp->exatoms, sizeof(ExAtom) * mp->nexatoms);
5152 for (i = 0; i < mp->nexatoms; i++) {
5153 /* Clear label id */
5154 ((ExAtom *)p)[i].labelid = 0;
5159 /* Expanded bonds */
5160 if (mp->nexbonds > 0) {
5161 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nexbonds;
5162 ptr = (char *)realloc(ptr, len_all + len);
5166 memmove(p, "EXBOND\0\0", 8);
5167 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nexbonds;
5168 p += 8 + sizeof(Int);
5169 memmove(p, mp->exbonds, sizeof(Int) * 2 * mp->nexbonds);
5174 if (mp->par != NULL) {
5176 for (type = kFirstParType; type <= kLastParType; type++) {
5177 const char *parname;
5178 Int parsize, parcount;
5182 parname = "BONDPAR\0";
5183 parsize = sizeof(BondPar);
5184 parcount = mp->par->nbondPars;
5185 parptr = mp->par->bondPars;
5188 parname = "ANGPAR\0\0";
5189 parsize = sizeof(AnglePar);
5190 parcount = mp->par->nanglePars;
5191 parptr = mp->par->anglePars;
5193 case kDihedralParType:
5194 parname = "DIHEPAR\0";
5195 parsize = sizeof(TorsionPar);
5196 parcount = mp->par->ndihedralPars;
5197 parptr = mp->par->dihedralPars;
5199 case kImproperParType:
5200 parname = "IMPRPAR\0";
5201 parsize = sizeof(TorsionPar);
5202 parcount = mp->par->nimproperPars;
5203 parptr = mp->par->improperPars;
5206 parname = "VDWPAR\0\0";
5207 parsize = sizeof(VdwPar);
5208 parcount = mp->par->nvdwPars;
5209 parptr = mp->par->vdwPars;
5211 case kVdwPairParType:
5212 parname = "VDWPPAR\0";
5213 parsize = sizeof(VdwPairPar);
5214 parcount = mp->par->nvdwpPars;
5215 parptr = mp->par->vdwpPars;
5217 case kVdwCutoffParType:
5218 parname = "VCUTPAR\0";
5219 parsize = sizeof(VdwCutoffPar);
5220 parcount = mp->par->nvdwCutoffPars;
5221 parptr = mp->par->vdwCutoffPars;
5227 len = 8 + sizeof(Int) + parsize * parcount;
5228 ptr = (char *)realloc(ptr, len_all + len);
5232 memmove(p, parname, 8);
5233 *((Int *)(p + 8)) = parsize * parcount;
5234 p += 8 + sizeof(Int);
5235 memmove(p, parptr, parsize * parcount);
5243 time_t tm = time(NULL);
5244 len = 8 + sizeof(Int) + sizeof(Int);
5245 ptr = (char *)realloc(ptr, len_all + len);
5249 memmove(p, "TIME\0\0\0\0", 8);
5250 *((Int *)(p + 8)) = sizeof(Int);
5251 p += 8 + sizeof(Int);
5252 *((Int *)p) = (Int)tm;
5258 if (outLength != NULL)
5259 *outLength = len_all;
5263 Panic("Low memory while serializing a molecule data");
5264 return NULL; /* Not reached */
5267 #pragma mark ====== Search for bonds, angles, dihedrals, impropers ======
5270 sMoleculeSearchIncludingAtoms(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5274 IntGroup *gp = NULL;
5275 if (atomgroup == NULL)
5277 for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5278 for (j = 0; j < nsize; j++) {
5279 if (IntGroupLookup(atomgroup, ip[j], NULL) != 0) {
5282 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5283 Panic("Low memory while searching %s", msg);
5292 MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5296 return sMoleculeSearchIncludingAtoms(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5300 MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5304 return sMoleculeSearchIncludingAtoms(mp->nangles, mp->angles, 3, atomgroup, "angles");
5308 MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5312 return sMoleculeSearchIncludingAtoms(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5316 MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5320 return sMoleculeSearchIncludingAtoms(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5324 sMoleculeSearchAcrossAtomGroup(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5328 IntGroup *gp = NULL;
5329 if (atomgroup == NULL)
5331 for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5333 for (j = 0; j < nsize; j++) {
5335 kk = (IntGroupLookup(atomgroup, ip[j], NULL) != 0);
5339 /* This bond etc. crosses the atom group border */
5342 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5343 Panic("Low memory while searching %s", msg);
5352 MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5356 return sMoleculeSearchAcrossAtomGroup(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5360 MoleculeSearchAnglesAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5364 return sMoleculeSearchAcrossAtomGroup(mp->nangles, mp->angles, 3, atomgroup, "angles");
5368 MoleculeSearchDihedralsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5372 return sMoleculeSearchAcrossAtomGroup(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5376 MoleculeSearchImpropersAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5380 return sMoleculeSearchAcrossAtomGroup(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5383 /* Subroutine for MoleculeGuessBonds. It can be also used independently, but make sure that *outNbonds/*outBonds
5384 _correctly_ represents an array of two integers (as in mp->nbonds/mp->bonds). */
5385 /* Find atoms within the given "distance" from the given atom. */
5386 /* If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5387 the threshold distance is given by the sum of van der Waals radii times limit. */
5388 /* If triangle is non-zero, then only atoms with lower indexes than index are looked for. */
5390 MoleculeFindCloseAtoms(Molecule *mp, Int index, Double limit, Int *outNbonds, Int **outBonds, Int triangle)
5392 Int n1, n2, j, nlim, newbond[2];
5393 Double a1, a2, alim;
5395 Atom *ap = ATOM_AT_INDEX(mp->atoms, index);
5396 n1 = ap->atomicNumber;
5397 if (n1 >= 0 && n1 < gCountElementParameters)
5398 a1 = gElementParameters[n1].radius;
5399 else a1 = gElementParameters[6].radius;
5401 nlim = (triangle ? index : mp->natoms);
5402 for (j = 0; j < nlim; j++) {
5403 Atom *bp = ATOM_AT_INDEX(mp->atoms, j);
5406 n2 = bp->atomicNumber;
5407 if (n2 >= 0 && n2 < gCountElementParameters)
5408 a2 = gElementParameters[n2].radius;
5409 else a2 = gElementParameters[6].radius;
5415 alim = limit * (a1 + a2);
5416 if (VecLength2(dr) < alim * alim) {
5419 /* MoleculeAddBonds(mp, 1, newbonds); */
5420 AssignArray(outBonds, outNbonds, sizeof(Int) * 2, *outNbonds, newbond);
5426 /* Guess the bonds from the coordinates */
5427 /* If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5428 the threshold distance is given by the sum of van der Waals radii times limit. */
5430 MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds)
5432 Int nbonds, *bonds, i, newbond[2];
5433 /* int i, j, n1, n2;
5436 Double a1, a2, alim;
5438 ElementPar *p = gElementParameters; */
5441 for (i = 0; i < mp->natoms; i++) {
5442 MoleculeFindCloseAtoms(mp, i, limit, &nbonds, &bonds, 1);
5444 ap = ATOM_AT_INDEX(mp->atoms, i);
5445 n1 = ap->atomicNumber;
5446 if (n1 >= 0 && n1 < gCountElementParameters)
5448 else a1 = p[6].radius;
5450 for (j = 0; j < i; j++) {
5451 bp = ATOM_AT_INDEX(mp->atoms, j);
5452 n2 = bp->atomicNumber;
5453 if (n2 >= 0 && n2 < gCountElementParameters)
5455 else a2 = p[6].radius;
5461 alim = limit * (a1 + a2);
5462 if (VecLength2(dr) < alim * alim) {
5465 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5471 newbond[0] = kInvalidIndex;
5473 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5476 if (outNbonds != NULL)
5477 *outNbonds = nbonds;
5478 if (outBonds != NULL)
5483 /* Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information */
5485 MoleculeRebuildTablesFromConnects(Molecule *mp)
5487 int i, j, k, retval;
5494 if (mp->nbonds == 0) {
5495 for (i = 0; i < mp->natoms; i++) {
5496 ap = ATOM_AT_INDEX(mp->atoms, i);
5497 cp = AtomConnects(ap);
5498 for (j = 0; j < ap->nconnects; j++) {
5504 /* MoleculeAddBonds() should not be used, because it assumes connects[] and
5505 bonds are already in sync */
5506 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
5507 /* retval = MoleculeAddBonds(mp, 1, ibuf);
5515 if (mp->nangles == 0) {
5516 for (i = 0; i < mp->natoms; i++) {
5517 ap = ATOM_AT_INDEX(mp->atoms, i);
5518 cp = AtomConnects(ap);
5519 for (j = 0; j < ap->nconnects; j++) {
5520 for (k = j + 1; k < ap->nconnects; k++) {
5525 retval = MoleculeAddAngles(mp, ibuf, NULL);
5533 /* Find dihedrals */
5534 if (mp->ndihedrals == 0) {
5535 for (i = 0; i < mp->natoms; i++) {
5536 ap = ATOM_AT_INDEX(mp->atoms, i);
5537 cp = AtomConnects(ap);
5538 for (j = 0; j < ap->nconnects; j++) {
5545 apjj = ATOM_AT_INDEX(mp->atoms, jj);
5546 cpjj = AtomConnects(apjj);
5547 for (k = 0; k < ap->nconnects; k++) {
5551 for (m = 0; m < apjj->nconnects; m++) {
5553 if (mm == i || mm == kk)
5560 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
5569 /* Find impropers */
5570 if (mp->nimpropers == 0) {
5571 for (i = 0; i < mp->natoms; i++) {
5572 int i1, i2, i4, n1, n2, n4;
5573 ap = ATOM_AT_INDEX(mp->atoms, i);
5574 cp = AtomConnects(ap);
5575 for (i1 = 0; i1 < ap->nconnects; i1++) {
5577 for (i2 = i1 + 1; i2 < ap->nconnects; i2++) {
5579 for (i4 = i2 + 1; i4 < ap->nconnects; i4++) {
5586 retval = MoleculeAddImpropers(mp, ibuf, NULL);
5595 mp->needsMDRebuild = 1;
5596 __MoleculeUnlock(mp);
5600 __MoleculeUnlock(mp);
5604 #pragma mark ====== Atom names ======
5606 /* Look for the n1-th atom in resno-th residue (n1 is 0-based) */
5608 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
5612 if (mp == NULL || mp->natoms == 0)
5615 for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5616 if (ap->resSeq == resno) {
5623 return lasti; /* max */
5628 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
5632 n = strtol(s, &p, 0);
5644 if ((p = strchr(s, ':')) != NULL) {
5645 /* Residue is specified */
5647 if ((pp = strchr(s, '.')) != NULL && pp < p) {
5648 /* Residue number is also specified */
5651 *resSeq = strtol(pp + 1, &ppp, 0);
5653 return -2; /* Bad format */
5654 while (isspace(*ppp))
5657 return -2; /* Bad format */
5660 /* Check whether the "residue name" is an integer */
5661 n = strtol(s, &pp, 0);
5663 while (isspace(*pp))
5665 if (*pp == 0 || *pp == ':') {
5668 return -2; /* Bad format */
5676 if (n >= sizeof(resName))
5677 n = sizeof(resName) - 1;
5678 strncpy(resName, s, n);
5686 strncpy(atomName, p, 4);
5691 /* Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer */
5693 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
5700 n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
5701 if (atomName[0] == 0) {
5702 if (n >= mp->natoms)
5703 n = -1; /* Out of range */
5706 for (n = 0; n < mp->natoms; n++) {
5707 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
5708 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
5709 && (resSeq < 0 || ap->resSeq == resSeq)
5710 && strncmp(atomName, ap->aname, 4) == 0) {
5714 return -1; /* Not found */
5718 MoleculeAreAtomsConnected(Molecule *mp, int n1, int n2)
5722 if (mp == NULL || n1 < 0 || n1 >= mp->natoms || n2 < 0 || n2 >= mp->natoms)
5724 ap = ATOM_AT_INDEX(mp->atoms, n1);
5725 cp = AtomConnects(ap);
5726 for (i = 0; i < ap->nconnects; i++)
5734 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
5738 if (mp == NULL || index < 0 || index >= mp->natoms) {
5742 ap = mp->atoms + index;
5743 if (ap->resSeq != 0) {
5744 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
5748 snprintf(buf, bufsize, "%.4s", ap->aname);
5751 #pragma mark ====== Selection ======
5754 sMoleculeNotifyChangeSelection(Molecule *mp)
5756 /* TODO: Finer control of notification types may be necessary */
5757 MoleculeCallback_notifyModification(mp, 0);
5761 MoleculeSetSelection(Molecule *mp, IntGroup *select)
5766 IntGroupRetain(select);
5767 if (mp->selection != NULL)
5768 IntGroupRelease(mp->selection);
5769 mp->selection = select;
5770 sMoleculeNotifyChangeSelection(mp);
5774 MoleculeGetSelection(Molecule *mp)
5778 else return mp->selection;
5782 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
5784 if (mp->selection == NULL)
5785 mp->selection = IntGroupNew();
5787 IntGroupClear(mp->selection);
5788 IntGroupAdd(mp->selection, n1, 1);
5789 sMoleculeNotifyChangeSelection(mp);
5793 MoleculeUnselectAtom(Molecule *mp, int n1)
5795 if (mp->selection != NULL)
5796 IntGroupRemove(mp->selection, n1, 1);
5797 sMoleculeNotifyChangeSelection(mp);
5801 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
5803 if (mp->selection == NULL)
5804 mp->selection = IntGroupNew();
5805 IntGroupReverse(mp->selection, n1, 1);
5806 sMoleculeNotifyChangeSelection(mp);
5810 MoleculeIsAtomSelected(Molecule *mp, int n1)
5812 if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
5818 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
5820 if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
5826 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
5829 IntGroup *remain, *ig1, *ig2;
5831 remain = IntGroupNewFromIntGroup(remove);
5835 status = IntGroupReverse(remain, 0, mp->natoms);
5837 ig1 = IntGroupNew();
5841 status = IntGroupDifference(selection, remove, ig1);
5844 ig2 = IntGroupNew();
5848 status = IntGroupDeconvolute(ig1, remain, ig2);
5851 IntGroupRelease(remain);
5853 IntGroupRelease(ig1);
5858 IntGroupRelease(ig2);
5863 #pragma mark ====== Atom Equivalence ======
5867 struct sEqList *next;
5868 struct sEqList *link;
5871 static struct sEqList *sListBase = NULL;
5872 static struct sEqList *sListFree = NULL;
5874 static struct sEqList *
5878 if (sListFree != NULL) {
5880 sListFree = lp->next;
5881 lp->i[0] = lp->i[1] = 0;
5885 lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
5886 lp->link = sListBase;
5892 sFreeEqList(struct sEqList *list)
5894 list->next = sListFree;
5899 sDeallocateEqLists(void)
5901 struct sEqList *lp, *lp_link;
5902 for (lp = sListBase; lp != NULL; lp = lp_link) {
5911 sExistInEqList(int i, int idx, struct sEqList *list)
5913 while (list != NULL) {
5914 if (list->i[idx] == i)
5921 static struct sEqList *
5922 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
5925 struct sEqList *list1, *list2;
5926 Int ii, jj, ni, nj, *cpi, *cpj;
5927 api = ATOM_AT_INDEX(mol->atoms, i);
5928 apj = ATOM_AT_INDEX(mol->atoms, j);
5929 if (api->atomicNumber != apj->atomicNumber)
5931 list1 = sAllocEqList();
5937 if (i == j || (db[i] != NULL && db[i] == db[j]))
5939 cpi = AtomConnects(api);
5940 cpj = AtomConnects(apj);
5941 for (ni = 0; ni < api->nconnects; ni++) {
5943 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
5945 if (sExistInEqList(ii, 0, list1))
5948 for (nj = 0; nj < apj->nconnects; nj++) {
5950 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
5952 if (sExistInEqList(jj, 1, list1))
5954 list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
5958 if (list2 == NULL) {
5960 return NULL; /* No equivalent to ii */
5962 list1 = list2; /* ii is OK, try next */
5968 sDBInclude(Int *ip, int i)
5973 for (j = ip[0] - 1; j >= 0; j--) {
5981 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
5983 Int **db; /* List of equivalents for each atom */
5985 Atom *api, *apj, *apk;
5986 Int *cpi, *cpj, *ibuf, nibuf;
5987 int i, j, k, ii, jj, kk;
5988 if (mol == NULL || mol->natoms == 0)
5990 db = (Int **)calloc(sizeof(Int *), mol->natoms);
5994 /* Find the equivalent univalent atoms */
5995 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
5996 if (api->nconnects < 2)
5998 cpi = AtomConnects(api);
5999 for (j = 0; j < api->nconnects; j++) {
6003 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6005 AssignArray(&ibuf, &nibuf, sizeof(Int), n, &jj);
6007 apj = ATOM_AT_INDEX(mol->atoms, jj);
6008 if (apj->nconnects != 1 || db[jj] != NULL)
6010 cpj = AtomConnects(apj);
6011 for (k = j + 1; k < api->nconnects; k++) {
6013 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
6015 apk = ATOM_AT_INDEX(mol->atoms, kk);
6016 if (apk->nconnects != 1 || db[kk] != NULL)
6018 if (apj->atomicNumber == apk->atomicNumber) {
6019 AssignArray(&ibuf, &nibuf, sizeof(Int), n, &kk);
6024 ip = (Int *)calloc(sizeof(Int), n + 1);
6028 memmove(ip + 1, ibuf, sizeof(Int) * n);
6029 for (k = 0; k < n; k++)
6039 /* Try matching (i,j) pair */
6040 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6041 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
6043 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
6044 struct sEqList *list;
6045 if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
6047 if (api->atomicNumber != apj->atomicNumber)
6048 continue; /* Different elements do not match */
6049 if (db[i] != NULL && db[i] == db[j])
6050 continue; /* Already equivalent */
6051 list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
6053 continue; /* (i,j) do not match */
6054 while (list != NULL) {
6057 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
6058 /* Merge db[ii] and db[jj] */
6059 k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
6060 ip = (Int *)calloc(sizeof(Int), k + 1);
6062 return NULL; /* Out of memory */
6063 if (db[ii] == NULL) {
6067 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
6070 if (db[jj] == NULL) {
6073 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
6082 for (k = 0; k < ip[0]; k++)
6086 printf("(%d,%d) matched: ", ii, jj);
6087 for (k = 0; k < ip[0]; k++) {
6088 printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
6098 /* Record the equivalent atoms with the lowest index for each atom */
6099 result = (Int *)calloc(sizeof(Int), mol->natoms);
6100 for (i = 0; i < mol->natoms; i++)
6102 for (i = 0; i < mol->natoms; i++) {
6103 if (result[i] >= 0 || (ip = db[i]) == NULL)
6106 for (j = 0; j < ip[0]; j++) {
6111 for (j = 0; j < ip[0]; j++) {
6112 result[ip[j + 1]] = k;
6113 db[ip[j + 1]] = NULL;
6117 sDeallocateEqLists();
6121 #pragma mark ====== Symmetry expansion ======
6124 MoleculeGetTransformForSymop(Molecule *mp, Symop symop, Transform *tf, int is_cartesian)
6127 if (mp == NULL || mp->cell == NULL)
6129 if (symop.sym >= mp->nsyms)
6131 memmove(*tf, mp->syms[symop.sym], sizeof(Transform));
6132 (*tf)[9] += symop.dx;
6133 (*tf)[10] += symop.dy;
6134 (*tf)[11] += symop.dz;
6136 TransformMul(t, *tf, mp->cell->rtr);
6137 TransformMul(*tf, mp->cell->tr, t);
6143 MoleculeGetSymopForTransform(Molecule *mp, const Transform tf, Symop *symop, int is_cartesian)
6147 if (mp == NULL || mp->cell == NULL)
6152 TransformMul(t, tf, mp->cell->tr);
6153 TransformMul(t, mp->cell->rtr, t);
6155 memmove(t, tf, sizeof(Transform));
6157 for (i = 0; i < mp->nsyms; i++) {
6158 Transform *tp = mp->syms + i;
6159 for (j = 0; j < 9; j++) {
6160 if (fabs((*tp)[j] - t[j]) > 1e-4)
6164 for (j = 9; j < 12; j++) {
6165 double f1 = t[j] - (*tp)[j];
6166 double f2 = floor(f1 + 0.5);
6167 if (fabs(f1 - f2) > 1e-4)
6177 symop->alive = (SYMOP_ALIVE((*symop)) != 0);
6182 return -3; /* Not found */
6186 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
6190 if (symop.sym >= mp->nsyms)
6192 if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
6193 TransformVec(vpout, mp->cell->rtr, vpin);
6194 TransformVec(vpout, mp->syms[symop.sym], vpout);
6195 vpout->x += symop.dx;
6196 vpout->y += symop.dy;
6197 vpout->z += symop.dz;
6198 TransformVec(vpout, mp->cell->tr, vpout);
6200 TransformVec(vpout, mp->syms[symop.sym], vpin);
6201 vpout->x += symop.dx;
6202 vpout->y += symop.dy;
6203 vpout->z += symop.dz;
6208 /* Add expanded atoms. Returns the number of newly created atoms.
6209 If indices is non-NULL, it should be an array of Int with at least
6210 IntGroupGetCount(group) entries, and on return it contains the
6211 indices of the expanded atoms (may be existing atoms if the expanded
6212 atoms are already present) */
6214 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group, Int *indices)
6216 int i, n, n0, n1, count, *table;
6218 IntGroupIterator iter;
6221 if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
6223 if (symop.sym >= mp->nsyms)
6226 /* Create atoms, with avoiding duplicates */
6227 n0 = n1 = mp->natoms;
6228 table = (int *)malloc(sizeof(int) * n0);
6231 for (i = 0; i < n0; i++)
6233 IntGroupIteratorInit(group, &iter);
6234 MoleculeGetTransformForSymop(mp, symop, &tr, 0);
6236 for (i = 0; i < count; i++) {
6241 n = IntGroupIteratorNext(&iter);
6242 ap = ATOM_AT_INDEX(mp->atoms, n);
6243 if (SYMOP_ALIVE(ap->symop)) {
6244 /* Calculate the cumulative symop */
6246 MoleculeGetTransformForSymop(mp, ap->symop, &t1, 0);
6247 TransformMul(t1, tr, t1);
6248 if (MoleculeGetSymopForTransform(mp, t1, &symop1, 0) != 0) {
6249 if (indices != NULL)
6251 continue; /* Skip this atom */
6258 /* Is this expansion already present? */
6259 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
6260 if (ap2->symbase == base && SYMOP_EQUAL(symop1, ap2->symop))
6264 /* If yes, then skip it */
6265 if (indices != NULL)
6269 /* Is the expanded position coincides with itself? */
6270 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
6271 VecSub(dr, ap->r, nr);
6272 if (VecLength2(dr) < 1e-6) {
6273 /* If yes, then this atom is included but no new atom is created */
6275 if (indices != NULL)
6278 /* Create a new atom */
6280 AtomDuplicate(&newAtom, ap);
6281 MoleculeCreateAnAtom(mp, &newAtom, -1);
6282 AtomClean(&newAtom);
6283 ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
6287 ap2->symop.alive = (symop.dx != 0 || symop.dy != 0 || symop.dz != 0 || symop.sym != 0);
6288 table[n] = n1; /* The index of the new atom */
6289 if (indices != NULL)
6294 IntGroupIteratorRelease(&iter);
6297 for (i = 0; i < n0; i++) {
6301 if (b[0] < 0 || b[0] == i)
6303 ap = ATOM_AT_INDEX(mp->atoms, i);
6304 cp = AtomConnects(ap);
6305 for (n = 0; n < ap->nconnects; n++) {
6306 b[1] = table[cp[n]];
6309 if (b[1] > n0 && b[0] > b[1])
6311 MoleculeAddBonds(mp, 1, b);
6314 mp->needsMDRebuild = 1;
6315 __MoleculeUnlock(mp);
6317 return n1 - n0; /* The number of added atoms */
6320 /* Recalculate the coordinates of symmetry expanded atoms.
6321 Returns the number of affected atoms.
6322 If group is non-NULL, only the expanded atoms whose base atoms are in the
6323 given group are considered.
6324 If groupout and vpout are non-NULL, the indices of the affected atoms
6325 and the original positions are returned (for undo operation).
6326 The pointers returned in *groupout and *vpout must be released and
6327 free()'ed by the caller */
6329 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
6333 if (mp == NULL || mp->natoms == 0 || mp->nsyms == 0)
6335 if (groupout != NULL && vpout != NULL) {
6336 *groupout = IntGroupNew();
6337 if (*groupout == NULL)
6339 *vpout = (Vector *)malloc(sizeof(Vector) * mp->natoms);
6340 if (*vpout == NULL) {
6341 IntGroupRelease(*groupout);
6344 } else groupout = NULL; /* To simplify test for validity of groupout/vpout */
6348 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6350 if (!SYMOP_ALIVE(ap->symop))
6352 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
6354 bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6355 MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
6356 VecSub(dr, nr, ap->r);
6357 if (VecLength2(dr) < 1e-20)
6359 if (groupout != NULL) {
6360 (*vpout)[count] = ap->r;
6361 IntGroupAdd(*groupout, i, 1);
6366 mp->needsMDCopyCoordinates = 1;
6367 __MoleculeUnlock(mp);
6368 if (groupout != NULL) {
6372 IntGroupRelease(*groupout);
6375 *vpout = (Vector *)realloc(*vpout, sizeof(Vector) * count);
6381 #pragma mark ====== Show/hide atoms ======
6384 sMoleculeNotifyChangeAppearance(Molecule *mp)
6386 /* TODO: Finer control of notification types may be necessary */
6387 MoleculeCallback_notifyModification(mp, 0);
6392 sMoleculeUnselectHiddenAtoms(Molecule *mp)
6395 if (mp == NULL || mp->selection == NULL)
6397 for (i = 0; i < mp->natoms; i++) {
6398 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6399 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
6400 IntGroupRemove(mp->selection, i, 1);
6402 sMoleculeNotifyChangeAppearance(mp);
6406 MoleculeShowAllAtoms(Molecule *mp)
6411 for (i = 0; i < mp->natoms; i++) {
6412 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6413 ap->exflags &= ~kAtomHiddenFlag;
6415 sMoleculeNotifyChangeAppearance(mp);
6420 MoleculeShowReverse(Molecule *mp)
6425 for (i = 0; i < mp->natoms; i++) {
6426 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6427 ap->exflags ^= kAtomHiddenFlag;
6429 sMoleculeUnselectHiddenAtoms(mp);
6430 sMoleculeNotifyChangeAppearance(mp);
6435 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
6438 if (mp == NULL || ig == NULL)
6440 for (i = 0; i < mp->natoms; i++) {
6441 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6442 if (ap->exflags & kAtomHiddenFlag)
6443 continue; /* Already hidden */
6444 if (IntGroupLookupPoint(ig, i) >= 0)
6445 ap->exflags |= kAtomHiddenFlag;
6447 sMoleculeUnselectHiddenAtoms(mp);
6448 sMoleculeNotifyChangeAppearance(mp);
6452 #pragma mark ====== Reversible Editing ======
6456 sMoleculeNotifyModification(Molecule *mp)
6458 ** TODO: Finer control of notification types may be necessary **
6459 MoleculeCallback_notifyModification(mp, 0);
6463 /* Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup */
6465 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
6468 if (where == NULL) {
6469 /* Append the new objects at the end */
6470 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
6473 n1 = IntGroupGetCount(where); /* Position to get new object */
6474 n2 = nobjs; /* Position to get old object */
6475 n3 = n1 + n2; /* Position to place new/old object */
6476 for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
6477 int start = IntGroupGetStartPoint(where, i);
6478 int end = IntGroupGetEndPoint(where, i);
6480 /* old[end-(n3-n2)..n2-1] is moved to old[end..n3-1] */
6481 memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
6482 n2 = end - (n3 - n2);
6485 /* new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1] */
6486 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
6493 /* Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
6495 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6497 int n1, n2, n3, start, end, i;
6498 if (objs == NULL || where == NULL)
6499 return 1; /* Bad argument */
6500 n1 = 0; /* Position to move remaining elements to */
6501 n2 = 0; /* Position to move remaining elements from */
6502 n3 = 0; /* Position to move removed elements to */
6503 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6504 end = IntGroupGetEndPoint(where, i);
6506 /* Move (start - n2) elements from objs[n2] to objs[n1] */
6508 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
6512 /* Move (end - start) elements from objs[n2] to clip[n3] */
6514 memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
6515 n3 += (end - start);
6516 n2 += (end - start);
6518 /* Move (nobjs - n2) elements from objs[n2] to objs[n1] */
6520 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
6524 /* Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
6526 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6528 int n1, start, end, i;
6529 if (objs == NULL || where == NULL)
6530 return 1; /* Bad argument */
6531 n1 = 0; /* Position to move removed elements to */
6532 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6533 end = IntGroupGetEndPoint(where, i);
6534 /* Copy (end - start) elements from objs[start] to clip[n1] */
6536 memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
6537 n1 += (end - start);
6542 /* Create a new atom with no bonding information. ap must _not_ be inside the given molecule
6543 (Use AtomDuplicate() first) */
6545 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
6549 if (mp == NULL || ap == NULL || mp->noModifyTopology)
6552 if (pos < 0 || pos >= mp->natoms)
6554 ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
6556 goto error; /* Out of memory */
6557 ap1 = ATOM_AT_INDEX(mp->atoms, pos);
6558 if (pos < mp->natoms - 1) {
6559 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6561 if (AtomDuplicate(ap1, ap) == NULL) {
6562 /* Cannot duplicate: restore the original state */
6563 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6568 if (ap1->resSeq >= mp->nresidues)
6569 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
6570 if (ap1->resName[0] == 0)
6571 strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
6572 if (ap1->segName[0] == 0)
6573 strncpy(ap1->segName, "MAIN", 4);
6574 if (pos < mp->natoms - 1) {
6575 /* Renumber the connect table, bonds, angles, etc. */
6576 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
6579 for (j = 0; j < api->nconnects; j++) {
6580 cp = AtomConnects(api);
6585 for (i = 0; i < mp->nbonds * 2; i++) {
6586 if (mp->bonds[i] >= pos)
6589 for (i = 0; i < mp->nangles * 3; i++) {
6590 if (mp->angles[i] >= pos)
6593 for (i = 0; i < mp->ndihedrals * 4; i++) {
6594 if (mp->dihedrals[i] >= pos)
6597 for (i = 0; i < mp->nimpropers * 4; i++) {
6598 if (mp->impropers[i] >= pos)
6602 mp->nframes = -1; /* Should be recalculated later */
6603 MoleculeIncrementModifyCount(mp);
6604 mp->needsMDRebuild = 1;
6605 __MoleculeUnlock(mp);
6608 __MoleculeUnlock(mp);
6612 /* Merge two molecules. We use this procedure for all add-atom operations. */
6613 /* resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
6615 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, int resSeqOffset)
6618 Int i, j, n1, n2, n3, n4, *cp;
6619 Int *new2old, *old2new;
6621 if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
6622 return 0; /* Do nothing */
6624 if (dst->noModifyTopology)
6625 return 1; /* Prohibited operation */
6627 if (where != NULL && IntGroupGetCount(where) != src->natoms)
6628 return 1; /* Bad parameter */
6630 __MoleculeLock(dst);
6633 if (resSeqOffset < 0)
6636 /* Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
6637 and ndst..ndst+nsrc-1 are for atoms in src. */
6638 new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
6639 if (new2old == NULL)
6641 old2new = new2old + ndst + nsrc;
6642 n1 = 0; /* dst index */
6643 n2 = 0; /* src index */
6644 n3 = 0; /* "merged" index */
6646 while (n1 < ndst || n2 < nsrc) {
6647 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
6650 /* n4 elements from dst[n1] will go to merged[n3] */
6651 for (j = 0; j < n4; j++) {
6652 old2new[n1 + j] = n3 + j;
6653 new2old[n3 + j] = n1 + j;
6657 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
6659 /* n4 elements from src[n2] will go to merged[n3] */
6660 for (j = 0; j < n4; j++) {
6661 old2new[ndst + n2 + j] = n3 + j;
6662 new2old[n3 + j] = ndst + n2 + j;
6669 /* Expand the destination array */
6670 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
6673 /* Move the atoms */
6674 if (where == NULL) {
6675 /* Duplicate atoms to the end of the destination array */
6676 for (i = 0; i < nsrc; i++) {
6677 if (AtomDuplicate(ATOM_AT_INDEX(dst->atoms, ndst + i), ATOM_AT_INDEX(src->atoms, i)) == NULL)
6680 // memmove(ATOM_AT_INDEX(dst->atoms, ndst), src->atoms, gSizeOfAtomRecord * nsrc);
6682 /* Duplicate to a temporary storage and then insert */
6683 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
6684 if (tempatoms == NULL)
6686 for (i = 0; i < nsrc; i++) {
6687 if (AtomDuplicate(ATOM_AT_INDEX(tempatoms, i), ATOM_AT_INDEX(src->atoms, i)) == NULL)
6690 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
6694 dst->natoms = ndst + nsrc;
6696 /* Renumber the atom indices in connect[] and symbase, and modify the residue numbers */
6697 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
6698 if (new2old[i] < ndst) {
6699 /* This atom is from dst */
6702 /* This atom is from src */
6703 n1 = ndst; /* Offset to the internal number */
6704 if (ap->resSeq != 0)
6705 ap->resSeq += resSeqOffset; /* Modify residue number */
6707 cp = AtomConnects(ap);
6708 for (j = 0; j < ap->nconnects; j++)
6709 cp[j] = old2new[cp[j] + n1];
6710 if (SYMOP_ALIVE(ap->symop))
6711 ap->symbase = old2new[ap->symbase + n1];
6714 /* Move the bonds, angles, dihedrals, impropers */
6715 for (i = 0; i < 4; i++) {
6716 Int *nitems, *nitems_src;
6717 Int **items, **items_src;
6718 Int nsize; /* Number of Ints in one element */
6721 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
6723 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
6725 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
6727 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
6729 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
6730 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
6731 /* Keep the old number of entries in dst, because it is updated by AssignArray() */
6733 /* Also keep the old number of entries in src, in case src and dst point the same molecule */
6735 /* Expand the array */
6736 if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
6738 /* Copy the items */
6739 memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
6741 for (j = 0; j < n1 * nsize; j++)
6742 (*items)[j] = old2new[(*items)[j]];
6743 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
6744 (*items)[j] = old2new[(*items)[j] + ndst];
6747 /* Merge parameters */
6748 if (src->par != NULL) {
6749 UnionPar *up1, *up2;
6752 if (dst->par == NULL)
6753 dst->par = ParameterNew();
6755 /* Renumber existing parameters */
6756 for (type = kFirstParType; type <= kLastParType; type++) {
6757 n1 = ParameterGetCountForType(dst->par, type);
6758 for (i = 0; i < n1; i++) {
6759 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
6760 ParameterRenumberAtoms(type, up1, ndst, old2new);
6765 for (type = kFirstParType; type <= kLastParType; type++) {
6766 n1 = ParameterGetCountForType(src->par, type);
6767 n2 = ParameterGetCountForType(dst->par, type);
6770 /* Determine which parameter should be copied from src to dst */
6771 for (i = 0; i < n1; i++) {
6773 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
6774 n3 = ParameterGetAtomTypes(type, up1, types);
6775 for (j = 0; j < n3; j++) {
6776 /* If it includes explicit atom index, then it should be copied */
6777 if (types[j] < kAtomTypeMinimum) {
6778 IntGroupAdd(ig, i, 1);
6783 for (j = 0; j < n2; j++) {
6784 up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
6785 if (ParameterCompare(up1, up2, type))
6789 /* This is an unknown parameter; should be copied */
6790 IntGroupAdd(ig, i, 1);
6793 n1 = IntGroupGetCount(ig);
6796 up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
6799 /* Copy parameters and renumber indices if necessary */
6800 for (i = 0; i < n1; i++) {
6801 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
6805 ParameterRenumberAtoms(type, up1 + i, nsrc, old2new + ndst);
6807 /* Merge parameters */
6809 IntGroupAdd(ig, n2, n1);
6810 if (ParameterInsert(dst->par, type, up1, ig) < n1)
6815 IntGroupRelease(ig);
6818 /* Copy the residues if necessary */
6819 /* src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
6820 However, 1+resSeqOffset should not overwrite the existing residue in dst;
6821 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1]. */
6822 n1 = dst->nresidues;
6823 if (1 + resSeqOffset < n1) {
6825 } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
6826 if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
6827 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
6829 memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
6832 MoleculeCleanUpResidueTable(dst);
6835 dst->nframes = -1; /* Should be recalculated later */
6837 MoleculeIncrementModifyCount(dst);
6838 dst->needsMDRebuild = 1;
6839 __MoleculeUnlock(dst);
6843 __MoleculeUnlock(dst);
6844 Panic("Low memory while adding atoms");
6845 return 1; /* Not reached */
6849 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag)
6851 Int nsrc, ndst, nsrcnew;
6852 Int i, j, n1, n2, n3, n4, *cp;
6853 Int *new2old, *old2new;
6854 IntGroup *move_g, *del_g, *remain_g, *dst_par_g, *remove_par_g;
6859 if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
6866 if (src->noModifyTopology && moveFlag)
6867 return 1; /* Prohibit editing */
6869 if ((ndst = IntGroupGetCount(where)) > src->natoms)
6870 return 1; /* Bad parameter */
6872 __MoleculeLock(src);
6875 nsrcnew = nsrc - ndst;
6876 if (resSeqOffset < 0)
6879 /* Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
6880 and nsrcnew..nsrc-1 are for atoms moved into dst. */
6881 new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
6882 if (new2old == NULL)
6884 old2new = new2old + nsrc;
6885 n1 = 0; /* src index */
6886 n2 = 0; /* dst index */
6887 n3 = 0; /* src index after "unmerge" */
6889 while (n1 < nsrc || n2 < ndst) {
6890 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
6893 /* n4 elements from src[n1] will go to unmerged[n3] */
6894 for (j = 0; j < n4; j++) {
6895 old2new[n1 + j] = n3 + j;
6896 new2old[n3 + j] = n1 + j;
6900 if ((n4 = IntGroupGetInterval(where, i)) < 0)
6902 /* n4 elements from src[n1] will go to dst[n2] */
6903 for (j = 0; j < n4; j++) {
6904 old2new[n1 + j] = nsrcnew + n2 + j;
6905 new2old[nsrcnew + n2 + j] = n1 + j;
6912 /* Atoms to remain in the source group */
6914 remain_g = IntGroupNewWithPoints(0, nsrc, -1);
6915 IntGroupRemoveIntGroup(remain_g, where);
6916 } else remain_g = NULL;
6918 /* Find parameters to be moved to the dst (dst_par_g), and to be removed from the src (remove_par_g) */
6919 if (src->par != NULL) {
6920 dst_par_g = IntGroupNew();
6922 remove_par_g = IntGroupNew();
6923 else remove_par_g = NULL;
6924 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
6925 n2 = ParameterGetCountForType(src->par, n1);
6928 for (i = 0; i < n2; i++) {
6929 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
6930 if (ParameterIsRelevantToAtomGroup(n1, up, src->atoms, where)) {
6931 /* This parameter is to be copied to dst */
6932 IntGroupAdd(dst_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
6934 if (moveFlag && !ParameterIsRelevantToAtomGroup(n1, up, src->atoms, remain_g)) {
6935 /* This parameter is to be removed */
6936 IntGroupAdd(remove_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
6940 } else dst_par_g = remove_par_g = NULL;
6942 /* Make a new molecule */
6944 dst = MoleculeNew();
6947 /* Expand the destination array */
6948 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
6950 dst_ap = dst->atoms;
6953 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
6958 /* Move the atoms */
6960 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
6962 src->natoms = nsrcnew;
6964 /* The atom record must be deallocated correctly */
6965 for (i = 0; i < ndst; i++)
6966 AtomClean(ATOM_AT_INDEX(dst_ap, i));
6970 for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
6971 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
6976 /* The dummy destination array is no longer needed */
6981 /* Renumber the atom indices in connect[] */
6983 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
6984 cp = AtomConnects(ap);
6985 for (j = n1 = 0; j < ap->nconnects; j++) {
6986 n2 = old2new[cp[j]];
6990 AtomResizeConnects(ap, n1);
6994 /* Renumber the atom indices in connect[] and the residue indices */
6996 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
6997 if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
6998 ap->resSeq -= resSeqOffset;
6999 else ap->resSeq = 0;
7000 cp = AtomConnects(ap);
7001 for (j = n1 = 0; j < ap->nconnects; j++) {
7002 n2 = old2new[cp[j]] - nsrcnew;
7006 AtomResizeConnects(ap, n1);
7010 /* Separate the bonds, angles, dihedrals, impropers */
7011 /* TODO: Improper torsions should also be copied! */
7012 move_g = IntGroupNew();
7013 del_g = IntGroupNew();
7014 if (move_g == NULL || del_g == NULL)
7016 for (i = 0; i < 4; i++) {
7017 Int *nitems, *nitems_dst;
7018 Int **items, **items_dst;
7019 Int nsize; /* Number of Ints in one element */
7020 unsigned char *counts;
7023 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
7025 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
7027 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
7029 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
7031 nitems = NULL; items = NULL; nsize = 0; break; /* Not reached */
7034 nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
7035 items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
7040 counts = (unsigned char *)calloc(1, *nitems);
7041 /* Find the entries that should be moved to dst */
7043 for (j = 0; j < *nitems * nsize; j++) {
7044 n1 = old2new[(*items)[j]];
7046 counts[j / nsize]++; /* Count the atom belonging to dst */
7047 /* if (n1 >= nsrcnew) {
7049 if (j % nsize == 0) {
7050 if (IntGroupAdd(sep, j / nsize, 1) != 0)
7055 (*items)[j] = n1; */
7057 for (j = n2 = n3 = 0; j < *nitems; j++) {
7058 if (counts[j] > 0) {
7059 /* Remove from src */
7061 if (IntGroupAdd(del_g, j, 1) != 0)
7063 if (counts[j] == nsize) {
7066 if (IntGroupAdd(move_g, j, 1) != 0)
7072 /* Expand the destination array */
7073 if (items_dst != NULL && n3 > 0) {
7074 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
7076 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
7079 /* Remove from src */
7081 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
7086 /* Renumber the entries */
7088 for (j = 0; j < *nitems * nsize; j++) {
7089 (*items)[j] = old2new[(*items)[j]];
7092 if (items_dst != NULL) {
7093 for (j = 0; j < *nitems_dst * nsize; j++) {
7094 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
7098 IntGroupClear(move_g);
7099 IntGroupClear(del_g);
7101 IntGroupRelease(move_g);
7102 IntGroupRelease(del_g);
7104 /* Copy the residues */
7106 /* src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset) */
7107 n1 = src->nresidues - resSeqOffset; /* This will be dst->nresidues (if >0) */
7108 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
7111 memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
7115 /* Copy the parameters to dst */
7116 if (dst != NULL && dst_par_g != NULL && (n2 = IntGroupGetCount(dst_par_g)) > 0) {
7117 IntGroup *dst_new_g = IntGroupNew();
7118 Int dst_par_count[kLastParType - kFirstParType + 1];
7119 if (dst_new_g == NULL)
7121 for (i = 0; i <= kLastParType - kFirstParType; i++)
7122 dst_par_count[i] = 0;
7123 up = (UnionPar *)calloc(sizeof(UnionPar), n2);
7126 if (ParameterCopy(src->par, kFirstParType, up, dst_par_g) < n2)
7128 /* Renumber the explicit atom indices */
7129 for (i = 0; i < nsrc; i++)
7130 old2new[i] -= nsrcnew; /* new indices for atoms in dst; otherwise negative numbers */
7131 for (i = 0; i < n2; i++) {
7132 /* Renumber the indices, and count the number of parameters for each type */
7133 n1 = kFirstParType + IntGroupGetNthPoint(dst_par_g, i) / kParameterIndexOffset;
7134 dst_par_count[n1 - kFirstParType]++;
7135 ParameterRenumberAtoms(n1, up + i, nsrc, old2new);
7137 for (i = 0; i < nsrc; i++)
7138 old2new[i] += nsrcnew;
7139 if (dst->par == NULL)
7140 dst->par = ParameterNew();
7141 for (i = 0; i <= kLastParType - kFirstParType; i++) {
7142 if (dst_par_count[i] > 0)
7143 IntGroupAdd(dst_new_g, i * kParameterIndexOffset, dst_par_count[i]);
7145 if (ParameterInsert(dst->par, kFirstParType, up, dst_new_g) < n2)
7148 IntGroupRelease(dst_new_g);
7151 /* Remove the unused parameter. Note: the parameters that are in remove_par_g and not in
7152 dst_par_g will disappear. To support undo, these parameters should be taken care separately. */
7153 if (remove_par_g != NULL && (n2 = IntGroupGetCount(remove_par_g)) > 0) {
7154 ParameterDelete(src->par, kFirstParType, NULL, remove_par_g);
7157 /* Renumber the parameter records remaining in the src */
7159 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7160 n2 = ParameterGetCountForType(src->par, n1);
7161 for (i = 0; i < n2; i++) {
7162 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7163 ParameterRenumberAtoms(n1, up, nsrc, old2new);
7169 MoleculeCleanUpResidueTable(src);
7171 MoleculeCleanUpResidueTable(dst);
7174 src->nframes = -1; /* Should be recalculated later */
7176 dst->nframes = -1; /* Should be recalculated later */
7182 MoleculeIncrementModifyCount(src);
7183 src->needsMDRebuild = 1;
7184 __MoleculeUnlock(src);
7189 __MoleculeUnlock(src);
7190 /* Panic("Low memory while removing atoms"); */
7194 /* Separate molecule into two parts. The atoms specified by 'where' are moved
7195 from src to a new molecule, which is returned as *dstp. Dstp can be NULL,
7196 in which case the moved atoms are discarded. */
7198 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset)
7200 return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1);
7203 /* Extract atoms from a given molecule into two parts. The atoms specified by
7204 'where' are copied from src to a new molecule, which is returned as *dstp.
7205 If dummyFlag is non-zero, then the atoms that are not included in the group
7206 but are connected to any atoms in the group are converted to "dummy" atoms
7207 (i.e. with element "Du" and names beginning with an underscore) and included
7208 in the new molecule object. */
7210 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
7214 /* Extract the fragment */
7215 retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0);
7221 /* Search bonds crossing the molecule border */
7222 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
7224 IntGroupIterator iter;
7227 IntGroupIteratorInit(ig, &iter);
7228 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7229 /* The atoms at the border */
7232 n1 = src->bonds[i*2];
7233 n2 = src->bonds[i*2+1];
7234 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
7238 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
7239 continue; /* Actually this is an internal error */
7241 /* n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule */
7242 /* Create a new dummy atom with the same segment/residue info with n1
7243 and the same position as n2 */
7244 ap = ATOM_AT_INDEX(src->atoms, n1);
7245 memset(&a, 0, gSizeOfAtomRecord);
7246 a.segSeq = ap->segSeq;
7247 memmove(a.segName, ap->segName, 4);
7248 a.resSeq = ap->resSeq;
7249 memmove(a.resName, ap->resName, 4);
7250 ElementToString(0, a.element); /* "Du" */
7251 snprintf(a.aname, 4, "_%d", idx++);
7252 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
7253 /* Add the dummy atom to the new molecule; nn[1] is the index
7254 of the new dummy atom in the new molecule */
7255 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
7256 /* Connect nn1 and nn2 */
7257 nn[2] = kInvalidIndex;
7258 MoleculeAddBonds(*dstp, 1, nn);
7260 IntGroupIteratorRelease(&iter);
7261 IntGroupRelease(ig);
7269 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds)
7271 int i, j, n1, n2, n;
7273 Int *bonds_tmp, *cp;
7275 if (mp == NULL || bonds == NULL || nbonds <= 0)
7277 if (mp->noModifyTopology)
7278 return -4; /* Prohibited operation */
7280 /* Check the bonds */
7281 bonds_tmp = (Int *)malloc(sizeof(Int) * nbonds * 2);
7282 if (bonds_tmp == NULL)
7283 return -4; /* Out of memory */
7285 for (i = 0; i < nbonds; i++) {
7287 n2 = bonds[i * 2 + 1];
7288 if (n1 < 0 || n1 >= mp->natoms || n2 < 0 || n2 >= mp->natoms)
7289 return -1; /* Bad bond specification */
7292 ap = ATOM_AT_INDEX(mp->atoms, n1);
7293 /* if (ap->nconnects >= ATOMS_MAX_CONNECTS - 1 || ATOM_AT_INDEX(mp->atoms, n2)->nconnects >= ATOMS_MAX_CONNECTS - 1)
7294 return -2; *//* Too many bonds */
7295 /* Check duplicates */
7296 cp = AtomConnects(ap);
7297 for (j = 0; j < ap->nconnects; j++) {
7301 if (j == ap->nconnects) {
7302 bonds_tmp[n * 2] = n1;
7303 bonds_tmp[n * 2 + 1] = n2;
7308 /* No bonds to add */
7315 /* Add connects[] */
7316 for (i = 0; i < n; i++) {
7317 n1 = bonds_tmp[i * 2];
7318 n2 = bonds_tmp[i * 2 + 1];
7319 ap = ATOM_AT_INDEX(mp->atoms, n1);
7320 AtomInsertConnectEntry(ap, ap->nconnects, n2);
7321 ap = ATOM_AT_INDEX(mp->atoms, n2);
7322 AtomInsertConnectEntry(ap, ap->nconnects, n1);
7325 /* Expand the array and insert */
7327 /* if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, mp->nbonds + nb - 1, NULL) == NULL
7328 || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nb, sizeof(Int) * 2, where) != 0) */
7329 if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, mp->nbonds + n - 1, NULL) == NULL)
7331 memmove(mp->bonds + n1 * 2, bonds_tmp, sizeof(Int) * 2 * n);
7333 /* Add angles, dihedrals, impropers */
7335 Int nangles, ndihedrals, nimpropers;
7336 Int *angles, *dihedrals, *impropers;
7338 Int *ip, *cp1, *cp2;
7342 angles = dihedrals = impropers = NULL;
7343 nangles = ndihedrals = nimpropers = 0;
7345 for (i = 0; i < n; i++) {
7346 n1 = bonds_tmp[i * 2];
7347 n2 = bonds_tmp[i * 2 + 1];
7348 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
7349 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
7350 cp1 = AtomConnects(ap1);
7351 cp2 = AtomConnects(ap2);
7352 /* Angles X-n1-n2 */
7353 for (j = 0; j < ap1->nconnects; j++) {
7360 for (k = 0; k < nangles; k++) {
7361 ip = angles + k * 3;
7362 if (ip[1] == n1 && ((ip[0] == n3 && ip[2] == n2) || (ip[0] == n2 && ip[2] == n3)))
7366 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7369 /* Dihedrals X-n1-n2-X */
7370 for (k = 0; k < ap2->nconnects; k++) {
7372 if (n4 == n1 || n4 == n3)
7375 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7378 /* Impropers X-n2-n1-X */
7381 for (k = 0; k < ap1->nconnects; k++) {
7382 n4 = ap1->connects[k];
7383 if (n4 == n2 || n4 <= n3)
7386 if (AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, temp) == NULL)
7390 /* Angles X-n2-n1 */
7391 for (j = 0; j < ap2->nconnects; j++) {
7398 for (k = 0; k < nangles; k++) {
7399 ip = angles + k * 3;
7400 if (ip[1] == n2 && ((ip[0] == n3 && ip[2] == n1) || (ip[0] == n1 && ip[2] == n3)))
7404 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7409 temp[0] = kInvalidIndex;
7410 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7412 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7414 if (AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, temp) == NULL)
7416 MoleculeAddAngles(mp, angles, NULL);
7417 MoleculeAddDihedrals(mp, dihedrals, NULL);
7418 MoleculeAddImpropers(mp, impropers, NULL);
7421 if (dihedrals != NULL)
7423 if (impropers != NULL)
7427 MoleculeIncrementModifyCount(mp);
7428 mp->needsMDRebuild = 1;
7429 __MoleculeUnlock(mp);
7435 __MoleculeUnlock(mp);
7436 Panic("Low memory while adding bonds");
7437 return -1; /* Not reached */
7441 MoleculeDeleteBonds(Molecule *mp, Int nbonds, const Int *bonds)
7443 Int i, j, n1, n2, *cp;
7446 if (mp == NULL || nbonds <= 0)
7448 if (mp->noModifyTopology)
7449 return -4; /* Prohibited operation */
7453 /* Update connects[] */
7454 for (i = 0; i < nbonds; i++) {
7456 n2 = bonds[i * 2 + 1];
7457 ap = ATOM_AT_INDEX(mp->atoms, n1);
7458 cp = AtomConnects(ap);
7459 for (j = 0; j < ap->nconnects; j++) {
7461 /* memmove(&ap->connects[j], &ap->connects[j + 1], sizeof(Int) * (ap->nconnects - j - 1));
7463 AtomDeleteConnectEntry(ap, j);
7467 ap = ATOM_AT_INDEX(mp->atoms, n2);
7468 cp = AtomConnects(ap);
7469 for (j = 0; j < ap->nconnects; j++) {
7471 /* memmove(&ap->connects[j], &ap->connects[j + 1], sizeof(Int) * (ap->nconnects - j - 1));
7473 AtomDeleteConnectEntry(ap, j);
7479 /* Remove bonds, angles, dihedrals, impropers */
7481 IntGroup *bg, *ag, *dg, *ig;
7488 if (bg == NULL || ag == NULL || dg == NULL || ig == NULL)
7490 for (i = 0; i < nbonds; i++) {
7492 n2 = bonds[i * 2 + 1];
7493 for (j = 0; j < mp->nbonds; j++) {
7494 ip = mp->bonds + j * 2;
7495 if ((ip[0] == n1 && ip[1] == n2)
7496 || (ip[1] == n1 && ip[0] == n2)) {
7497 if (IntGroupAdd(bg, j, 1) != 0)
7501 for (j = 0; j < mp->nangles; j++) {
7502 ip = mp->angles + j * 3;
7503 if ((ip[0] == n1 && ip[1] == n2)
7504 || (ip[1] == n1 && ip[0] == n2)
7505 || (ip[1] == n1 && ip[2] == n2)
7506 || (ip[2] == n1 && ip[1] == n2)) {
7507 if (IntGroupAdd(ag, j, 1) != 0)
7511 for (j = 0; j < mp->ndihedrals; j++) {
7512 ip = mp->dihedrals + j * 4;
7513 if ((ip[1] == n1 && ip[2] == n2)
7514 || (ip[2] == n1 && ip[1] == n2)) {
7515 if (IntGroupAdd(dg, j, 1) != 0)
7519 for (j = 0; j < mp->nimpropers; j++) {
7520 ip = mp->impropers + j * 4;
7521 if ((ip[0] == n1 && ip[2] == n2)
7522 || (ip[1] == n1 && ip[2] == n2)
7523 || (ip[3] == n1 && ip[2] == n2)
7524 || (ip[0] == n2 && ip[2] == n1)
7525 || (ip[1] == n2 && ip[2] == n1)
7526 || (ip[3] == n2 && ip[2] == n1)) {
7527 if (IntGroupAdd(ig, j, 1) != 0)
7532 if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, NULL, sizeof(Int) * 2, bg) != 0)
7534 mp->nbonds -= IntGroupGetCount(bg);
7536 if (IntGroupGetCount(ag) > 0)
7537 MoleculeDeleteAngles(mp, NULL, ag);
7538 if (IntGroupGetCount(dg) > 0)
7539 MoleculeDeleteDihedrals(mp, NULL, dg);
7540 if (IntGroupGetCount(ig) > 0)
7541 MoleculeDeleteImpropers(mp, NULL, ig);
7542 IntGroupRelease(bg);
7543 IntGroupRelease(ag);
7544 IntGroupRelease(dg);
7545 IntGroupRelease(ig);
7548 MoleculeIncrementModifyCount(mp);
7549 mp->needsMDRebuild = 1;
7550 __MoleculeUnlock(mp);
7555 __MoleculeUnlock(mp);
7556 Panic("Low memory while removing bonds");
7557 return -1; /* Not reached */
7561 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
7564 if (mp == NULL || angles == NULL)
7566 if (mp->noModifyTopology)
7567 return -4; /* Prohibited operation */
7571 nc = IntGroupGetCount(where);
7573 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
7579 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
7580 || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
7581 __MoleculeUnlock(mp);
7582 Panic("Low memory while adding angles");
7585 mp->needsMDRebuild = 1;
7586 __MoleculeUnlock(mp);
7591 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
7594 if (mp == NULL || where == NULL)
7596 if (mp->noModifyTopology)
7597 return -4; /* Prohibited operation */
7599 if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
7600 __MoleculeUnlock(mp);
7601 Panic("Low memory while adding angles");
7603 mp->nangles -= (nc = IntGroupGetCount(where));
7604 mp->needsMDRebuild = 1;
7605 __MoleculeUnlock(mp);
7610 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
7613 if (mp == NULL || dihedrals == NULL)
7615 if (mp->noModifyTopology)
7616 return -4; /* Prohibited operation */
7618 nc = IntGroupGetCount(where);
7620 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
7626 n1 = mp->ndihedrals;
7628 if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
7629 || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
7630 __MoleculeUnlock(mp);
7631 Panic("Low memory while adding dihedrals");
7633 mp->needsMDRebuild = 1;
7634 __MoleculeUnlock(mp);
7639 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
7642 if (mp == NULL || where == NULL)
7644 if (mp->noModifyTopology)
7645 return -4; /* Prohibited operation */
7647 if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
7648 __MoleculeUnlock(mp);
7649 Panic("Low memory while adding dihedrals");
7651 mp->ndihedrals -= (nc = IntGroupGetCount(where));
7652 mp->needsMDRebuild = 1;
7653 __MoleculeUnlock(mp);
7658 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
7661 if (mp == NULL || impropers == NULL)
7663 if (mp->noModifyTopology)
7664 return -4; /* Prohibited operation */
7666 nc = IntGroupGetCount(where);
7668 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
7674 n1 = mp->nimpropers;
7676 if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
7677 || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
7678 __MoleculeUnlock(mp);
7679 Panic("Low memory while adding impropers");
7681 mp->needsMDRebuild = 1;
7682 __MoleculeUnlock(mp);
7687 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
7690 if (mp == NULL || where == NULL)
7692 if (mp->noModifyTopology)
7693 return -4; /* Prohibited operation */
7695 if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
7696 __MoleculeUnlock(mp);
7697 Panic("Low memory while adding impropers");
7699 mp->nimpropers -= (nc = IntGroupGetCount(where));
7700 __MoleculeUnlock(mp);
7705 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
7708 if (mp == NULL || mp->bonds == NULL)
7710 for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
7711 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
7718 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
7721 if (mp == NULL || mp->angles == NULL)
7723 for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
7724 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
7725 (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
7732 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
7735 if (mp == NULL || mp->dihedrals == NULL)
7737 for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
7738 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
7739 (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
7746 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
7749 if (mp == NULL || mp->impropers == NULL)
7751 for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
7754 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
7755 (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
7756 (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
7762 /* Remove the bond at bondIndex and create two dummy atoms instead.
7763 The dummy atoms are placed at the end of atoms[], and the residue
7764 numbers are the same as the root atoms (i.e. the atoms to which
7765 the dummy atoms are connected). The indices are returned in
7766 dummyIndices[0,1]. */
7768 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
7770 Int roots[3], newBonds[5];
7775 if (mp == NULL || mp->noModifyTopology)
7777 if (bondIndex < 0 || bondIndex >= mp->nbonds)
7779 roots[0] = mp->bonds[bondIndex * 2];
7780 roots[1] = mp->bonds[bondIndex * 2 + 1];
7781 roots[2] = kInvalidIndex;
7782 rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
7783 rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
7784 VecSub(dr, rootp[0]->r, rootp[1]->r);
7785 for (i = 0; i < 2; i++) {
7788 memmove(nap, rootp[i], sizeof(na));
7789 nap->aname[0] = '*';
7790 strcpy(nap->element, "Du");
7792 nap->charge = nap->weight = 0.0;
7793 nap->atomicNumber = 0;
7795 w = (i == 0 ? 0.4 : -0.4);
7796 VecScaleInc(nap->r, dr, w);
7803 /* Expand atoms array and append the dummy atoms at the end */
7805 natoms = mp->natoms;
7806 if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
7808 memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
7809 dummyIndices[0] = natoms;
7810 dummyIndices[1] = natoms + 1;
7812 /* Remove the old bond and create new bonds */
7813 /* ig = IntGroupNewWithPoints(bondIndex, 1, -1);
7816 MoleculeDeleteBonds(mp, NULL, ig);
7817 IntGroupRelease(ig); */
7818 MoleculeDeleteBonds(mp, 1, roots);
7819 newBonds[0] = roots[0];
7820 newBonds[1] = dummyIndices[0];
7821 newBonds[2] = roots[1];
7822 newBonds[3] = dummyIndices[1];
7823 newBonds[4] = kInvalidIndex;
7825 i = (MoleculeAddBonds(mp, 2, newBonds) < 0 ? -1 : 0);
7826 mp->needsMDRebuild = 1;
7827 __MoleculeUnlock(mp);
7831 __MoleculeUnlock(mp);
7832 Panic("Low memory during creating dummy atoms");
7836 /* Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
7837 a bond between the two root atoms. The value bondIndex is used as a
7838 hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
7839 the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
7840 is ignored and the new bond is stored at the end of bonds[]. */
7842 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
7849 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
7852 if (mol == NULL || mol->noModifyTopology)
7859 __MoleculeLock(mol);
7860 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
7861 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
7862 mol->needsMDRebuild = 1;
7863 __MoleculeUnlock(mol);
7870 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
7873 if (mol == NULL || mol->noModifyTopology)
7875 n1 = mol->ndihedrals;
7876 np1 = mol->dihedrals;
7877 mol->ndihedrals = 0;
7878 mol->dihedrals = NULL;
7879 if (ndihedrals > 0) {
7880 __MoleculeLock(mol);
7881 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
7882 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
7883 mol->needsMDRebuild = 1;
7884 __MoleculeUnlock(mol);
7886 *outDihedrals = np1;
7891 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
7894 if (mol == NULL || mol->noModifyTopology)
7896 n1 = mol->nimpropers;
7897 np1 = mol->impropers;
7898 mol->nimpropers = 0;
7899 mol->impropers = NULL;
7900 if (nimpropers > 0) {
7901 __MoleculeLock(mol);
7902 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
7903 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
7904 mol->needsMDRebuild = 1;
7905 __MoleculeUnlock(mol);
7907 *outImpropers = np1;
7913 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
7920 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
7921 return 0; /* molecule is empty */
7922 if (mol->noModifyTopology)
7926 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7927 Int *cp = AtomConnects(ap);
7928 for (j = 0; j < ap->nconnects; j++) {
7930 for (k = j + 1; k < ap->nconnects; k++) {
7932 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
7933 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
7942 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
7946 if (outAngles != NULL)
7947 *outAngles = angles;
7952 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
7954 Int n1, n2, n3, n4, *ip, *cp2, *cp3;
7959 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
7960 return 0; /* molecule is empty */
7963 for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
7964 Int i1, i3, i4, *ip;
7965 cp2 = AtomConnects(ap2);
7966 for (i3 = 0; i3 < ap2->nconnects; i3++) {
7970 ap3 = ATOM_AT_INDEX(mol->atoms, n3);
7971 cp3 = AtomConnects(ap3);
7972 for (i1 = 0; i1 < ap2->nconnects; i1++) {
7976 for (i4 = 0; i4 < ap3->nconnects; i4++) {
7978 if (n2 == n4 || n1 == n4)
7980 if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
7981 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
7991 if (ndihedrals > 0) {
7992 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
7996 if (outDihedrals != NULL)
7997 *outDihedrals = dihedrals;
8002 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
8004 Int n1, n2, n3, n4, t1, t2, t3, t4, *ip, *cp;
8005 Parameter *par = mol->par;
8010 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8011 return 0; /* molecule is empty */
8012 if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
8013 return 0; /* No improper parameters are defined */
8017 for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
8018 Int i1, i2, i4, found, *ip;
8020 cp = AtomConnects(ap3);
8021 for (i1 = 0; i1 < ap3->nconnects; i1++) {
8023 t1 = ATOM_AT_INDEX(ap, n1)->type;
8024 for (i2 = i1 + 1; i2 < ap3->nconnects; i2++) {
8026 t2 = ATOM_AT_INDEX(ap, n2)->type;
8027 for (i4 = i2 + 1; i4 < ap3->nconnects; i4++) {
8029 t4 = ATOM_AT_INDEX(ap, n4)->type;
8031 if (ParameterLookupImproperPar(par, t1, t2, t3, t4, n1, n2, n3, n4, 0) != NULL)
8033 else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, -1, -1, -1, -1, 0) != NULL)
8035 if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
8036 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8046 if (nimpropers > 0) {
8047 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8051 if (outImpropers != NULL)
8052 *outImpropers = impropers;
8056 #pragma mark ====== Residues ======
8059 MoleculeCleanUpResidueTable(Molecule *mp)
8063 if (mp == NULL || mp->natoms == 0)
8067 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8068 if (ap->resSeq >= maxres)
8069 maxres = ap->resSeq + 1;
8070 if (ap->resSeq < mp->nresidues) {
8071 if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
8072 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8074 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
8077 if (maxres < mp->nresidues)
8078 mp->nresidues = maxres;
8079 __MoleculeUnlock(mp);
8082 /* Change the number of residues. If nresidues is greater than the current value,
8083 then the array mp->residues is expanded with null names. If nresidues is smaller
8084 than the current value, mp->nresidues is set to the smallest possible value
8085 that is no smaller than nresidues and larger than any of the resSeq values. */
8087 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
8092 if (mp->nresidues == nresidues)
8094 else if (mp->nresidues < nresidues) {
8097 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
8098 while (n < nresidues)
8099 mp->residues[n++][0] = 0;
8100 __MoleculeUnlock(mp);
8106 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8107 if (ap->resSeq >= n)
8116 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
8118 IntGroupIterator iter;
8119 int withArray, resSeq, maxSeq;
8123 /* If LSB of resSeqs is 1, then a constant value is used for all specified atoms */
8124 if (((int)resSeqs & 1) == 0) {
8129 resSeq = ((int)resSeqs - 1) / 2;
8132 IntGroupIteratorInit(group, &iter);
8134 /* Change resSeqs */
8138 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8139 ap = ATOM_AT_INDEX(mp->atoms, i);
8141 resSeq = resSeqs[j++];
8142 if (resSeq > maxSeq)
8144 ap->resSeq = resSeq;
8146 __MoleculeUnlock(mp);
8148 /* Expand array if necessary */
8149 if (maxSeq >= mp->nresidues)
8150 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8152 /* Synchronize resName and residues[] */
8154 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8155 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8157 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8159 IntGroupIteratorRelease(&iter);
8160 __MoleculeUnlock(mp);
8162 MoleculeIncrementModifyCount(mp);
8168 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
8170 return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(resSeq * 2 + 1));
8173 /* Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
8174 specifies the mp->nresidues after modifying the residue numbers.
8175 If all atoms are modified, then the table of residue names is also shifted. Otherwise,
8176 the table of residue names is not touched. */
8178 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
8180 int i, maxSeq, nmodatoms;
8182 IntGroupIterator iter;
8183 IntGroupIteratorInit(group, &iter);
8186 nresidues = mp->nresidues;
8189 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8190 ap = ATOM_AT_INDEX(mp->atoms, i);
8191 ap->resSeq += offset;
8192 if (ap->resSeq < 0) {
8193 /* Bad argument; undo change and returns this index + 1 */
8195 ap->resSeq -= offset;
8196 while ((i = IntGroupIteratorLast(&iter)) >= 0) {
8197 ap = ATOM_AT_INDEX(mp->atoms, i);
8198 ap->resSeq -= offset;
8200 IntGroupIteratorRelease(&iter);
8201 return bad_index + 1;
8203 if (ap->resSeq > maxSeq)
8204 maxSeq = ap->resSeq;
8207 if (maxSeq >= nresidues)
8208 nresidues = maxSeq + 1;
8209 if (offset < 0 && nmodatoms == mp->natoms) {
8210 /* Shift the residue names downward */
8211 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
8213 __MoleculeUnlock(mp);
8214 MoleculeChangeNumberOfResidues(mp, nresidues);
8215 if (offset > 0 && nmodatoms == mp->natoms) {
8216 /* Shift the residue names upward */
8218 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
8219 __MoleculeUnlock(mp);
8221 IntGroupIteratorRelease(&iter);
8223 MoleculeIncrementModifyCount(mp);
8228 /* Change residue names for the specified residue numbers. Names is an array of
8229 chars containing argc*4 characters, and every 4 characters represent a
8230 residue name; characters '\x01'-'\x1f' are converted to '\0', which allow
8231 names to be handled as a C string. */
8233 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
8238 for (i = 0; i < argc; i++) {
8239 if (maxSeq < resSeqs[i])
8240 maxSeq = resSeqs[i];
8242 if (maxSeq >= mp->nresidues)
8243 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8245 for (i = 0; i < argc; i++) {
8246 char *p = mp->residues[resSeqs[i]];
8248 strncpy(p, names + i * 4, 4);
8249 for (j = 0; j < 4; j++) {
8250 if (p[j] >= 0 && p[j] < 0x20)
8254 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8255 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8257 __MoleculeUnlock(mp);
8259 MoleculeIncrementModifyCount(mp);
8264 /* Returns the maximum residue number actually used */
8266 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
8271 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8272 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8274 if (ap->resSeq > maxSeq)
8275 maxSeq = ap->resSeq;
8280 /* Returns the minimum residue number actually used */
8282 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
8286 minSeq = ATOMS_MAX_NUMBER;
8287 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8288 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8290 if (ap->resSeq < minSeq)
8291 minSeq = ap->resSeq;
8293 return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
8296 #pragma mark ====== Sort by Residues ======
8299 sAtomSortComparator(const void *a, const void *b)
8301 const Atom *ap, *bp;
8302 ap = *((const Atom **)a);
8303 bp = *((const Atom **)b);
8304 if (ap->resSeq == bp->resSeq) {
8305 /* Retain the original order (i.e. atom with larger pointer address is larger) */
8312 /* Compare the residue sequence. However, residue sequence 0 is always larger. */
8313 if (ap->resSeq == 0)
8315 else if (bp->resSeq == 0)
8317 else if (ap->resSeq < bp->resSeq)
8319 else if (ap->resSeq > bp->resSeq)
8326 sMoleculeReorder(Molecule *mp)
8328 int i, res, prevRes;
8332 if (mp == NULL || mp->natoms <= 1)
8335 /* Sort the atoms, bonds, etc. */
8336 apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
8337 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
8338 newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
8339 if (apArray == NULL || old2new == NULL || newAtoms == NULL)
8340 Panic("Low memory during reordering atoms");
8341 for (i = 0; i < mp->natoms; i++)
8342 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
8344 /* Sort the atoms. Note: apArray is an array of "Pointer to Atom" */
8345 qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
8347 /* Make a table of 'which atom becomes which' */
8348 for (i = 0; i < mp->natoms; i++) {
8349 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
8353 /* Renumber the bonds, etc. */
8354 for (i = 0; i < mp->nbonds * 2; i++) {
8355 mp->bonds[i] = old2new[mp->bonds[i]];
8357 for (i = 0; i < mp->nangles * 3; i++) {
8358 mp->angles[i] = old2new[mp->angles[i]];
8360 for (i = 0; i < mp->ndihedrals * 4; i++) {
8361 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
8363 for (i = 0; i < mp->nimpropers * 4; i++) {
8364 mp->impropers[i] = old2new[mp->impropers[i]];
8366 for (i = 0; i < mp->natoms; i++) {
8368 ip = AtomConnects(apArray[i]);
8369 for (j = 0; j < apArray[i]->nconnects; j++, ip++)
8373 /* Renumber the residues so that the residue numbers are contiguous */
8375 for (i = 0; i < mp->natoms; i++) {
8376 if (apArray[i]->resSeq == 0)
8378 if (apArray[i]->resSeq != prevRes) {
8380 prevRes = apArray[i]->resSeq;
8381 if (prevRes != res) {
8382 strncpy(mp->residues[res], mp->residues[prevRes], 4);
8385 apArray[i]->resSeq = res;
8387 mp->nresidues = res + 1;
8389 /* Sort the atoms and copy back to atoms[] */
8390 for (i = 0; i < mp->natoms; i++) {
8391 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
8393 memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
8395 /* Free the locally allocated storage */
8402 /* Renumber atoms */
8404 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
8406 Int *old2new, i, j, retval;
8410 if (mp->noModifyTopology)
8412 if (old2new_out != NULL)
8413 old2new = old2new_out;
8415 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
8416 saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
8417 if (old2new == NULL || saveAtoms == NULL)
8418 Panic("Low memory during reordering atoms");
8419 memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
8421 for (i = 0; i < mp->natoms; i++)
8423 for (i = 0; i < isize && i < mp->natoms; i++) {
8425 if (j < 0 || j >= mp->natoms) {
8426 retval = 1; /* Out of range */
8429 if (old2new[j] != -1) {
8430 retval = 2; /* Duplicate entry */
8435 if (i < mp->natoms) {
8436 for (j = 0; j < mp->natoms; j++) {
8437 if (old2new[j] != -1)
8442 if (i != mp->natoms) {
8443 retval = 3; /* Internal inconsistency */
8447 /* Renumber the bonds, etc. */
8448 for (i = 0; i < mp->nbonds * 2; i++) {
8449 mp->bonds[i] = old2new[mp->bonds[i]];
8451 for (i = 0; i < mp->nangles * 3; i++) {
8452 mp->angles[i] = old2new[mp->angles[i]];
8454 for (i = 0; i < mp->ndihedrals * 4; i++) {
8455 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
8457 for (i = 0; i < mp->nimpropers * 4; i++) {
8458 mp->impropers[i] = old2new[mp->impropers[i]];
8460 for (i = 0; i < mp->natoms; i++) {
8461 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
8462 Int *ip = AtomConnects(ap);
8463 for (j = 0; j < ap->nconnects; j++, ip++)
8466 if (mp->par != NULL) {
8467 /* Renumber the parameters */
8469 for (j = kFirstParType; j <= kLastParType; j++) {
8470 n = ParameterGetCountForType(mp->par, j);
8471 for (i = 0; i < n; i++) {
8472 UnionPar *up = ParameterGetUnionParFromTypeAndIndex(mp->par, j, i);
8474 ParameterRenumberAtoms(j, up, mp->natoms, old2new);
8479 /* Renumber the atoms */
8480 for (i = 0; i < mp->natoms; i++)
8481 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
8484 MoleculeIncrementModifyCount(mp);
8485 mp->needsMDRebuild = 1;
8488 __MoleculeUnlock(mp);
8490 if (old2new_out == NULL)
8495 #pragma mark ====== Coordinate Transform ======
8498 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
8503 Transform rtr, symtr;
8504 if (mp == NULL || tr == NULL)
8506 TransformInvert(rtr, tr);
8508 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8509 if (group == NULL || IntGroupLookup(group, i, NULL) != 0) {
8510 TransformVec(&ap->r, tr, &ap->r);
8511 if (!SYMOP_ALIVE(ap->symop))
8513 /* Transform symop */
8514 if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
8516 TransformMul(symtr, tr, symtr);
8517 if (group == NULL || IntGroupLookup(group, ap->symbase, NULL) != 0)
8518 TransformMul(symtr, symtr, rtr);
8520 if (!SYMOP_ALIVE(ap->symop))
8522 /* Transform symop if the base atom is transformed */
8523 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
8525 if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
8527 TransformMul(symtr, symtr, rtr);
8529 if (MoleculeGetSymopForTransform(mp, symtr, &new_symop, 1) != 0)
8531 ap->symop = new_symop;
8533 mp->needsMDCopyCoordinates = 1;
8534 __MoleculeUnlock(mp);
8535 sMoleculeNotifyChangeAppearance(mp);
8540 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
8544 if (mp == NULL || tr == NULL)
8547 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8548 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8550 TransformVec(&ap->r, tr, &ap->r);
8552 mp->needsMDCopyCoordinates = 1;
8553 __MoleculeUnlock(mp);
8554 sMoleculeNotifyChangeAppearance(mp);
8559 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
8562 if (mp == NULL || vp == NULL)
8564 memset(tr, 0, sizeof(tr));
8565 tr[0] = tr[4] = tr[8] = 1.0;
8569 MoleculeTransform(mp, tr, group);
8573 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
8576 TransformForRotation(tr, axis, angle, center);
8577 MoleculeTransform(mp, tr, group);
8581 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
8586 if (mp == NULL || center == NULL)
8588 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
8589 return 2; /* Empty molecule */
8591 center->x = center->y = center->z = 0.0;
8592 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8593 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8595 VecScaleInc(*center, ap->r, ap->weight);
8599 return 3; /* Atomic weights are not defined? */
8601 VecScaleSelf(*center, w);
8606 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
8613 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
8614 return 2; /* Empty molecule */
8615 vmin.x = vmin.y = vmin.z = 1e50;
8616 vmax.x = vmax.y = vmax.z = -1e50;
8617 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8618 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8620 if (vmin.x > ap->r.x)
8622 if (vmin.y > ap->r.y)
8624 if (vmin.z > ap->r.z)
8626 if (vmax.x < ap->r.x)
8628 if (vmax.y < ap->r.y)
8630 if (vmax.z < ap->r.z)
8640 #pragma mark ====== Measurements ======
8643 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
8646 /* if (mp->is_xtal_coord) {
8647 TransformVec(&r1, mp->cell->tr, vp1);
8648 TransformVec(&r2, mp->cell->tr, vp2);
8654 return VecLength(r1);
8658 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
8662 /* if (mp->is_xtal_coord) {
8663 TransformVec(&r1, mp->cell->tr, vp1);
8664 TransformVec(&r2, mp->cell->tr, vp2);
8665 TransformVec(&r3, mp->cell->tr, vp3);
8673 w = VecLength(r1) * VecLength(r3);
8676 return acos(VecDot(r1, r3) / w) * kRad2Deg;
8680 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
8682 Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
8684 /* if (mp->is_xtal_coord) {
8685 TransformVec(&r1, mp->cell->tr, vp1);
8686 TransformVec(&r2, mp->cell->tr, vp2);
8687 TransformVec(&r3, mp->cell->tr, vp3);
8688 TransformVec(&r4, mp->cell->tr, vp4);
8695 VecSub(r21, r1, r2);
8696 VecSub(r32, r2, r3);
8697 VecSub(r43, r3, r4);
8698 VecCross(v1, r21, r32);
8699 VecCross(v2, r32, r43);
8700 VecCross(v3, r32, v1);
8704 if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
8710 VecScaleSelf(v1, w1);
8711 VecScaleSelf(v2, w2);
8712 VecScaleSelf(v3, w3);
8713 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
8717 #pragma mark ====== XtalCell Parameters ======
8720 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
8722 if (mp->cell != NULL) {
8723 TransformVec(dst, mp->cell->tr, src);
8728 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
8730 if (mp->cell != NULL) {
8731 TransformVec(dst, mp->cell->rtr, src);
8736 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
8738 static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
8740 Vector *vp1, *vp2, *vp3;
8745 for (n1 = 0; n1 < 3; n1++) {
8746 if (cp->flags[n1] != 0)
8750 /* All directions are non-periodic */
8751 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
8752 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
8756 vp1 = &(cp->axes[n1]);
8757 vp2 = &(cp->axes[n2]);
8758 vp3 = &(cp->axes[n3]);
8759 cp->tr[n1*3] = vp1->x;
8760 cp->tr[n1*3+1] = vp1->y;
8761 cp->tr[n1*3+2] = vp1->z;
8762 cp->tr[9] = cp->origin.x;
8763 cp->tr[10] = cp->origin.y;
8764 cp->tr[11] = cp->origin.z;
8765 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
8766 /* 1-dimensional or 2-dimensional system */
8767 /* Create "dummy" axes, so that transforms between internal and cartesian coordinates are
8768 possible with a single matrix */
8769 if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
8771 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
8772 VecCross(v1, *vp1, xvec);
8773 VecCross(v2, *vp1, yvec);
8774 if (VecLength2(v1) < VecLength2(v2))
8776 VecCross(v2, *vp1, v1);
8777 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
8778 return -1; /* Non-regular transform */
8779 } else if (cp->flags[n2] == 0) {
8781 VecCross(v1, v2, *vp1);
8782 if (NormalizeVec(&v1, &v1))
8783 return -1; /* Non-regular transform */
8786 VecCross(v2, *vp1, v1);
8787 if (NormalizeVec(&v2, &v2))
8788 return -1; /* Non-regular transform */
8790 cp->tr[n2*3] = v1.x;
8791 cp->tr[n2*3+1] = v1.y;
8792 cp->tr[n2*3+2] = v1.z;
8793 cp->tr[n3*3] = v2.x;
8794 cp->tr[n3*3+1] = v2.y;
8795 cp->tr[n3*3+2] = v2.z;
8797 VecCross(v1, *vp1, *vp2);
8798 if (fabs(VecDot(v1, *vp3)) < 1e-7)
8799 return -1; /* Non-regular transform */
8800 cp->tr[n2*3] = vp2->x;
8801 cp->tr[n2*3+1] = vp2->y;
8802 cp->tr[n2*3+2] = vp2->z;
8803 cp->tr[n3*3] = vp3->x;
8804 cp->tr[n3*3+1] = vp3->y;
8805 cp->tr[n3*3+2] = vp3->z;
8808 if (TransformInvert(cp->rtr, cp->tr))
8809 return -1; /* Non-regular transform */
8811 /* Calculate the reciprocal cell parameters */
8812 cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[1] * cp->rtr[1] + cp->rtr[2] * cp->rtr[2]);
8813 cp->rcell[1] = sqrt(cp->rtr[3] * cp->rtr[3] + cp->rtr[4] * cp->rtr[4] + cp->rtr[5] * cp->rtr[5]);
8814 cp->rcell[2] = sqrt(cp->rtr[6] * cp->rtr[6] + cp->rtr[7] * cp->rtr[7] + cp->rtr[8] * cp->rtr[8]);
8815 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;
8816 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;
8817 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;
8820 /* Calculate a, b, c, alpha, beta, gamma */
8821 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[1] * cp->tr[1] + cp->tr[2] * cp->tr[2]);
8822 cp->cell[1] = sqrt(cp->tr[3] * cp->tr[3] + cp->tr[4] * cp->tr[4] + cp->tr[5] * cp->tr[5]);
8823 cp->cell[2] = sqrt(cp->tr[6] * cp->tr[6] + cp->tr[7] * cp->tr[7] + cp->tr[8] * cp->tr[8]);
8824 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;
8825 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;
8826 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;
8833 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
8842 memset(&cmat, 0, sizeof(Transform));
8844 if (mp->cell != NULL) {
8845 memmove(&cmat, &(mp->cell->tr), sizeof(Transform));
8848 cmat[0] = cmat[4] = cmat[8] = 1.0;
8851 /* mp->is_xtal_coord = 0; */
8855 cp = (XtalCell *)calloc(sizeof(XtalCell), 1);
8857 Panic("Low memory during setting cell parameters");
8859 cmat[0] = cmat[4] = cmat[8] = 1.0;
8861 /* if (mp->is_xtal_coord)
8862 memmove(&cmat, &(cp->tr), sizeof(Transform)); */
8864 /* mp->is_xtal_coord = 1; */
8865 /* alpha, beta, gamma are in degree */
8869 cp->cell[3] = alpha;
8871 cp->cell[5] = gamma;
8872 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
8873 /* c unique (hexagonal etc.) */
8874 Double cosa, cosb, sinb, cosg;
8875 cosa = cos(alpha * kDeg2Rad);
8876 cosb = cos(beta * kDeg2Rad);
8877 sinb = sin(beta * kDeg2Rad);
8878 cosg = cos(gamma * kDeg2Rad);
8879 cp->axes[0].x = a * sinb;
8881 cp->axes[0].z = a * cosb;
8882 cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
8883 cp->axes[1].z = b * cosa;
8884 cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
8890 Double cosg, sing, cosa, cosb;
8891 cosa = cos(alpha * kDeg2Rad);
8892 cosb = cos(beta * kDeg2Rad);
8893 cosg = cos(gamma * kDeg2Rad);
8894 sing = sin(gamma * kDeg2Rad);
8895 cp->axes[0].x = a * sing;
8896 cp->axes[0].y = a * cosg;
8901 cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
8902 cp->axes[2].y = c * cosa;
8903 cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
8905 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
8906 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
8907 MoleculeCalculateCellFromAxes(cp, 0);
8908 TransformMul(cmat, cp->rtr, cmat);
8911 /* Update the coordinates (if requested) */
8912 if (convertCoordinates) {
8913 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8914 TransformVec(&(ap->r), cmat, &(ap->r));
8918 /* Update the anisotropic parameters */
8919 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8920 Aniso *anp = ap->aniso;
8922 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
8925 __MoleculeUnlock(mp);
8926 sMoleculeNotifyChangeAppearance(mp);
8930 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x13, Double x23, const Double *sigmaptr)
8934 const Double log2 = 0.693147180559945;
8935 const Double pi22 = 19.7392088021787; /* 2*pi**2 */
8941 if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
8943 anp = mp->atoms[n1].aniso;
8946 anp = (Aniso *)calloc(sizeof(Aniso), 1);
8948 __MoleculeUnlock(mp);
8949 Panic("Low memory during setting anisotropic atom parameters");
8951 mp->atoms[n1].aniso = anp;
8954 case 1: d = 1; dx = 0.5; break;
8955 case 2: d = log2; dx = log2; break;
8956 case 3: d = log2; dx = log2 * 0.5; break;
8957 case 4: u = 1; d = 0.25; dx = 0.25; break;
8958 case 5: u = 1; d = 0.25; dx = 0.125; break;
8959 case 8: u = 1; d = pi22; dx = pi22; break;
8960 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
8961 case 10: d = pi22; dx = pi22; break;
8962 default: d = dx = 1; break;
8964 anp->bij[0] = x11 * d;
8965 anp->bij[1] = x22 * d;
8966 anp->bij[2] = x33 * d;
8967 anp->bij[3] = x12 * dx;
8968 anp->bij[4] = x13 * dx;
8969 anp->bij[5] = x23 * dx;
8970 if (sigmaptr != NULL) {
8972 anp->bsig[0] = sigmaptr[0] * d;
8973 anp->bsig[1] = sigmaptr[1] * d;
8974 anp->bsig[2] = sigmaptr[2] * d;
8975 anp->bsig[3] = sigmaptr[3] * dx;
8976 anp->bsig[4] = sigmaptr[4] * dx;
8977 anp->bsig[5] = sigmaptr[5] * dx;
8980 anp->bsig[0] = anp->bsig[1] = anp->bsig[2] = anp->bsig[3] = anp->bsig[4] = anp->bsig[5] = 0.0;
8983 if (cp != NULL && u == 1) {
8984 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
8985 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
8986 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
8987 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
8988 anp->bij[4] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[3] * kDeg2Rad); */
8989 anp->bij[5] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[4] * kDeg2Rad); */
8990 if (sigmaptr != NULL) {
8991 anp->bsig[0] *= cp->rcell[0] * cp->rcell[0];
8992 anp->bsig[1] *= cp->rcell[1] * cp->rcell[1];
8993 anp->bsig[2] *= cp->rcell[2] * cp->rcell[2];
8994 anp->bsig[3] *= cp->rcell[0] * cp->rcell[1];
8995 anp->bsig[4] *= cp->rcell[2] * cp->rcell[0];
8996 anp->bsig[5] *= cp->rcell[1] * cp->rcell[2];
9000 /* Calculate the principal axes (in Cartesian coordinates) */
9001 /* The principal axes are the eigenvectors of matrix At(B^-1)A, where
9002 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
9003 in which x and z are the crystal-space and cartesian coordinates. */
9004 m1[0] = anp->bij[0] / pi22;
9005 m1[4] = anp->bij[1] / pi22;
9006 m1[8] = anp->bij[2] / pi22;
9007 m1[1] = m1[3] = anp->bij[3] / pi22;
9008 m1[2] = m1[6] = anp->bij[4] / pi22;
9009 m1[5] = m1[7] = anp->bij[5] / pi22;
9010 MatrixInvert(m1, m1);
9012 memmove(m2, cp->rtr, sizeof(Mat33));
9013 MatrixMul(m1, m1, m2);
9014 MatrixTranspose(m2, m2);
9015 MatrixMul(m1, m2, m1);
9017 MatrixSymDiagonalize(m1, val, axis);
9018 for (u = 0; u < 3; u++) {
9020 fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
9023 val[u] = 1 / sqrt(val[u]);
9025 anp->pmat[u*3] = axis[u].x * val[u];
9026 anp->pmat[u*3+1] = axis[u].y * val[u];
9027 anp->pmat[u*3+2] = axis[u].z * val[u];
9029 __MoleculeUnlock(mp);
9033 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic)
9035 static Vector zeroVec = {0, 0, 0};
9036 /* static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}; */
9042 if (mp->cell != NULL)
9045 /* mp->is_xtal_coord = 0; */
9048 memset(&b, 0, sizeof(b));
9049 b.axes[0] = (ax != NULL ? *ax : zeroVec);
9050 b.axes[1] = (ay != NULL ? *ay : zeroVec);
9051 b.axes[2] = (az != NULL ? *az : zeroVec);
9053 memmove(b.flags, periodic, 3);
9054 if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
9057 if (mp->cell != NULL) {
9058 if (mp->cell->has_sigma) {
9059 /* Keep the sigma */
9061 memmove(b.cellsigma, mp->cell->cellsigma, sizeof(mp->cell->cellsigma));
9065 mp->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
9066 if (mp->cell != NULL) {
9067 memmove(mp->cell, &b, sizeof(XtalCell));
9069 } else n = -2; /* Out of memory */
9070 __MoleculeUnlock(mp);
9074 #pragma mark ====== Fragment manipulation ======
9077 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
9081 if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
9083 IntGroupAdd(result, idx, 1);
9084 ap = ATOM_AT_INDEX(mp->atoms, idx);
9085 cp = AtomConnects(ap);
9086 for (i = 0; i < ap->nconnects; i++) {
9088 if (IntGroupLookup(result, idx2, NULL))
9090 sMoleculeFragmentSub(mp, idx2, result, exatoms);
9094 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
9095 not containing the atoms in exatoms */
9097 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
9100 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9102 result = IntGroupNew();
9103 sMoleculeFragmentSub(mp, n1, result, exatoms);
9107 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
9108 not containing the atoms n2, n3, ... (terminated by -1) */
9110 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
9113 IntGroup *exatoms, *result;
9114 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9116 exatoms = IntGroupNew();
9117 for (i = 0; i < argc; i++)
9118 IntGroupAdd(exatoms, argv[i], 1);
9119 result = IntGroupNew();
9120 sMoleculeFragmentSub(mp, n1, result, exatoms);
9121 IntGroupRelease(exatoms);
9125 /* The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
9126 not containing the atoms in exatoms */
9128 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
9130 IntGroupIterator iter;
9133 if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
9135 IntGroupIteratorInit(inatoms, &iter);
9136 result = IntGroupNew();
9137 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9138 sMoleculeFragmentSub(mp, i, result, exatoms);
9140 IntGroupIteratorRelease(&iter);
9144 /* Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
9145 group is bound to the rest of the molecule via only one bond.
9146 If the result is true, then the atoms belonging to the (only) bond are returned
9147 in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
9148 and n2 can be NULL, if those informations are not needed. */
9150 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
9152 Int i, i1, i2, j, k, bond_count, nval1, nval2, *cp;
9154 if (mp == NULL || mp->natoms == 0 || group == NULL)
9155 return 0; /* Invalid arguments */
9157 for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
9158 i2 = IntGroupGetEndPoint(group, i);
9159 for (j = i1; j < i2; j++) {
9160 if (j < 0 || j >= mp->natoms)
9161 return 0; /* Invalid atom group */
9162 ap = ATOM_AT_INDEX(mp->atoms, j);
9163 cp = AtomConnects(ap);
9164 for (k = 0; k < ap->nconnects; k++) {
9165 if (IntGroupLookup(group, cp[k], NULL) == 0) {
9170 return 0; /* Too many bonds */
9175 if (bond_count == 1) {
9186 /* Returns non-zero if the given group is 'rotatable' in the molecule. The group
9187 is said to be 'rotatable' when either of the following conditions are met; (1)
9188 the group is detachable, or (2) the group consists of two bonded atoms that define
9189 a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
9190 (either a new IntGroup or 'group' with incremented reference count; thus the caller
9191 is responsible for releasing the returned value). */
9193 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
9196 if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
9197 if (rotGroup != NULL) {
9198 IntGroupRetain(group);
9203 if (group != NULL && IntGroupGetCount(group) == 2) {
9204 i1 = IntGroupGetNthPoint(group, 0);
9205 i2 = IntGroupGetNthPoint(group, 1);
9206 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
9207 IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
9210 i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
9212 IntGroupRelease(frag);
9213 if (rotGroup != NULL)
9217 if (rotGroup != NULL)
9219 else if (frag != NULL)
9220 IntGroupRelease(frag);
9227 #pragma mark ====== Multiple frame ======
9230 MoleculeGetNumberOfFrames(Molecule *mp)
9234 if (mp->nframes <= 0) {
9238 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9239 if (ap->nframes > n)
9250 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
9252 int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
9255 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
9258 n_old = MoleculeGetNumberOfFrames(mp);
9259 n_new = n_old + count;
9260 last_inserted = IntGroupGetNthPoint(group, count - 1);
9261 if (n_new <= last_inserted) {
9262 exframes = last_inserted - n_new + 1; /* number of extra frames that will be silently inserted */
9264 } else exframes = 0;
9266 tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4); /* "*4" for handling cells */
9272 /* Copy back the current coordinates */
9273 /* No change in the current coordinates, but the frame buffer is updated */
9274 MoleculeSelectFrame(mp, mp->cframe, 1);
9276 /* Expand ap->frames for all atoms */
9277 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9278 if (ap->frames == NULL)
9279 vp = (Vector *)calloc(sizeof(Vector), n_new);
9281 vp = (Vector *)realloc(ap->frames, sizeof(Vector) * n_new);
9283 __MoleculeUnlock(mp);
9286 for (j = ap->nframes; j < n_new; j++)
9290 if (mp->cell != NULL && (mp->frame_cells != NULL || inFrameCell != NULL)) {
9291 vp = mp->frame_cells;
9292 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, n_new - 1, NULL);
9294 /* Set the first cell parameters */
9295 mp->frame_cells[0] = mp->cell->axes[0];
9296 mp->frame_cells[1] = mp->cell->axes[1];
9297 mp->frame_cells[2] = mp->cell->axes[2];
9298 mp->frame_cells[3] = mp->cell->origin;
9300 /* vp = mp->frame_cells;
9301 if (mp->frame_cells == NULL) {
9302 vp = (Vector *)calloc(sizeof(Vector), n_new * 4);
9303 vp[0] = mp->cell->axes[0];
9304 vp[1] = mp->cell->axes[1];
9305 vp[2] = mp->cell->axes[2];
9306 vp[3] = mp->cell->origin;
9308 vp = (Vector *)realloc(mp->frame_cells, sizeof(Vector) * 4 * n_new);
9310 __MoleculeUnlock(mp);
9313 mp->frame_cells = vp; */
9316 /* group = [n0..n1-1, n2..n3-1, ...] */
9318 /* tempv[0..n0-1] <- ap[0..n0-1], s += n0,
9319 tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
9320 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
9321 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
9323 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
9324 At last, s will become n_old and t will become count. */
9325 for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9326 int s, t, ns, ne, mult;
9329 if (i == mp->natoms) {
9330 if (mp->cell == NULL || mp->frame_cells == NULL)
9332 vp = mp->frame_cells;
9339 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
9341 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
9344 ne = IntGroupGetEndPoint(group, j);
9346 if (i == mp->natoms) {
9347 if (inFrameCell != NULL) {
9348 tempv[ns * 4] = inFrameCell[t * 4];
9349 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
9350 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
9351 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
9353 tempv[ns * 4] = mp->cell->axes[0];
9354 tempv[ns * 4 + 1] = mp->cell->axes[1];
9355 tempv[ns * 4 + 2] = mp->cell->axes[2];
9356 tempv[ns * 4 + 3] = mp->cell->origin;
9359 if (inFrame != NULL)
9360 tempv[ns] = inFrame[natoms * t + i];
9369 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
9373 ap->nframes = n_new;
9374 memmove(vp, tempv, sizeof(Vector) * mult * n_new);
9377 mp->nframes = n_new;
9378 MoleculeSelectFrame(mp, last_inserted, 0);
9379 MoleculeIncrementModifyCount(mp);
9380 __MoleculeUnlock(mp);
9385 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
9387 int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
9390 IntGroup *group, *group2;
9392 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
9395 /* outFrame[] should have enough size for Vector * natoms * group.count */
9396 memset(outFrame, 0, sizeof(Vector) * natoms * count);
9397 if (mp->cell != NULL && mp->frame_cells != NULL)
9398 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
9400 n_old = MoleculeGetNumberOfFrames(mp);
9402 return -2; /* Cannot delete last frame */
9404 group = IntGroupNew();
9405 group2 = IntGroupNewWithPoints(0, n_old, -1);
9406 IntGroupIntersect(inGroup, group2, group);
9407 IntGroupRelease(group2);
9408 count = IntGroupGetCount(group);
9409 n_new = n_old - count;
9411 IntGroupRelease(group);
9412 return -2; /* Trying to delete too many frames */
9414 tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4); /* "*4" for handling cells */
9415 if (tempv == NULL) {
9416 IntGroupRelease(group);
9422 /* Copy back the current coordinates */
9423 /* No change in the current coordinates, but the frame buffer is updated */
9424 MoleculeSelectFrame(mp, mp->cframe, 1);
9426 /* Determine which frame should be selected after removal is completed */
9429 if (IntGroupLookup(group, mp->cframe, &i)) {
9430 /* cframe will be removed */
9431 n1 = IntGroupGetStartPoint(group, i) - 1;
9433 n1 = IntGroupGetEndPoint(group, i);
9434 } else n1 = mp->cframe;
9435 /* Change to that frame */
9436 MoleculeSelectFrame(mp, n1, 0);
9437 group2 = IntGroupNewFromIntGroup(group);
9438 IntGroupReverse(group2, 0, n_old);
9439 new_cframe = IntGroupLookupPoint(group2, n1);
9441 return -3; /* This cannot happen */
9442 IntGroupRelease(group2);
9445 /* group = [n0..n1-1, n2..n3-1, ...] */
9447 /* tempv[0..n0-1] -> ap[0..n0-1], s += n0,
9448 tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
9449 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
9450 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
9452 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
9453 At last, s will become n_new and t will become count. */
9455 for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9456 int s, t, j, ns, ne;
9458 /* if i == mp->natoms, mp->frame_cells is handled */
9459 if (i == mp->natoms) {
9460 if (mp->cell == NULL || mp->frame_cells == NULL)
9463 vp = mp->frame_cells;
9469 ap->frames = vp = (Vector *)calloc(sizeof(Vector), n_old);
9471 __MoleculeUnlock(mp);
9475 old_count = ap->nframes;
9478 /* Copy vp to tempv */
9479 memset(tempv, 0, sizeof(Vector) * mult * n_old);
9480 memmove(tempv, vp, sizeof(Vector) * mult * (old_count > n_old ? n_old : old_count));
9481 ne = ns = s = t = 0;
9482 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
9486 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (ns - ne));
9489 ne = IntGroupGetEndPoint(group, j);
9494 outFrame[natoms * t + i] = tempv[ns];
9495 else if (outFrameCell != NULL) {
9496 outFrameCell[t * 4] = tempv[ns * 4];
9497 outFrameCell[t * 4 + 1] = tempv[ns * 4 + 1];
9498 outFrameCell[t * 4 + 2] = tempv[ns * 4 + 2];
9499 outFrameCell[t * 4 + 3] = tempv[ns * 4 + 3];
9506 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (n_old - ne));
9514 if (i < mp->natoms) {
9519 free(mp->frame_cells);
9520 mp->frame_cells = NULL;
9524 ap->frames = (Vector *)realloc(ap->frames, sizeof(Vector) * s);
9526 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, s - 1, NULL);
9531 mp->nframes = nframes;
9533 /* Select the "last" frame; do not "copy back" the coordinates to the frame table */
9534 /* i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
9535 MoleculeSelectFrame(mp, new_cframe, 0);
9537 IntGroupRelease(group);
9539 MoleculeIncrementModifyCount(mp);
9540 __MoleculeUnlock(mp);
9545 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
9549 if (mp == NULL || mp->natoms == 0)
9551 cframe = mp->cframe;
9554 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9555 if (copyback && cframe >= 0 && cframe < ap->nframes) {
9556 /* Write the current coordinate back to the frame array */
9557 ap->frames[cframe] = ap->r;
9559 if (frame >= 0 && frame < ap->nframes) {
9560 /* Read the coordinate from the frame array */
9561 ap->r = ap->frames[frame];
9566 if (mp->cell != NULL && mp->frame_cells != NULL) {
9567 /* Write the current cell back to the frame_cells array */
9568 if (copyback && cframe >= 0) {
9569 Vector *vp = (Vector *)AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, cframe, NULL);
9570 vp[0] = mp->cell->axes[0];
9571 vp[1] = mp->cell->axes[1];
9572 vp[2] = mp->cell->axes[2];
9573 vp[3] = mp->cell->origin;
9575 /* Set the cell from the frame array */
9576 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);
9578 mp->needsMDCopyCoordinates = 1;
9579 __MoleculeUnlock(mp);
9582 sMoleculeNotifyChangeAppearance(mp);
9587 #pragma mark ====== MO calculation ======
9589 /* Calculate an MO value for a single point. */
9590 /* Index is the MO number (1-based) */
9591 /* tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom. */
9593 sCalcMOPoint(const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
9597 Double val, tval, *cnp, *tmpp, *mobasep, *mop;
9599 /* Cache dr and |dr|^2 */
9600 for (i = 0; i < bset->natoms; i++) {
9601 Vector r = bset->pos[i];
9602 tmp[i * 4] = r.x = vp->x - r.x;
9603 tmp[i * 4 + 1] = r.y = vp->y - r.y;
9604 tmp[i * 4 + 2] = r.z = vp->z - r.z;
9605 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
9607 /* Iterate over all shells */
9609 mobasep = bset->mo + (index - 1) * bset->ncomps;
9610 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
9611 pp = bset->priminfos + sp->p_idx;
9612 cnp = bset->cns + sp->cn_idx;
9613 tmpp = tmp + sp->a_idx * 4;
9614 mop = mobasep + sp->m_idx;
9618 for (j = 0; j < sp->nprim; j++) {
9619 tval += *cnp++ * exp(-pp->A * tmpp[3]);
9622 val += mop[0] * tval;
9628 for (j = 0; j < sp->nprim; j++) {
9629 tval = exp(-pp->A * tmpp[3]);
9635 x *= mop[0] * tmpp[0];
9636 y *= mop[1] * tmpp[1];
9637 z *= mop[2] * tmpp[2];
9644 for (j = 0; j < sp->nprim; j++) {
9645 tval = exp(-pp->A * tmpp[3]);
9653 x *= mop[1] * tmpp[0];
9654 y *= mop[2] * tmpp[1];
9655 z *= mop[3] * tmpp[2];
9656 val += t + x + y + z;
9660 Double xx, yy, zz, xy, xz, yz;
9661 xx = yy = zz = xy = xz = yz = 0;
9662 for (j = 0; j < sp->nprim; j++) {
9663 tval = exp(-pp->A * tmpp[3]);
9664 xx += *cnp++ * tval;
9665 yy += *cnp++ * tval;
9666 zz += *cnp++ * tval;
9667 xy += *cnp++ * tval;
9668 xz += *cnp++ * tval;
9669 yz += *cnp++ * tval;
9672 xx *= mop[0] * tmpp[0] * tmpp[0];
9673 yy *= mop[1] * tmpp[1] * tmpp[1];
9674 zz *= mop[2] * tmpp[2] * tmpp[2];
9675 xy *= mop[3] * tmpp[0] * tmpp[1];
9676 xz *= mop[4] * tmpp[0] * tmpp[2];
9677 yz *= mop[5] * tmpp[1] * tmpp[2];
9678 val += xx + yy + zz + xy + xz + yz;
9682 Double d0, d1p, d1n, d2p, d2n;
9683 d0 = d1p = d1n = d2p = d2n = 0;
9684 for (j = 0; j < sp->nprim; j++) {
9685 tval = exp(-pp->A * tmpp[3]);
9686 d0 += *cnp++ * tval;
9687 d1p += *cnp++ * tval;
9688 d1n += *cnp++ * tval;
9689 d2p += *cnp++ * tval;
9690 d2n += *cnp++ * tval;
9693 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
9694 d1p *= mop[1] * tmpp[0] * tmpp[2];
9695 d1n *= mop[2] * tmpp[1] * tmpp[2];
9696 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
9697 d2n *= mop[4] * tmpp[0] * tmpp[1];
9698 val += d0 + d1p + d1n + d2p + d2n;
9706 /* Calculate one MO. The input vectors should be in bohr unit (angstrom * 1.889725989 = kAngstrom2Bohr). */
9707 /* mono is the MO number (1-based) */
9709 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)
9711 int ix, iy, iz, n, nn;
9714 if (mp == NULL || mp->bset == NULL)
9716 if (mp->bset->cns == NULL) {
9717 if (sSetupGaussianCoefficients(mp->bset) != 0)
9720 cp = (Cube *)calloc(sizeof(Cube), 1);
9724 cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
9725 if (cp->dp == NULL) {
9738 /* TODO: use multithread */
9739 tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms * 4);
9746 for (ix = 0; ix < nx; ix++) {
9748 for (iy = 0; iy < ny; iy++) {
9749 for (iz = 0; iz < nz; iz++) {
9750 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
9751 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
9752 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
9753 cp->dp[n++] = sCalcMOPoint(mp->bset, mono, &p, tmp);
9755 if (callback != NULL && n - nn > 100) {
9757 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
9761 return -2; /* User interrupt */
9768 AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
9769 return mp->bset->ncubes - 1;
9773 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
9776 Vector rmin, rmax, *vp;
9777 Double dr, dx, dy, dz;
9778 if (mp == NULL || mp->bset == NULL || mp->bset->natoms == 0)
9782 rmin.x = rmin.y = rmin.z = 1e10;
9783 rmax.x = rmax.y = rmax.z = -1e10;
9784 for (i = 0, vp = mp->bset->pos; i < mp->bset->natoms; i++, vp++) {
9785 dr = RadiusForAtomicNumber(ATOM_AT_INDEX(mp->atoms, i)->atomicNumber);
9788 dr = dr * kAngstrom2Bohr * 3.0 + 2.0;
9789 if (rmin.x > vp->x - dr)
9790 rmin.x = vp->x - dr;
9791 if (rmin.y > vp->y - dr)
9792 rmin.y = vp->y - dr;
9793 if (rmin.z > vp->z - dr)
9794 rmin.z = vp->z - dr;
9795 if (rmax.x < vp->x + dr)
9796 rmax.x = vp->x + dr;
9797 if (rmax.y < vp->y + dr)
9798 rmax.y = vp->y + dr;
9799 if (rmax.z < vp->z + dr)
9800 rmax.z = vp->z + dr;
9802 dx = rmax.x - rmin.x;
9803 dy = rmax.y - rmin.y;
9804 dz = rmax.z - rmin.z;
9805 dr = pow(dx * dy * dz / npoints, 1.0/3.0);
9806 *nx = floor(dx / dr + 0.5);
9807 *ny = floor(dy / dr + 0.5);
9808 *nz = floor(dz / dr + 0.5);
9816 xp->x = yp->y = zp->z = dr;
9817 xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
9822 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
9824 if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
9826 return mp->bset->cubes[index];
9830 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
9833 if (mp == NULL || mp->bset == NULL)
9835 for (i = 0; i < mp->bset->ncubes; i++) {
9836 if (mp->bset->cubes[i]->idn == mono)
9843 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
9846 if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
9848 CubeRelease(mp->bset->cubes[index]);
9850 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
9851 if (--(mp->bset->ncubes) == 0) {
9852 free(mp->bset->cubes);
9853 mp->bset->cubes = NULL;
9855 return mp->bset->ncubes;
9859 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
9864 if (mp == NULL || mp->bset == NULL)
9865 return -1; /* Molecule or the basis set information is empty */
9866 cp = MoleculeGetCubeAtIndex(mp, index);
9868 return -2; /* MO not yet calculated */
9869 fp = fopen(fname, "wb");
9871 return -3; /* Cannot create file */
9874 fprintf(fp, "%s MO=%d\n", comment, cp->idn);
9875 fprintf(fp, " MO coefficients\n");
9877 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms), cp->origin.x, cp->origin.y, cp->origin.z);
9878 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx, cp->dx.x, cp->dx.y, cp->dx.z);
9879 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny, cp->dy.x, cp->dy.y, cp->dy.z);
9880 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz, cp->dz.x, cp->dz.y, cp->dz.z);
9882 /* Atomic information */
9883 for (i = 0; i < mp->bset->natoms; i++) {
9884 Vector *vp = mp->bset->pos + i;
9885 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
9886 /* The second number should actually be the effective charge */
9887 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber, vp->x, vp->y, vp->z);
9889 fprintf(fp, "%5d%5d\n", 1, 1);
9892 for (i = n = 0; i < cp->nx; i++) {
9893 for (j = 0; j < cp->ny; j++) {
9894 for (k = 0; k < cp->nz; k++) {
9895 fprintf(fp, " %12.5e", cp->dp[n++]);
9896 if (k == cp->nz - 1 || k % 6 == 5)