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 AtomDuplicate(Atom *dst, const Atom *src)
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) {
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;
74 if (ap->aniso != NULL) {
78 if (ap->frames != NULL) {
96 BasisSetRelease(BasisSet *bset)
101 if (bset->shells != NULL)
103 if (bset->priminfos != NULL)
104 free(bset->priminfos);
105 if (bset->mo != NULL)
107 if (bset->cns != NULL)
109 if (bset->moenergies != NULL)
110 free(bset->moenergies);
111 if (bset->scfdensities != NULL)
112 free(bset->scfdensities);
113 if (bset->pos != NULL)
115 if (bset->nuccharges != NULL)
116 free(bset->nuccharges);
117 if (bset->cubes != NULL) {
118 for (i = 0; i < bset->ncubes; i++) {
119 CubeRelease(bset->cubes[i]);
126 #pragma mark ====== Accessor types ======
129 MolEnumerableNew(Molecule *mol, int kind)
131 MolEnumerable *mseq = (MolEnumerable *)calloc(sizeof(MolEnumerable), 1);
133 mseq->mol = MoleculeRetain(mol);
140 MolEnumerableRelease(MolEnumerable *mseq)
143 MoleculeRelease(mseq->mol);
149 AtomRefNew(Molecule *mol, int idx)
151 AtomRef *aref = (AtomRef *)calloc(sizeof(AtomRef), 1);
153 aref->mol = MoleculeRetain(mol);
160 AtomRefRelease(AtomRef *aref)
163 MoleculeRelease(aref->mol);
168 #pragma mark ====== Creation of molecules ======
174 Molecule *mp = (Molecule *)calloc(sizeof(Molecule), 1);
176 Panic("Cannot allocate new molecule record");
177 snprintf(name, sizeof name, "Untitled %d", sMoleculeUntitledCount++);
178 ObjectInit((Object *)mp, (Object **)&sMoleculeRoot, name);
183 MoleculeNewWithName(const char *name)
185 Molecule *mp = MoleculeNew();
186 MoleculeSetName(mp, name);
191 MoleculeInitWithAtoms(Molecule *mp, const Atom *atoms, int natoms)
198 if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
199 Panic("Cannot allocate memory for atoms");
200 for (i = 0; i < natoms; i++)
201 AtomDuplicate(mp->atoms + i, atoms + i);
202 mp->nframes = -1; /* Should be recalculated later */
207 MoleculeInitWithMolecule(Molecule *mp2, const Molecule *mp)
209 MoleculeInitWithAtoms(mp2, mp->atoms, mp->natoms);
210 if (mp->nbonds > 0) {
211 if (NewArray(&mp2->bonds, &mp2->nbonds, sizeof(Int)*2, mp->nbonds) == NULL)
213 memmove(mp2->bonds, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
215 if (mp->nangles > 0) {
216 if (NewArray(&mp2->angles, &mp2->nangles, sizeof(Int)*3, mp->nangles) == NULL)
218 memmove(mp2->angles, mp->angles, sizeof(Int) * 3 * mp->nangles);
220 if (mp->ndihedrals > 0) {
221 if (NewArray(&mp2->dihedrals, &mp2->ndihedrals, sizeof(Int)*4, mp->ndihedrals) == NULL)
223 memmove(mp2->dihedrals, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
225 if (mp->nimpropers > 0) {
226 if (NewArray(&mp2->impropers, &mp2->nimpropers, sizeof(Int)*4, mp->nimpropers) == NULL)
228 memmove(mp2->impropers, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
230 if (mp->nresidues > 0) {
231 if (NewArray(&mp2->residues, &mp2->nresidues, sizeof(mp->residues[0]), mp->nresidues) == NULL)
233 memmove(mp2->residues, mp->residues, sizeof(mp->residues[0]) * mp->nresidues);
235 if (mp->cell != NULL) {
236 mp2->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
237 memmove(mp2->cell, mp->cell, sizeof(XtalCell));
240 mp2->nsyms = mp->nsyms;
241 mp2->syms = (Transform *)calloc(sizeof(Transform), mp2->nsyms);
242 memmove(mp2->syms, mp->syms, sizeof(Transform) * mp2->nsyms);
245 mp2->par = ParameterDuplicate(mp->par);
246 if (mp->arena != NULL) {
248 md_arena_init_from_arena(mp2->arena, mp->arena);
253 Panic("Cannot allocate memory for duplicate molecule");
254 return NULL; /* Not reached */
257 /* Assign a unique name to this parameter record */
259 MoleculeSetName(Molecule *mp, const char *name)
261 ObjectSetName((Object *)mp, name, (Object *)sMoleculeRoot);
265 MoleculeGetName(Molecule *mp)
267 return ObjectGetName((Object *)mp);
271 MoleculeWithName(const char *name)
273 return (Molecule *)ObjectWithName(name, (Object *)sMoleculeRoot);
277 MoleculeSetPath(Molecule *mol, const char *fname)
280 if (mol == NULL || fname == NULL)
282 if (mol->path != NULL)
283 free((void *)(mol->path));
284 if (fname[0] == '/' || (isalpha(fname[0]) && fname[1] == ':')) {
286 mol->path = strdup(fname);
289 cwd = getcwd(NULL, 0);
290 asprintf(&buf, "%s/%s", cwd, fname);
296 MoleculeGetPath(Molecule *mol)
304 MoleculeRetain(Molecule *mp)
306 ObjectIncrRefCount((Object *)mp);
311 MoleculeClear(Molecule *mp)
315 if (mp->arena != NULL) {
316 md_arena_set_molecule(mp->arena, NULL);
319 if (mp->par != NULL) {
320 ParameterRelease(mp->par);
323 if (mp->bset != NULL) {
324 BasisSetRelease(mp->bset);
327 if (mp->atoms != NULL) {
329 for (i = 0; i < mp->natoms; i++)
330 AtomClean(mp->atoms + i);
335 if (mp->bonds != NULL) {
340 if (mp->angles != NULL) {
345 if (mp->dihedrals != NULL) {
347 mp->dihedrals = NULL;
350 if (mp->impropers != NULL) {
352 mp->impropers = NULL;
355 if (mp->residues != NULL) {
360 if (mp->elpots != NULL) {
365 if (mp->path != NULL) {
366 free((void *)mp->path);
372 MoleculeRelease(Molecule *mp)
376 if (ObjectDecrRefCount((Object *)mp) == 0) {
378 ObjectDealloc((Object *)mp, (Object **)&sMoleculeRoot);
383 MoleculeExchange(Molecule *mp1, Molecule *mp2)
386 struct MainView *mview1, *mview2;
387 struct MDArena *arena1, *arena2;
388 /* mview and arena must be kept as they are */
393 /* 'natoms' is the first member to be copied */
394 int ofs = offsetof(Molecule, natoms);
395 memmove((char *)(&mp_temp) + ofs, (char *)mp1 + ofs, sizeof(Molecule) - ofs);
396 memmove((char *)mp1 + ofs, (char *)mp2 + ofs, sizeof(Molecule) - ofs);
397 memmove((char *)mp2 + ofs, (char *)(&mp_temp) + ofs, sizeof(Molecule) - ofs);
402 /* if (mp1->arena != NULL && mp1->arena->mol == mp2)
403 mp1->arena->mol = mp1;
404 if (mp1->arena != NULL && mp1->arena->xmol == mp2)
405 mp1->arena->xmol = mp1;
406 if (mp2->arena != NULL && mp2->arena->mol == mp1)
407 mp2->arena->mol = mp2;
408 if (mp2->arena != NULL && mp2->arena->xmol == mp1)
409 mp2->arena->xmol = mp2; */
412 #pragma mark ====== Mutex ======
415 MoleculeLock(Molecule *mol)
417 if (mol == NULL || mol->mutex == NULL)
419 MoleculeCallback_lockMutex(mol->mutex);
423 MoleculeUnlock(Molecule *mol)
425 if (mol == NULL || mol->mutex == NULL)
427 MoleculeCallback_unlockMutex(mol->mutex);
430 #pragma mark ====== Modify count ======
433 MoleculeIncrementModifyCount(Molecule *mp)
436 if (++(mp->modifyCount) == 1)
437 MoleculeCallback_notifyModification(mp, 0);
438 /* fprintf(stderr, "MoleculeIncrementModifyCount: %d\n", mp->modifyCount); */
443 MoleculeClearModifyCount(Molecule *mp)
447 /* fprintf(stderr, "MoleculeClearModifyCount: %d\n", mp->modifyCount); */
451 #pragma mark ====== File handling functions ======
454 guessMoleculeType(const char *fname)
458 const char *retval = NULL;
459 fp = fopen(fname, "rb");
461 memset(buf, 0, sizeof buf);
462 if (fread(buf, 1, sizeof buf - 1, fp) > 0) {
463 if (strncmp(buf, "PSF", 3) == 0)
465 else if (((p = strstr(buf, "ATOM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r'))
466 || ((p = strstr(buf, "HETATM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r')))
469 retval = "???"; /* unknown */
477 guessElement(Atom *ap)
479 int atomicNumber = -1;
480 if (ap->atomicNumber > 0)
481 atomicNumber = ap->atomicNumber;
483 atomicNumber = GuessAtomicNumber(ap->element, ap->weight);
484 if (atomicNumber <= 0 && ap->aname[0] != 0)
485 atomicNumber = GuessAtomicNumber(ap->aname, ap->weight);
487 if (atomicNumber >= 0) {
488 ap->atomicNumber = atomicNumber;
490 ap->weight = WeightForAtomicNumber(atomicNumber);
491 if (ap->element[0] == 0)
492 ElementToString(atomicNumber, ap->element);
498 sReadLineWithInterrupt(char *buf, int size, FILE *stream, int *lineNumber)
500 static int lastLineNumber = 0;
501 if (lineNumber != NULL) {
502 if (*lineNumber == 0)
504 else if (*lineNumber >= lastLineNumber + 1000) {
505 if (MyAppCallback_checkInterrupt() != 0)
506 return -1; /* User interrupt */
507 lastLineNumber = *lineNumber;
510 return ReadLine(buf, size, stream, lineNumber);
514 MoleculeLoadFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
517 if (ftype == NULL || *ftype == 0) {
519 cp = strrchr(fname, '.');
523 cp = guessMoleculeType(fname);
524 if (strcmp(cp, "???") != 0)
528 if (strcasecmp(ftype, "psf") == 0) {
529 retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
530 } else if (strcasecmp(ftype, "pdb") == 0) {
531 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
532 } else if (strcasecmp(ftype, "tep") == 0) {
533 retval = MoleculeLoadTepFile(mp, fname, errbuf, errbufsize);
534 } else if (strcasecmp(ftype, "res") == 0 || strcasecmp(ftype, "ins") == 0) {
535 retval = MoleculeLoadShelxFile(mp, fname, errbuf, errbufsize);
536 } else if (strcasecmp(ftype, "fchk") == 0 || strcasecmp(ftype, "fch") == 0) {
537 retval = MoleculeLoadGaussianFchkFile(mp, fname, errbuf, errbufsize);
539 snprintf(errbuf, errbufsize, "Unknown format %s", ftype);
542 /* if (retval != 0) {
543 retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
546 MoleculeSetPath(mp, fname);
551 MoleculeLoadMbsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
555 int i, j, k, n, err, fn, nframes;
564 char *bufp, *valp, *comp;
569 const int kUndefined = -10000000;
571 if (errbuf == NULL) {
576 if (mp->natoms != 0 || mp->par != NULL || mp->arena != NULL) {
577 snprintf(errbuf, errbufsize, "The molecule must be empty");
580 fp = fopen(fname, "rb");
582 snprintf(errbuf, errbufsize, "Cannot open file");
585 for (i = 0; i < 8; i++)
586 mview_fbuf[i] = kUndefined;
587 for (i = 0; i < 16; i++)
588 mview_ibuf[i] = kUndefined;
593 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
594 if (strncmp(buf, "!:", 2) != 0)
595 continue; /* Skip until section header is found */
597 strsep(&bufp, " \t\n");
598 if (strcmp(buf, "!:atoms") == 0) {
599 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
604 /* idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge */
605 if (sscanf(buf, "%d %4s %d %4s %4s %4s %lf %lf %4s %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) {
606 snprintf(errbuf, errbufsize, "line %d: coordinates cannot be read for atom %d", lineNumber, mp->natoms + 1);
609 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
610 strncpy(ap->segName, cbuf[0], 4);
611 ap->resSeq = ibuf[1];
612 strncpy(ap->resName, cbuf[1], 4);
613 strncpy(ap->aname, cbuf[2], 4);
614 ap->type = AtomTypeEncodeToUInt(cbuf[3]);
615 ap->charge = dbuf[0];
616 ap->weight = dbuf[1];
617 strncpy(ap->element, cbuf[4], 2);
618 ap->atomicNumber = ibuf[2];
619 ap->occupancy = dbuf[2];
620 ap->tempFactor = dbuf[3];
621 ap->intCharge = ibuf[3];
624 } else if (strcmp(buf, "!:atoms_symop") == 0) {
626 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
631 /* idx symop symbase */
632 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
633 snprintf(errbuf, errbufsize, "line %d: symmetry operations cannot be read for atom %d", lineNumber, i + 1);
636 if (i >= mp->natoms) {
637 snprintf(errbuf, errbufsize, "line %d: too many atomic symmetry info\n", lineNumber);
640 ap = ATOM_AT_INDEX(mp->atoms, i);
641 ap->symop.sym = ibuf[1] / 1000000;
642 ap->symop.dx = (ibuf[1] % 1000000) / 10000;
643 ap->symop.dy = (ibuf[1] % 10000) / 100;
644 ap->symop.dz = ibuf[1] % 100;
645 ap->symbase = ibuf[2];
649 } else if (strcmp(buf, "!:atoms_fix") == 0) {
651 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
656 /* idx fix_force fix_pos */
657 if (sscanf(buf, "%d %lf %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]) < 5) {
658 snprintf(errbuf, errbufsize, "line %d: fix atom info cannot be read for atom %d", lineNumber, i + 1);
661 if (i >= mp->natoms) {
662 snprintf(errbuf, errbufsize, "line %d: too many fix atom info\n", lineNumber);
665 ap = ATOM_AT_INDEX(mp->atoms, i);
666 ap->fix_force = dbuf[0];
667 ap->fix_pos.x = dbuf[1];
668 ap->fix_pos.y = dbuf[2];
669 ap->fix_pos.z = dbuf[3];
673 } else if (strcmp(buf, "!:mm_exclude") == 0) {
675 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
680 /* idx mm_exclude periodic_exclude */
681 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
682 snprintf(errbuf, errbufsize, "line %d: mm_exclude flags cannot be read for atom %d", lineNumber, i + 1);
685 if (i >= mp->natoms) {
686 snprintf(errbuf, errbufsize, "line %d: too many mm_exclude flags\n", lineNumber);
689 ap = ATOM_AT_INDEX(mp->atoms, i);
690 ap->mm_exclude = (ibuf[1] != 0);
691 ap->periodic_exclude = (ibuf[2] != 0);
695 } else if (strcmp(buf, "!:positions") == 0) {
697 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
703 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
704 snprintf(errbuf, errbufsize, "line %d: atom position cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
707 if (i >= mp->natoms) {
708 snprintf(errbuf, errbufsize, "line %d: too many atom position records\n", lineNumber);
714 ap = ATOM_AT_INDEX(mp->atoms, i);
716 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes, &v);
718 ap->frames[0] = ap->r;
725 mp->nframes = nframes;
726 mp->cframe = nframes - 1;
728 mp->nframes = mp->cframe = 0;
731 } else if (strcmp(buf, "!:bonds") == 0) {
732 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
737 /* from1 to1 from2 to2 from3 to3 from4 to4 */
738 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]);
739 if (i < 2 || i % 2 != 0) {
740 snprintf(errbuf, errbufsize, "line %d: bad bond format", lineNumber);
743 for (j = 0; j < i; j += 2) {
745 iibuf[1] = ibuf[j + 1];
746 if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[0] == iibuf[1]) {
747 snprintf(errbuf, errbufsize, "line %d: bad bond format", lineNumber);
750 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, iibuf);
751 for (k = 0; k < 2; k++) {
752 ap = ATOM_AT_INDEX(mp->atoms, iibuf[k]);
753 for (n = 0; n < ap->nconnects; n++) {
754 if (ap->connects[n] == iibuf[1 - k])
757 if (n >= ap->nconnects) {
758 if (ap->nconnects >= ATOMS_MAX_CONNECTS - 1) {
759 snprintf(errbuf, errbufsize, "line %d: too many bonds on atom %d", lineNumber, iibuf[k]);
762 ap->connects[ap->nconnects++] = iibuf[1 - k];
768 } else if (strcmp(buf, "!:angles") == 0) {
769 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
774 /* a1 b1 c1 a2 b2 c2 a3 b3 c3 */
775 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]);
776 if (i == 0 || i % 3 != 0) {
777 snprintf(errbuf, errbufsize, "line %d: bad angle format", lineNumber);
780 for (j = 0; j < i; j += 3) {
782 iibuf[1] = ibuf[j + 1];
783 iibuf[2] = ibuf[j + 2];
784 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]) {
785 snprintf(errbuf, errbufsize, "line %d: bad angle format", lineNumber);
788 AssignArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, mp->nangles, iibuf);
792 } else if (strcmp(buf, "!:dihedrals") == 0) {
793 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
798 /* a1 b1 c1 d1 a2 b2 c2 d2 */
799 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]);
800 if (i == 0 || i % 4 != 0) {
801 snprintf(errbuf, errbufsize, "line %d: bad dihedral format", lineNumber);
804 for (j = 0; j < i; j += 4) {
806 iibuf[1] = ibuf[j + 1];
807 iibuf[2] = ibuf[j + 2];
808 iibuf[3] = ibuf[j + 3];
809 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]) {
810 snprintf(errbuf, errbufsize, "line %d: bad dihedral format", lineNumber);
813 AssignArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, mp->ndihedrals, iibuf);
817 } else if (strcmp(buf, "!:impropers") == 0) {
818 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
823 /* a1 b1 c1 d1 a2 b2 c2 d2 */
824 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]);
825 if (i == 0 || i % 4 != 0) {
826 snprintf(errbuf, errbufsize, "line %d: bad improper format", lineNumber);
829 for (j = 0; j < i; j += 4) {
831 iibuf[1] = ibuf[j + 1];
832 iibuf[2] = ibuf[j + 2];
833 iibuf[3] = ibuf[j + 3];
834 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]) {
835 snprintf(errbuf, errbufsize, "line %d: bad improper format", lineNumber);
838 AssignArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, mp->nimpropers, iibuf);
842 } else if (strcmp(buf, "!:xtalcell") == 0 && mp->cell == NULL) {
843 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
848 /* a b c alpha beta gamma */
849 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
850 snprintf(errbuf, errbufsize, "line %d: bad xtalcell format", lineNumber);
853 MoleculeSetCell(mp, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], 0);
856 } else if (strcmp(buf, "!:symmetry_operations") == 0) {
858 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
864 /* a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3 */
865 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
866 snprintf(errbuf, errbufsize, "line %d: bad symmetry_operation format", lineNumber);
870 tr[i * 3 + 1] = dbuf[1];
871 tr[i * 3 + 2] = dbuf[2];
874 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
879 } else if (strcmp(buf, "!:periodic_box") == 0) {
882 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
887 /* a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3 */
888 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
889 snprintf(errbuf, errbufsize, "line %d: bad symmetry_operation format", lineNumber);
897 /* j = sscanf(buf, "%d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3]); */
898 cbuf[0][0] = dbuf[0];
899 cbuf[0][1] = dbuf[1];
900 cbuf[0][2] = dbuf[2];
901 MoleculeSetPeriodicBox(mp, vs, vs + 1, vs + 2, vs + 3, cbuf[0]);
903 mp->is_xtal_coord = (ibuf[3] != 0); */
907 } else if (strcmp(buf, "!:md_parameters") == 0) {
909 if (mp->arena == NULL)
910 mp->arena = md_arena_new(NULL);
912 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
918 comp = strsep(&bufp, " \t");
920 while (*bufp == ' ' || *bufp == '\t')
922 valp = strsep(&bufp, "\n");
924 if (strcmp(comp, "alchem_flags") == 0) {
925 j = (valp == NULL ? 0 : atoi(valp));
927 valp = (char *)malloc(j);
929 while ((k = fgetc(fp)) >= 0) {
931 if (k < '0' || k > '9') {
932 snprintf(errbuf, errbufsize, "line %d: too few flags in alchem_flags block", lineNumber + 1);
936 ReadLine(buf, sizeof buf, fp, &lineNumber);
939 if (*bufp >= '0' && *bufp <= '2') {
941 snprintf(errbuf, errbufsize, "line %d: too many flags in alchem_flags block", lineNumber);
945 valp[i++] = *bufp - '0';
946 } else if (*bufp != ' ' && *bufp != '\t' && *bufp != '\n') {
947 snprintf(errbuf, errbufsize, "line %d: strange character (0x%02x) in alchem_flags block", lineNumber, (int)*bufp);
956 md_set_alchemical_flags(arena, j, valp);
961 /* In the following, the redundant "!= NULL" is to suppress suprious warning */
962 if ((strcmp(comp, "log_file") == 0 && (pp = &arena->log_result_name) != NULL)
963 || (strcmp(comp, "coord_file") == 0 && (pp = &arena->coord_result_name) != NULL)
964 || (strcmp(comp, "vel_file") == 0 && (pp = &arena->vel_result_name) != NULL)
965 || (strcmp(comp, "force_file") == 0 && (pp = &arena->force_result_name) != NULL)
966 || (strcmp(comp, "debug_file") == 0 && (pp = &arena->debug_result_name) != NULL)) {
967 if (*valp == 0 || strstr(valp, "(null)") == valp)
972 char *valp1 = strchr(valp, '\n');
978 } else if ((strcmp(comp, "debug_output_level") == 0 && (ip = &arena->debug_output_level) != NULL)
979 || (strcmp(comp, "coord_output_freq") == 0 && (ip = &arena->coord_output_freq) != NULL)
980 || (strcmp(comp, "energy_output_freq") == 0 && (ip = &arena->energy_output_freq) != NULL)
981 || (strcmp(comp, "coord_frame") == 0 && (ip = &arena->coord_result_frame) != NULL)
982 || (strcmp(comp, "andersen_freq") == 0 && (ip = &arena->andersen_thermo_freq) != NULL)
983 || (strcmp(comp, "random_seed") == 0 && (ip = &arena->random_seed) != NULL)
984 || (strcmp(comp, "use_xplor_shift") == 0 && (ip = &arena->use_xplor_shift) != NULL)
985 || (strcmp(comp, "relocate_center") == 0 && (ip = &arena->relocate_center) != NULL)
986 || (strcmp(comp, "surface_potential_freq") == 0 && (ip = &arena->surface_potential_freq) != NULL)
987 || (strcmp(comp, "use_graphite") == 0 && (ip = &arena->use_graphite) != NULL)) {
988 *ip = (valp == NULL ? 0 : atoi(valp));
989 } else if ((strcmp(comp, "timestep") == 0 && (dp = &arena->timestep) != NULL)
990 || (strcmp(comp, "cutoff") == 0 && (dp = &arena->cutoff) != NULL)
991 || (strcmp(comp, "electro_cutoff") == 0 && (dp = &arena->electro_cutoff) != NULL)
992 || (strcmp(comp, "pairlist_distance") == 0 && (dp = &arena->pairlist_distance) != NULL)
993 || (strcmp(comp, "temperature") == 0 && (dp = &arena->temperature) != NULL)
994 || (strcmp(comp, "andersen_coupling") == 0 && (dp = &arena->andersen_thermo_coupling) != NULL)
995 || (strcmp(comp, "dielectric") == 0 && (dp = &arena->dielectric) != NULL)
996 || (strcmp(comp, "gradient_convergence") == 0 && (dp = &arena->gradient_convergence) != NULL)
997 || (strcmp(comp, "coordinate_convergence") == 0 && (dp = &arena->coordinate_convergence) != NULL)
998 || (strcmp(comp, "scale14_vdw") == 0 && (dp = &arena->scale14_vdw) != NULL)
999 || (strcmp(comp, "scale14_elect") == 0 && (dp = &arena->scale14_elect) != NULL)
1000 || (strcmp(comp, "surface_probe_radius") == 0 && (dp = &arena->probe_radius) != NULL)
1001 || (strcmp(comp, "surface_tension") == 0 && (dp = &arena->surface_tension) != NULL)
1002 || (strcmp(comp, "alchemical_lambda") == 0 && (dp = &arena->alchem_lambda) != NULL)
1003 || (strcmp(comp, "alchemical_delta_lambda") == 0 && (dp = &arena->alchem_dlambda) != NULL)) {
1004 *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1008 } else if (strcmp(buf, "!:pressure_control_parameters") == 0) {
1009 MDPressureArena *pressure;
1010 if (mp->arena == NULL)
1011 mp->arena = md_arena_new(mp);
1012 if (mp->arena->pressure == NULL)
1013 mp->arena->pressure = pressure_new();
1014 pressure = mp->arena->pressure;
1015 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1021 comp = strsep(&bufp, " \t");
1023 while (*bufp == ' ' || *bufp == '\t')
1025 valp = strsep(&bufp, "\n");
1027 if (strcmp(comp, "pressure") == 0) {
1028 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) {
1029 snprintf(errbuf, errbufsize, "line %d: bad format", lineNumber);
1032 for (i = 0; i < 9; i++)
1033 pressure->apply[i] = dbuf[i];
1034 } else if (strcmp(comp, "pressure_cell_flexibility") == 0) {
1035 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) {
1036 snprintf(errbuf, errbufsize, "line %d: bad format", lineNumber);
1039 for (i = 0; i < 8; i++)
1040 pressure->cell_flexibility[i] = dbuf[i];
1041 } else if ((strcmp(comp, "pressure_freq") == 0 && (ip = &pressure->freq) != NULL)) {
1042 *ip = (valp == NULL ? 0 : atoi(valp));
1043 } else if ((strcmp(comp, "pressure_coupling") == 0 && (dp = &pressure->coupling) != NULL)
1044 || (strcmp(comp, "pressure_fluctuate_cell_origin") == 0 && (dp = &pressure->fluctuate_cell_origin) != NULL)
1045 || (strcmp(comp, "pressure_fluctuate_cell_orientation") == 0 && (dp = &pressure->fluctuate_cell_orientation) != NULL)) {
1046 *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1050 } else if (strcmp(buf, "!:velocity") == 0) {
1052 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1058 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1059 snprintf(errbuf, errbufsize, "line %d: atom velocity cannot be read for atom %d", lineNumber, i + 1);
1062 if (i >= mp->natoms) {
1063 snprintf(errbuf, errbufsize, "line %d: too many atom velocity records\n", lineNumber);
1066 ap = ATOM_AT_INDEX(mp->atoms, i);
1073 } else if (strcmp(buf, "!:force") == 0) {
1075 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1081 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1082 snprintf(errbuf, errbufsize, "line %d: atom force cannot be read for atom %d", lineNumber, i + 1);
1085 if (i >= mp->natoms) {
1086 snprintf(errbuf, errbufsize, "line %d: too many atom force records\n", lineNumber);
1089 ap = ATOM_AT_INDEX(mp->atoms, i);
1096 } else if (strcmp(buf, "!:parameter") == 0 || strcmp(buf, "!:parameters") == 0) {
1097 Parameter *par = mp->par;
1099 mp->par = ParameterNew();
1104 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1109 j = ParameterReadFromString(par, buf, &bufp, fname, lineNumber, 0);
1111 snprintf(errbuf, errbufsize, "%s", bufp);
1117 MyAppCallback_setConsoleColor(1);
1118 MyAppCallback_showScriptMessage("%s", bufp);
1119 MyAppCallback_setConsoleColor(0);
1123 } else if (strcmp(buf, "!:trackball") == 0) {
1125 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1130 /* scale; trx try trz; theta_deg x y z */
1131 if ((i == 0 && sscanf(buf, "%f", &mview_fbuf[0]) < 1)
1132 || (i == 1 && sscanf(buf, "%f %f %f",
1133 &mview_fbuf[1], &mview_fbuf[2], &mview_fbuf[3]) < 3)
1134 || (i == 2 && sscanf(buf, "%f %f %f %f",
1135 &mview_fbuf[4], &mview_fbuf[5], &mview_fbuf[6], &mview_fbuf[7]) < 4)) {
1136 snprintf(errbuf, errbufsize, "line %d: bad trackball format", lineNumber);
1142 } else if (strcmp(buf, "!:view") == 0) {
1143 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1149 comp = strsep(&bufp, " \t");
1151 while (*bufp == ' ' || *bufp == '\t')
1153 valp = strsep(&bufp, "\n");
1155 /* In the following, the redundant "!= NULL" is to suppress suprious warning */
1156 if ((strcmp(comp, "show_unit_cell") == 0 && (i = 1))
1157 || (strcmp(comp, "show_periodic_box") == 0 && (i = 2))
1158 || (strcmp(comp, "show_expanded_atoms") == 0 && (i = 3))
1159 || (strcmp(comp, "show_ellipsoids") == 0 && (i = 4))
1160 || (strcmp(comp, "show_hydrogens") == 0 && (i = 5))
1161 || (strcmp(comp, "show_dummy_atoms") == 0 && (i = 6))
1162 || (strcmp(comp, "show_rotation_center") == 0 && (i = 7))
1163 || (strcmp(comp, "show_graphite_flag") == 0 && (i = 8))
1164 || (strcmp(comp, "show_periodic_image_flag") == 0 && (i = 9))
1165 || (strcmp(comp, "show_graphite") == 0 && (i = 10))) {
1166 mview_ibuf[i - 1] = atoi(valp);
1167 } else if (strcmp(comp, "show_periodic_image") == 0) {
1168 sscanf(valp, "%d %d %d %d %d %d",
1169 &mview_ibuf[10], &mview_ibuf[11], &mview_ibuf[12],
1170 &mview_ibuf[13], &mview_ibuf[14], &mview_ibuf[15]);
1175 /* Unknown sections are silently ignored */
1178 MoleculeCleanUpResidueTable(mp);
1179 if (mp->arena != NULL)
1180 md_arena_set_molecule(mp->arena, mp);
1184 if (errbuf[0] != 0) {
1185 /* The content of mp may be broken, so make it empty */
1189 MainView *mview = mp->mview;
1190 if (mview != NULL) {
1191 if (mview_ibuf[0] != kUndefined)
1192 mview->showUnitCell = mview_ibuf[0];
1193 if (mview_ibuf[1] != kUndefined)
1194 mview->showPeriodicBox = mview_ibuf[1];
1195 if (mview_ibuf[2] != kUndefined)
1196 mview->showExpandedAtoms = mview_ibuf[2];
1197 if (mview_ibuf[3] != kUndefined)
1198 mview->showEllipsoids = mview_ibuf[3];
1199 if (mview_ibuf[4] != kUndefined)
1200 mview->showHydrogens = mview_ibuf[4];
1201 if (mview_ibuf[5] != kUndefined)
1202 mview->showDummyAtoms = mview_ibuf[5];
1203 if (mview_ibuf[6] != kUndefined)
1204 mview->showRotationCenter = mview_ibuf[6];
1205 if (mview_ibuf[7] != kUndefined)
1206 mview->showGraphiteFlag = mview_ibuf[7];
1207 if (mview_ibuf[8] != kUndefined)
1208 mview->showPeriodicImageFlag = mview_ibuf[8];
1209 if (mview_ibuf[9] != kUndefined)
1210 mview->showGraphite = mview_ibuf[9];
1211 for (i = 0; i < 6; i++) {
1212 if (mview_ibuf[10 + i] != kUndefined)
1213 mview->showPeriodicImage[i] = mview_ibuf[10 + i];
1215 if (mview->track != NULL) {
1216 if (mview_fbuf[0] != kUndefined)
1217 TrackballSetScale(mview->track, mview_fbuf[0]);
1218 if (mview_fbuf[1] != kUndefined)
1219 TrackballSetTranslate(mview->track, mview_fbuf + 1);
1220 if (mview_fbuf[4] != kUndefined)
1221 TrackballSetRotate(mview->track, mview_fbuf + 4);
1229 MoleculeLoadPsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
1238 Vector *frames = NULL;
1241 if (errbuf == NULL) {
1248 fp = fopen(fname, "rb");
1250 snprintf(errbuf, errbufsize, "Cannot open file");
1253 /* flockfile(fp); */
1256 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1257 if (strncmp(buf, "PSF", 3) == 0) {
1261 for (p = buf; *p != 0 && isspace(*p); p++) {}
1267 if (strstr(buf, "!COORD") != NULL) {
1268 /* Extended psf file with coordinates */
1270 /* Allocate a temporary storage for frames */
1271 size_t size = sizeof(Vector) * mp->natoms * fn;
1273 frames = (Vector *)malloc(size);
1275 frames = (Vector *)realloc(frames, size);
1280 /* Copy the coordinates of the first frame */
1281 for (i = 0; i < mp->natoms; i++) {
1282 ap = ATOM_AT_INDEX(mp->atoms, i);
1286 /* Copy the coordinates of the last frame to the newly created frame */
1287 memmove(frames + sizeof(Vector) * mp->natoms * fn, frames + sizeof(Vector) * mp->natoms * (fn - 1), sizeof(Vector) * mp->natoms);
1290 /* Read coordinates */
1291 for (i = 0; i < mp->natoms; i++) {
1294 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1296 snprintf(errbuf, errbufsize, "line %d: premature end of file while reading coordinates (frame %d)", lineNumber, fn);
1299 if (sscanf(buf, "%lg %lg %lg", dval, dval + 1, dval + 2) != 3) {
1301 snprintf(errbuf, errbufsize, "line %d: coordinates cannot be read for atom %d", lineNumber, i + 1);
1308 ATOM_AT_INDEX(mp->atoms, i)->r = r;
1310 frames[mp->natoms * (fn - 1) + i] = r;
1319 ReadFormat(buf, "I8", &natoms);
1320 if (mp->atoms != NULL)
1322 if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
1325 for (i = 0; i < natoms; i++) {
1327 char segName[5], resName[4], atomName[5], atomType[3], element[3];
1330 memset(&w, 0, sizeof(w));
1331 ap = ATOM_AT_INDEX(mp->atoms, i);
1332 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1334 snprintf(errbuf, errbufsize, "line %d: premature end of file while reading atoms", lineNumber);
1337 ReadFormat(buf, "I8 x1 S4 I5 x1 S3 x2 S4 x1 S4 F16 F10",
1338 &w.serial, w.segName, &ap->resSeq, w.resName, w.atomName,
1339 w.atomType, &ap->charge, &ap->weight);
1340 strncpy(ap->segName, w.segName, 4);
1341 strncpy(ap->resName, w.resName, 3);
1342 strncpy(ap->aname, w.atomName, 4);
1343 ap->type = AtomTypeEncodeToUInt(w.atomType);
1344 /* $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl */
1345 ap->atomicNumber = GuessAtomicNumber(w.atomName, ap->weight);
1346 ElementToString(ap->atomicNumber, w.element);
1347 strncpy(ap->element, w.element, 2);
1348 /* w.element[0] = 0;
1349 for (p = w.atomName; *p != 0; p++) {
1350 if (isalpha(*p) && *p != '_') {
1351 w.element[0] = toupper(*p);
1352 if (isalpha(p[1]) && p[1] != '_') {
1353 w.element[1] = toupper(p[1]);
1361 strncpy(ap->element, w.element, 2);
1362 ap->atomicNumber = ElementToInt(w.element); */
1363 if (w.resName[0] == 0)
1364 strncpy(ap->resName, "XXX", 3);
1365 if (ap->resSeq > mp->nresidues)
1366 mp->nresidues = ap->resSeq;
1368 if (mp->residues != NULL)
1370 if (NewArray(&mp->residues, &mp->nresidues, sizeof(char (*)[4]), mp->nresidues + 1) == 0)
1372 for (i = 0; i < mp->natoms; i++) {
1373 j = mp->atoms[i].resSeq;
1374 if (mp->residues[j][0] == 0)
1375 strncpy(mp->residues[j], mp->atoms[i].resName, 4);
1378 } else if (section == 3) {
1382 ReadFormat(buf, "I8", &nbonds);
1383 if (mp->bonds != NULL)
1385 if (NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, nbonds) == NULL)
1388 for (i = 0; i < nbonds; i += 4) {
1389 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1390 snprintf(errbuf, errbufsize, "line %d: premature end of file while reading bonds", lineNumber);
1394 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1395 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1396 for (j = 0; j < 4 && i + j < nbonds; j++) {
1399 b1 = ibuf[j * 2] - 1; /* Internal atom number is 0-based */
1400 b2 = ibuf[j * 2 + 1] - 1;
1401 if (b1 < 0 || b1 >= mp->natoms || b2 < 0 || b2 >= mp->natoms) {
1402 snprintf(errbuf, errbufsize, "line %d: The bond %d-%d includes non-existent atom", lineNumber, b1+1, b2+1);
1408 ap = ATOM_AT_INDEX(mp->atoms, b1);
1409 if (ap->nconnects < ATOMS_MAX_CONNECTS)
1410 ap->connects[ap->nconnects++] = b2;
1412 snprintf(errbuf, errbufsize, "line %d: The atom %d has more than %d bonds", lineNumber, b1+1, ATOMS_MAX_CONNECTS);
1416 ap = ATOM_AT_INDEX(mp->atoms, b2);
1417 if (ap->nconnects < ATOMS_MAX_CONNECTS)
1418 ap->connects[ap->nconnects++] = b1;
1420 snprintf(errbuf, errbufsize, "line %d: The atom %d has more than %d bonds", lineNumber, b2+1, ATOMS_MAX_CONNECTS);
1427 } else if (section == 4) {
1431 ReadFormat(buf, "I8", &nangles);
1432 if (mp->angles != NULL)
1434 if (NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, nangles) == NULL)
1437 for (i = 0; i < nangles; i += 3) {
1438 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1439 snprintf(errbuf, errbufsize, "line %d: premature end of file while reading angles", lineNumber);
1443 ReadFormat(buf, "I8I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1444 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7, ibuf + 8);
1445 for (j = 0; j < 3 && i + j < nangles; j++) {
1447 a1 = ibuf[j * 3] - 1; /* Internal atom number is 0-based */
1448 a2 = ibuf[j * 3 + 1] - 1;
1449 a3 = ibuf[j * 3 + 2] - 1;
1450 if (a1 < 0 || a1 >= mp->natoms || a2 < 0 || a2 >= mp->natoms || a3 < 0 || a3 >= mp->natoms) {
1451 snprintf(errbuf, errbufsize, "line %d: The angle %d-%d-%d includes non-existent atom", lineNumber, a1+1, a2+1, a3+1);
1461 } else if (section == 5 || section == 6) {
1462 /* Dihedrals and Impropers */
1465 ReadFormat(buf, "I8", &ndihedrals);
1467 if (NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, ndihedrals) == NULL)
1471 if (NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, ndihedrals) == NULL)
1475 for (i = 0; i < ndihedrals; i += 2) {
1476 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1478 snprintf(errbuf, errbufsize, "line %d: premature end of file while reading %s", lineNumber, (section == 5 ? "dihedral" : "improper"));
1482 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3, ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1483 for (j = 0; j < 2 && i + j < ndihedrals; j++) {
1485 d1 = ibuf[j * 4] - 1; /* Internal atom number is 0-based */
1486 d2 = ibuf[j * 4 + 1] - 1;
1487 d3 = ibuf[j * 4 + 2] - 1;
1488 d4 = ibuf[j * 4 + 3] - 1;
1489 if (d1 < 0 || d1 >= mp->natoms || d2 < 0 || d2 >= mp->natoms || d3 < 0 || d3 >= mp->natoms || d4 < 0 || d4 >= mp->natoms) {
1490 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);
1504 /* Create frames for each atom if necessary */
1506 for (i = 0; i < mp->natoms; i++) {
1507 ap = ATOM_AT_INDEX(mp->atoms, i);
1508 ap->frames = (Vector *)malloc(sizeof(Vector) * fn);
1509 if (ap->frames == NULL)
1512 for (j = 0; j < fn; j++)
1513 ap->frames[j] = frames[mp->natoms * j + i];
1520 /* funlockfile(fp); */
1522 mp->nframes = -1; /* Should be recalculated later */
1525 else if (section == -1)
1529 Panic("low memory while reading structure file %s", fname);
1530 return 1; /* not reached */
1533 /* ("-x", "y", "-z+0.5") -> (-1,0,0,0,0,1,0,0,0,0,-1,0.5) */
1535 sMoleculeSymopStringsToTransform(char **symops, Transform tr)
1539 memset(tr, 0, sizeof(Transform));
1540 for (i = 0; i < 3; i++) {
1544 while (*symop != 0) {
1546 while (isspace(*symop))
1548 if (*symop == 0 || *symop == '\r' || *symop == 'n')
1550 if (*symop == '-') {
1553 } else if (*symop == '+') {
1557 while (isspace(*symop))
1559 if (*symop == '.' || isdigit(*symop)) {
1560 /* Numerical offset */
1561 double d = strtod(symop, &symop);
1562 if (*symop == '/') {
1563 double dd = strtod(symop + 1, &symop);
1567 return 1; /* Bad format */
1570 } else if (*symop == 'x' || *symop == 'X') {
1573 } else if (*symop == 'y' || *symop == 'Y') {
1576 } else if (*symop == 'z' || *symop == 'Z') {
1579 } else return 1; /* Bad format */
1580 } /* end while (*symop != 0) */
1586 sMoleculeGenerateSymopWithTransform(Molecule *mp, Transform gtr, int num)
1592 for (i = 0; i < num; i++) {
1593 memmove(tr, mp->syms[i], sizeof(Transform));
1594 TransformMul(tr, gtr, tr);
1595 for (j = 9; j < 12; j++) {
1598 else if (tr[j] <= 0.0)
1601 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1608 char *p = buf + strlen(buf) - 1;
1609 if (p >= buf && (*p == '\n' || *p == '\r')) {
1611 if (--p >= buf && (*p == '\n' || *p == '\r')) {
1619 MoleculeLoadTepFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
1629 if (errbuf == NULL) {
1636 fp = fopen(fname, "rb");
1638 snprintf(errbuf, errbufsize, "Cannot open file");
1642 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1643 if (section == -1) {
1650 ReadFormat(buf, "I1F8F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5);
1652 MoleculeSetCell(mp, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], 0);
1659 if (cellType == 0) {
1660 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);
1674 char *symops[3], *brks;
1676 memset(tr, 0, sizeof(Transform));
1677 ReadFormat(buf, "I1", ibuf);
1678 symops[0] = strtok_r(buf + 1, ", ", &brks);
1679 symops[1] = strtok_r(NULL, ", ", &brks);
1680 symops[2] = strtok_r(NULL, ", ", &brks);
1681 if (sMoleculeSymopStringsToTransform(symops, tr)) {
1682 snprintf(errbuf, errbufsize, "line %d: bad symmetry specification", lineNumber);
1686 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
1692 if (section == 2) { /* Atoms */
1696 int atomIndex = mp->natoms;
1697 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
1698 memset(ap, 0, gSizeOfAtomRecord);
1699 ReadFormat(buf, "S6x3x9x9F9F9F9F9", name, fbuf, fbuf+1, fbuf+2, fbuf+3);
1700 strncpy(ap->aname, name, 4);
1704 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
1705 /* ap->atomicNumber = AtomNameToElement(ap->name);
1706 ElementToString(ap->atomicNumber, ap->element); */
1707 /* sAtomSetElement(ap, -1, ap->name); */
1710 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1711 snprintf(errbuf, errbufsize, "unexpected end of file");
1714 ReadFormat(buf, "I1F8F9F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
1716 if ((atomType >= 0 && atomType <= 5) || (atomType >= 8 && atomType <= 10)) {
1717 /* Anisotropic thermal parameters */
1718 MoleculeSetAniso(mp, atomIndex, atomType, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[5], fbuf[4]);
1726 MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
1728 MoleculeAddBonds(mp, nbonds, bonds);
1731 mp->nframes = -1; /* Should be recalculated later */
1734 Panic("low memory while reading structure file %s", fname);
1735 return -1; /* not reached */
1739 MoleculeLoadShelxFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
1747 int currentResSeq = 0;
1748 char currentResName[6];
1754 char (*sfacs)[4] = NULL;
1756 if (errbuf == NULL) {
1763 currentResName[0] = 0;
1764 fp = fopen(fname, "rb");
1766 snprintf(errbuf, errbufsize, "Cannot open file");
1770 tr[0] = tr[4] = tr[8] = 1;
1771 tr[1] = tr[2] = tr[3] = tr[5] = tr[6] = tr[7] = tr[9] = tr[10] = tr[11] = 0;
1772 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), 0, tr) == 0)
1774 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1775 if (strncmp(buf, "CELL", 4) == 0) {
1777 sscanf(buf + 4, " %f %f %f %f %f %f %f", fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
1778 MoleculeSetCell(mp, fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], fbuf[6], 0);
1780 } else if (strncmp(buf, "SFAC", 4) == 0) {
1782 for (p1 = strtok_r(buf + 4, " ", &p2); p1 != NULL; p1 = strtok_r(NULL, " ", &p2)) {
1783 char *pp = (char *)AssignArray(&sfacs, &nsfacs, 4, nsfacs, NULL);
1790 } else if (strncmp(buf, "LATT", 4) == 0) {
1791 sscanf(buf + 4, " %d", &latticeType);
1793 } else if (strncmp(buf, "SYMM", 4) == 0) {
1794 char *symops[3], *brks;
1795 memset(tr, 0, sizeof(Transform));
1796 // ReadFormat(buf + 4, "I1", ibuf);
1798 symops[0] = strtok_r(buf + 4, ",", &brks);
1799 symops[1] = strtok_r(NULL, ",", &brks);
1800 symops[2] = strtok_r(NULL, ",", &brks);
1801 if (sMoleculeSymopStringsToTransform(symops, tr)) {
1802 snprintf(errbuf, errbufsize, "line %d: bad symmetry specification", lineNumber);
1805 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
1808 } else if (strncmp(buf, "RESI", 4) == 0) {
1809 for (p1 = buf + 4; isspace(*p1); p1++);
1811 for (p2 = p1 + 1; isalnum(*p2); p2++);
1813 strncpy(currentResName, p1, 4);
1814 currentResName[4] = 0;
1816 } else currentResName[0] = 0;
1817 sscanf(buf + 4, " %d", ¤tResSeq);
1820 /* Atom name: [A-Za-z]{1,2}[0-9]* */
1821 for (p1 = buf; p1 < buf + 2 && (isalpha(*p1) && *p1 != '_'); p1++);
1823 while (isdigit(*p1))
1826 if (p1 > buf && p1 <= buf + 4 && isspace(*p1)) {
1830 int atomIndex = mp->natoms;
1831 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
1832 memset(ap, 0, gSizeOfAtomRecord);
1833 strncpy(ap->aname, buf, 4);
1834 n = sscanf(p1, " %d %f %f %f %f %f %f %2s", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, cont);
1835 if (n == 8 && strcmp(cont, "=") == 0) {
1836 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1837 snprintf(errbuf, errbufsize, "line %d: unexpected end of file within the atom cards", lineNumber);
1840 sscanf(buf, " %f %f %f %f", fbuf+6, fbuf+7, fbuf+8, fbuf+9);
1842 } else n = 5; /* Iso */
1846 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
1847 ap->occupancy = fbuf[3];
1848 if (ap->aname[0] != 'Q' && ibuf[0] >= 1 && ibuf[0] <= nsfacs) {
1849 strncpy(ap->element, sfacs[ibuf[0] - 1], 2);
1851 /* sAtomSetElement(ap, -1, sfacs[ibuf[0] - 1]); */
1852 /* strncpy(ap->element, sfacs[ibuf[0] - 1], 4);
1853 ap->atomicNumber = ElementToInt(ap->element); */
1855 sAtomSetElement(ap, -1, ap->name); */
1856 /* ap->atomicNumber = AtomNameToElement(ap->name);
1857 ElementToString(ap->atomicNumber, ap->element); */
1861 ap->tempFactor = fbuf[4] * 78.9568352087147; /* 8*pi*pi */
1863 MoleculeSetAniso(mp, atomIndex, 8, fbuf[4], fbuf[5], fbuf[6], fbuf[9], fbuf[7], fbuf[8]);
1864 ap->resSeq = currentResSeq;
1865 strncpy(ap->resName, currentResName, 4);
1872 /* Add symmetry operations according to the lattice type */
1873 switch (latticeType < 0 ? -latticeType : latticeType) {
1874 static Transform tr_i = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0.5};
1875 static Transform tr_c = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0};
1876 static Transform tr_a = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.5};
1877 static Transform tr_b = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5};
1878 static Transform tr_r1 = {0, -1, 0, 1, -1, 0, 0, 0, 1, 0, 0, 0};
1879 static Transform tr_r2 = {-1, 1, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0};
1883 sMoleculeGenerateSymopWithTransform(mp, tr_i, 0);
1885 case 3: /* Rhombohedral obverse on hexagonal axes */
1887 sMoleculeGenerateSymopWithTransform(mp, tr_r1, n);
1888 sMoleculeGenerateSymopWithTransform(mp, tr_r2, n);
1892 sMoleculeGenerateSymopWithTransform(mp, tr_a, n);
1893 sMoleculeGenerateSymopWithTransform(mp, tr_b, n);
1894 sMoleculeGenerateSymopWithTransform(mp, tr_c, n);
1897 sMoleculeGenerateSymopWithTransform(mp, tr_a, 0);
1900 sMoleculeGenerateSymopWithTransform(mp, tr_b, 0);
1903 sMoleculeGenerateSymopWithTransform(mp, tr_c, 0);
1907 if (latticeType > 0) {
1908 static Transform tr_inv = {-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0};
1909 sMoleculeGenerateSymopWithTransform(mp, tr_inv, 0);
1912 MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
1914 MoleculeAddBonds(mp, nbonds, bonds);
1917 mp->nframes = -1; /* Should be recalculated later */
1920 Panic("low memory while reading structure file %s", fname);
1921 return -1; /* not reached */
1924 /* Add one gaussian orbital shell information (not undoable) */
1926 MoleculeAddGaussianOrbitalShell(Molecule *mol, Int sym, Int nprims, Int a_idx)
1931 return -1; /* Molecule is empty */
1934 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
1936 return -2; /* Low memory */
1938 shellp = AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), bset->nshells, NULL);
1940 return -2; /* Low memory */
1942 case 0: shellp->sym = kGTOType_S; shellp->ncomp = 1; break;
1943 case 1: shellp->sym = kGTOType_P; shellp->ncomp = 3; break;
1944 case -1: shellp->sym = kGTOType_SP; shellp->ncomp = 4; break;
1945 case 2: shellp->sym = kGTOType_D; shellp->ncomp = 6; break;
1946 case -2: shellp->sym = kGTOType_D5; shellp->ncomp = 5; break;
1947 /* TODO: Support F/F7 type orbitals */
1948 /* case 3: sp->sym = kGTOtype_F; sp->ncomp = 10; break;
1949 case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break; */
1951 return -3; /* Unsupported shell type */
1953 shellp->nprim = nprims;
1954 shellp->a_idx = a_idx;
1955 if (bset->shells < shellp) {
1956 shellp->m_idx = shellp[-1].m_idx + shellp[-1].ncomp;
1957 shellp->p_idx = shellp[-1].p_idx + shellp[-1].nprim;
1965 /* Add a set of gaussian primitive coefficients (not undoable) */
1967 MoleculeAddGaussianPrimitiveCoefficients(Molecule *mol, Double exponent, Double contraction, Double contraction_sp)
1972 return -1; /* Molecule is empty */
1975 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
1977 return -2; /* Low memory */
1979 primp = AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), bset->npriminfos, NULL);
1981 return -2; /* Low memory */
1982 primp->A = exponent;
1983 primp->C = contraction;
1984 primp->Csp = contraction_sp;
1988 /* Set MO coefficients for idx-th MO */
1990 MoleculeSetMOCoefficients(Molecule *mol, Int idx, Double energy, Int ncomps, Double *coeffs)
1995 return -1; /* Molecule is empty */
1998 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2000 return -2; /* Low memory */
2002 if (bset->nmos == 0) {
2003 if (bset->nshells > 0) {
2004 /* Shell info is already set: calculate the number of MOs from there */
2005 for (i = n = 0; i < bset->nshells; i++)
2006 n += bset->shells[i].ncomp;
2008 } else if (ncomps > 0) {
2009 bset->ncomps = ncomps;
2011 if (bset->rflag == 0)
2012 bset->nmos = bset->ncomps * 2;
2014 bset->nmos = bset->ncomps;
2015 if (bset->nmos <= 0)
2016 return -3; /* Bad or inconsistent number of MOs */
2017 bset->mo = (Double *)calloc(sizeof(Double), bset->nmos * bset->ncomps);
2018 bset->moenergies = (Double *)calloc(sizeof(Double), bset->nmos);
2019 if (bset->mo == NULL || bset->moenergies == NULL) {
2020 if (bset->mo != NULL)
2022 if (bset->moenergies != NULL)
2023 free(bset->moenergies);
2025 bset->moenergies = NULL;
2027 return -2; /* Low memory */
2030 if (idx < 0 || idx >= bset->nmos)
2031 return -4; /* Bad MO index */
2032 if (energy != -1000000)
2033 bset->moenergies[idx] = energy;
2034 if (ncomps < bset->ncomps)
2035 return -5; /* Insufficient number of data provided */
2036 memmove(bset->mo + (idx * bset->ncomps), coeffs, sizeof(Double) * bset->ncomps);
2037 if (bset->cns != NULL) {
2038 /* Clear the cached values */
2046 /* Allocate BasisSet record. rflag: UHF, 0; RHF, 1; ROHF, 2
2047 ne_alpha: number of alpha electrons, ne_beta: number of beta electrons
2048 The natoms and pos are copied from mol. */
2050 MoleculeAllocateBasisSetRecord(Molecule *mol, Int rflag, Int ne_alpha, Int ne_beta)
2055 if (mol == NULL || mol->natoms == 0)
2056 return -1; /* Molecule is empty */
2059 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2061 return -2; /* Low memory */
2063 if (bset->pos != NULL) {
2067 bset->natoms = mol->natoms;
2068 bset->pos = (Vector *)calloc(sizeof(Vector), bset->natoms);
2069 if (bset->pos == NULL)
2070 return -2; /* Low memory */
2071 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
2072 bset->pos[i].x = ap->r.x * kAngstrom2Bohr;
2073 bset->pos[i].y = ap->r.y * kAngstrom2Bohr;
2074 bset->pos[i].z = ap->r.z * kAngstrom2Bohr;
2076 bset->ne_alpha = ne_alpha;
2077 bset->ne_beta = ne_beta;
2078 bset->rflag = rflag;
2083 sSeparateTokens(char *inString, char **outPtr, int size)
2087 for (i = 0; i < size; i++) {
2088 p = strtok((i == 0 ? inString : NULL), " \r\n");
2099 sReadNumberArray(void *basep, Int *countp, Int size, Int num, FILE *fp, int *lnp)
2103 *((void **)basep) = NULL;
2105 if (AssignArray(basep, countp, size, num - 1, NULL) == NULL)
2106 return 4; /* Out of memory */
2108 while (ReadLine(buf, sizeof buf, fp, lnp) > 0) {
2109 char *tokens[16], *p;
2110 sSeparateTokens(buf, tokens, 16);
2111 for (i = 0; i < 16; i++) {
2112 if (tokens[i] == NULL)
2114 if (size == sizeof(Int)) {
2115 (*((Int **)basep))[n] = strtol(tokens[i], &p, 0);
2116 } else if (size == sizeof(Double)) {
2117 (*((Double **)basep))[n] = strtod(tokens[i], &p);
2118 } else return -1; /* Internal error */
2119 if (tokens[i] == p || *p != 0)
2120 return 1; /* Non-digit character */
2122 if (i < 15 && tokens[i + 1] != NULL)
2123 return 2; /* Too many data */
2124 return 0; /* All data are successfully read */
2128 return 3; /* Unexpected EOF */
2132 sSetupGaussianCoefficients(BasisSet *bset)
2139 /* Cache the contraction coefficients for efficient calculation */
2140 /* Sum up the number of components for all primitives */
2141 for (i = k = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2143 k += sp->nprim * sp->ncomp;
2145 /* Allocate memory for the cached values */
2146 if (AssignArray(&bset->cns, &bset->ncns, sizeof(Double), k - 1, NULL) == NULL)
2148 /* Iterate over all primitives */
2150 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2151 for (j = 0, pp = bset->priminfos + sp->p_idx; j < sp->nprim; j++, pp++) {
2154 // (8 alpha^3/pi^3)^0.25 exp(-alpha r^2)
2155 *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2158 // (128 alpha^5/pi^3)^0.25 [x|y|z]exp(-alpha r^2)
2159 d = pp->C * pow(pp->A, 1.25) * 1.425410941;
2165 *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2166 d = pp->Csp * pow(pp->A, 1.25) * 1.425410941;
2172 // xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2)
2173 // xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2174 d = pp->C * pow(pp->A, 1.75);
2175 dp[0] = dp[1] = dp[2] = d * 1.645922781;
2176 dp[3] = dp[4] = dp[5] = d * 2.850821881;
2180 // 3zz-rr: (128 alpha^7/9pi^3)^0.25 (3zz-rr)exp(-alpha r^2)
2181 // xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2182 // xx-yy: (128 alpha^7/pi^3)^0.25 (xx-yy)exp(-alpha r^2)
2183 d = pp->C * pow(pp->A, 1.75);
2184 dp[0] = d * 0.822961390;
2185 dp[1] = dp[2] = dp[4] = d * 2.850821881;
2186 dp[3] = d * 1.425410941;
2196 MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
2201 int natoms, nbasis, i, j, k, n, mxbond, retval, ncomps, nprims, nelec;
2212 if (errbuf == NULL) {
2219 bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2223 fp = fopen(fname, "rb");
2225 snprintf(errbuf, errbufsize, "Cannot open file");
2229 natoms = nbasis = -1;
2237 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2240 if (lineNumber == 2) {
2243 bset->rflag = 0; /* UHF */
2244 else if (buf[11] == 'O')
2245 bset->rflag = 2; /* ROHF */
2246 else bset->rflag = 1; /* RHF */
2249 while (p > buf && *p == ' ')
2252 sSeparateTokens(buf + 42, tokens, 16);
2253 if (strcmp(buf, "Number of atoms") == 0) {
2254 if (tokens[1] == NULL || (natoms = atoi(tokens[1])) <= 0) {
2255 snprintf(errbuf, errbufsize, "Line %d: strange number of atoms: %s", lineNumber, tokens[1]);
2259 /* Allocate atom records (all are empty for now) */
2260 AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms - 1, NULL);
2261 /* Also allocate atom position array for MO calculations */
2262 AssignArray(&bset->pos, &bset->natoms, sizeof(Vector), natoms - 1, NULL);
2263 /* Also allocate nuclear charge array */
2264 bset->nuccharges = (Double *)calloc(sizeof(Double), natoms);
2265 } else if (strcmp(buf, "Number of electrons") == 0) {
2266 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2267 snprintf(errbuf, errbufsize, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
2272 } else if (strcmp(buf, "Number of alpha electrons") == 0) {
2273 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2274 snprintf(errbuf, errbufsize, "Line %d: strange number of alpha electrons: %s", lineNumber, tokens[1]);
2279 } else if (strcmp(buf, "Number of beta electrons") == 0) {
2280 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2281 snprintf(errbuf, errbufsize, "Line %d: strange number of beta electrons: %s", lineNumber, tokens[1]);
2286 if (bset->ne_alpha + bset->ne_beta != nelec) {
2287 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);
2291 } else if (strcmp(buf, "Number of basis functions") == 0) {
2292 if (tokens[1] == NULL || (nbasis = atoi(tokens[1])) <= 0) {
2293 snprintf(errbuf, errbufsize, "Line %d: strange number of basis functions: %s", lineNumber, tokens[1]);
2297 } else if (strcmp(buf, "Atomic numbers") == 0) {
2298 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2299 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2303 if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms, fp, &lineNumber) != 0) {
2304 snprintf(errbuf, errbufsize, "Line %d: cannot read atomic numbers", lineNumber);
2308 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2309 ap->atomicNumber = iary[i];
2310 bset->nuccharges[i] = iary[i];
2311 ElementToString(ap->atomicNumber, ap->element);
2312 memmove(ap->aname, ap->element, 4);
2313 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
2318 } else if (strcmp(buf, "Nuclear charges") == 0) {
2319 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2320 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2324 if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms, fp, &lineNumber) != 0) {
2325 snprintf(errbuf, errbufsize, "Line %d: cannot read nuclear charges", lineNumber);
2329 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2330 bset->nuccharges[i] = dary[i];
2334 } else if (strcmp(buf, "Current cartesian coordinates") == 0) {
2335 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * 3) {
2336 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of cartesian coordinates: %s", lineNumber, tokens[2]);
2340 if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms * 3, fp, &lineNumber) != 0) {
2341 snprintf(errbuf, errbufsize, "Line %d: cannot read cartesian coordinates", lineNumber);
2345 for (i = 0, ap = mp->atoms, vp = bset->pos; i < natoms; i++, ap = ATOM_NEXT(ap), vp++) {
2346 vp->x = dary[i * 3];
2347 vp->y = dary[i * 3 + 1];
2348 vp->z = dary[i * 3 + 2];
2349 ap->r.x = vp->x * kBohr2Angstrom;
2350 ap->r.y = vp->y * kBohr2Angstrom;
2351 ap->r.z = vp->z * kBohr2Angstrom;
2355 } else if (strcmp(buf, "MxBond") == 0) {
2356 if (tokens[1] == NULL || (mxbond = atoi(tokens[1])) <= 0) {
2357 snprintf(errbuf, errbufsize, "Line %d: strange number of bonds per atom: %s", lineNumber, tokens[1]);
2361 } else if (strcmp(buf, "IBond") == 0) {
2362 Int bonds[ATOMS_MAX_CONNECTS * 2 + 1], lim;
2363 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * mxbond) {
2364 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of bonds: %s", lineNumber, tokens[2]);
2368 if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms * mxbond, fp, &lineNumber) != 0) {
2369 snprintf(errbuf, errbufsize, "Line %d: cannot read bond information", lineNumber);
2373 lim = (mxbond > ATOMS_MAX_CONNECTS ? ATOMS_MAX_CONNECTS : mxbond);
2374 for (i = 0; i < natoms; i++) {
2375 for (j = k = 0; j < lim; j++) {
2376 n = iary[i * mxbond + j] - 1;
2378 /* Connect atom i and atom n */
2384 bonds[k] = kInvalidIndex;
2385 MoleculeAddBonds(mp, k / 2, bonds);
2390 } else if (strcmp(buf, "Shell types") == 0) {
2391 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0) {
2392 snprintf(errbuf, errbufsize, "Line %d: wrong number of shell types: %s", lineNumber, tokens[2]);
2396 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2397 snprintf(errbuf, errbufsize, "Line %d: cannot read shell types", lineNumber);
2401 /* Allocate ShellInfo table and store shell type information */
2402 AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), nary - 1, NULL);
2403 for (i = n = 0, sp = bset->shells; i < nary; i++, sp++) {
2405 case 0: sp->sym = kGTOType_S; sp->ncomp = 1; break;
2406 case 1: sp->sym = kGTOType_P; sp->ncomp = 3; break;
2407 case -1: sp->sym = kGTOType_SP; sp->ncomp = 4; break;
2408 case 2: sp->sym = kGTOType_D; sp->ncomp = 6; break;
2409 case -2: sp->sym = kGTOType_D5; sp->ncomp = 5; break;
2410 /* TODO: Support F/F7 type orbitals */
2411 /* case 3: sp->sym = kGTOtype_F; sp->ncomp = 10; break;
2412 case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break; */
2414 snprintf(errbuf, errbufsize, "Line %d: unsupported shell type %d", lineNumber, iary[i]);
2421 bset->ncomps = ncomps = n;
2424 } else if (strcmp(buf, "Number of primitives per shell") == 0) {
2425 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2426 snprintf(errbuf, errbufsize, "Line %d: wrong size of the primitive table: %s", lineNumber, tokens[2]);
2430 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2431 snprintf(errbuf, errbufsize, "Line %d: cannot read primitive table", lineNumber);
2435 for (i = n = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2436 sp->nprim = iary[i];
2443 } else if (strcmp(buf, "Shell to atom map") == 0) {
2444 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2445 snprintf(errbuf, errbufsize, "Line %d: wrong size of the shell-to-atom map: %s", lineNumber, tokens[2]);
2449 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2450 snprintf(errbuf, errbufsize, "Line %d: cannot read shell-to-atom table", lineNumber);
2454 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2455 sp->a_idx = iary[i] - 1;
2459 } else if (strcmp(buf, "Primitive exponents") == 0) {
2460 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nprims) {
2461 snprintf(errbuf, errbufsize, "Line %d: wrong number of primitive exponents: %s", lineNumber, tokens[2]);
2465 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2466 snprintf(errbuf, errbufsize, "Line %d: cannot read primitive exponents", lineNumber);
2470 /* Allocate PrimInfo table */
2471 AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), nprims - 1, NULL);
2472 for (i = 0, pp = bset->priminfos; i < nprims; i++, pp++) {
2477 } else if (strcmp(buf, "Contraction coefficients") == 0) {
2478 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2479 snprintf(errbuf, errbufsize, "Line %d: wrong number of contraction coefficients: %s", lineNumber, tokens[2]);
2483 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2484 snprintf(errbuf, errbufsize, "Line %d: cannot read contraction coefficients", lineNumber);
2488 for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2493 } else if (strcmp(buf, "P(S=P) Contraction coefficients") == 0) {
2494 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2495 snprintf(errbuf, errbufsize, "Line %d: wrong number of P(S=P) contraction coefficients: %s", lineNumber, tokens[2]);
2499 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2500 snprintf(errbuf, errbufsize, "Line %d: cannot read P(S=P) contraction coefficients", lineNumber);
2504 for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2509 } else if (strcmp(buf, "Alpha Orbital Energies") == 0) {
2510 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2511 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of alpha orbitals: %s", lineNumber, tokens[2]);
2515 if (sReadNumberArray(&bset->moenergies, &bset->nmos, sizeof(Double), i, fp, &lineNumber) != 0) {
2516 snprintf(errbuf, errbufsize, "Line %d: cannot read alpha orbital energies", lineNumber);
2520 } else if (strcmp(buf, "Alpha MO coefficients") == 0) {
2521 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2522 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of alpha MO coefficients: %s", lineNumber, tokens[2]);
2526 if (sReadNumberArray(&bset->mo, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2527 snprintf(errbuf, errbufsize, "Line %d: cannot read MO coefficients", lineNumber);
2531 } else if (strcmp(buf, "Beta Orbital Energies") == 0) {
2532 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2533 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of beta orbitals: %s", lineNumber, tokens[2]);
2537 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2538 snprintf(errbuf, errbufsize, "Line %d: cannot read beta orbital energies", lineNumber);
2542 bset->moenergies = (Double *)realloc(bset->moenergies, sizeof(Double) * 2 * ncomps);
2543 bset->nmos = ncomps * 2;
2544 bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);
2545 memmove(bset->moenergies + ncomps, dary, sizeof(Double) * ncomps);
2546 memset(bset->mo + ncomps * ncomps, 0, sizeof(Double) * ncomps * ncomps);
2549 } else if (strcmp(buf, "Beta MO coefficients") == 0) {
2550 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2551 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of beta MO coefficients: %s", lineNumber, tokens[2]);
2555 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2556 snprintf(errbuf, errbufsize, "Line %d: cannot read alpha MO coefficients", lineNumber);
2560 bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps); /* Should be unnecessary, just in case */
2561 memmove(bset->mo + ncomps * ncomps, dary, sizeof(Double) * ncomps * ncomps);
2564 } else if (strcmp(buf, "Total SCF Density") == 0) {
2565 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * (ncomps + 1) / 2) {
2566 snprintf(errbuf, errbufsize, "Line %d: wrong or inconsistent number of SCF densities: %s", lineNumber, tokens[2]);
2570 if (sReadNumberArray(&bset->scfdensities, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2571 snprintf(errbuf, errbufsize, "Line %d: cannot read SCF densities", lineNumber);
2577 if (mp->natoms == 0) {
2578 snprintf(errbuf, errbufsize, "Atom information is missing");
2582 if (bset->shells == NULL || bset->priminfos == NULL) {
2583 snprintf(errbuf, errbufsize, "Gaussian primitive information is missing");
2587 if (bset->mo == NULL) {
2588 snprintf(errbuf, errbufsize, "MO coefficients were not found");
2592 if (sSetupGaussianCoefficients(bset) != 0) {
2593 snprintf(errbuf, errbufsize, "Internal error during setup MO calculation");
2606 if (mp->bset != NULL) {
2607 BasisSetRelease(mp->bset);
2613 Panic("low memory while reading fchk file %s", fname);
2614 return -1; /* not reached */
2618 MoleculeLoadGamessDatFile(Molecule *mol, const char *fname, char *errbuf, int errbufsize)
2623 int lineNumber, i, j, k, len, natoms = 0;
2629 Vector *vbuf = NULL;
2631 int optimizing = 0, status = 0;
2633 if (errbuf == NULL) {
2639 mol = MoleculeNew();
2641 if (mol->natoms == 0)
2644 fp = fopen(fname, "rb");
2646 snprintf(errbuf, errbufsize, "Cannot open file");
2650 /* ESP is cleared (not undoable!) */
2651 if (mol->elpots != NULL) {
2658 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2661 if (strncmp(buf, " $DATA", 6) == 0) {
2662 /* Initial geometry */
2664 vbuf = (Vector *)calloc(sizeof(Vector), mol->natoms);
2667 ReadLine(buf, sizeof buf, fp, &lineNumber); /* Title */
2668 ReadLine(buf, sizeof buf, fp, &lineNumber); /* Symmetry */
2669 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2670 if (strncmp(buf, " $END", 5) == 0)
2672 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
2673 snprintf(errbuf, errbufsize, "Line %d: bad format in $DATA section", lineNumber);
2678 memset(&a, 0, sizeof(a));
2679 strncpy(a.aname, sval, 4);
2683 a.atomicNumber = (Int)dval[0];
2684 strncpy(a.element, ElementToString(a.atomicNumber, sval), 3);
2685 a.type = AtomTypeEncodeToUInt(a.element);
2686 a.weight = WeightForAtomicNumber(a.atomicNumber);
2687 MoleculeCreateAnAtom(mol, &a, mol->natoms);
2690 if (i >= mol->natoms) {
2691 snprintf(errbuf, errbufsize, "Line %d: too many atoms", lineNumber);
2694 if ((ap = ATOM_AT_INDEX(mol->atoms, i))->atomicNumber != dval[0]) {
2695 snprintf(errbuf, errbufsize, "Line %d: atomic number does not match", lineNumber);
2698 vbuf[i].x = dval[1];
2699 vbuf[i].y = dval[2];
2700 vbuf[i].z = dval[3];
2702 /* Skip until a blank line is found */
2703 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2704 for (j = 0; buf[j] == ' '; j++);
2712 /* Set atom positions */
2714 if (natoms < mol->natoms) {
2715 snprintf(errbuf, errbufsize, "Line %d: too few atoms", lineNumber);
2718 ig = IntGroupNewWithPoints(0, natoms, -1);
2719 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, natoms, vbuf);
2720 IntGroupRelease(ig);
2723 vbuf = (Vector *)calloc(sizeof(Vector), natoms);
2724 nframes = MoleculeGetNumberOfFrames(mol);
2728 } else if (strstr(buf, "DATA FROM NSERCH") != NULL || (strstr(buf, "RESULTS FROM SUCCESSFUL") != NULL && (n1 = 1))) {
2729 /* Skip until the separator line is read (three or four lines) */
2733 snprintf(errbuf, errbufsize, "Line %d: the separator line at the top of the coordinates is not found: bad format?", lineNumber);
2736 ReadLine(buf, sizeof buf, fp, &lineNumber);
2737 } while (strstr(buf, "----------------------------") == NULL);
2738 for (i = 0; i < natoms; i++) {
2739 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2740 snprintf(errbuf, errbufsize, "Unexpected end of file in reading NSERCH data");
2743 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
2744 snprintf(errbuf, errbufsize, "Line %d: bad format in NSERCH coordinate data", lineNumber);
2747 vbuf[i].x = dval[1];
2748 vbuf[i].y = dval[2];
2749 vbuf[i].z = dval[3];
2751 ig = IntGroupNewWithPoints(nframes, 1, -1);
2752 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, natoms, vbuf, 0, NULL);
2753 IntGroupRelease(ig);
2756 optimizing = 1; /* Flag to skip reading the VEC group */
2760 } else if (strstr(buf, "E(UHF)") != NULL || (strstr(buf, "E(RHF)") != NULL && (n1 = 1)) || (strstr(buf, "E(ROHF)") != NULL && (n1 = 2))) {
2761 if (mol->bset == NULL) {
2762 i = MoleculeAllocateBasisSetRecord(mol, n1, 0, 0);
2764 snprintf(errbuf, errbufsize, "Line %d: cannot allocate basis set internal buffer", lineNumber);
2768 } else if (strncmp(buf, " $VEC", 5) == 0) {
2770 /* Read the vec group */
2771 if (mol->bset == NULL)
2772 continue; /* Just ignore */
2774 continue; /* Ignore VEC group during optimization */
2775 coeffs = (Double *)calloc(sizeof(Double), mol->bset->ncomps);
2776 if (coeffs == NULL) {
2777 snprintf(errbuf, errbufsize, "Line %d: low memory during $VEC", lineNumber);
2781 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2783 if (strncmp(buf, " $END", 5) == 0)
2785 while ((j = 5 + (k % 5) * 15) <= len && buf[j] != 0 && buf[j] != '\n') {
2786 strncpy(sval, buf + j, 15);
2788 coeffs[k] = strtod(sval, NULL);
2793 if (k < mol->bset->ncomps)
2795 j = MoleculeSetMOCoefficients(mol, i, -1000000, k, coeffs);
2797 snprintf(errbuf, errbufsize, "Line %d: cannot set coefficients for MO %d", lineNumber, i + 1);
2807 } else if ((strstr(buf, "ELECTRIC POTENTIAL") != NULL || strstr(buf, "ELECTROSTATIC POTENTIAL") != NULL) && strstr(buf, "ELPOTT") != NULL) {
2809 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
2811 if (strstr(buf, "TOTAL NUMBER OF GRID POINTS") != NULL)
2813 if (sscanf(buf, "%d %lf %lf %lf %lf", &ival[0], &dval[0], &dval[1], &dval[2], &dval[3]) < 5)
2815 ep = AssignArray(&mol->elpots, &mol->nelpots, sizeof(Elpot), i, NULL);
2816 ep->pos.x = dval[0];
2817 ep->pos.y = dval[1];
2818 ep->pos.z = dval[2];
2823 goto redo; /* This section has no end line, so the last line should be processed again */
2824 else break; /* End of file encountered or interrupted */
2825 } /* TODO: read MOLPLT info if present */
2828 snprintf(errbuf, errbufsize, "User interrupt at line %d", lineNumber);
2833 if (newmol && mol->nbonds == 0) {
2836 MoleculeGuessBonds(mol, 1.2, &nbonds, &bonds);
2838 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds);
2846 MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
2849 if (ftype == NULL || *ftype == 0) {
2851 cp = strrchr(fname, '.');
2855 cp = guessMoleculeType(fname);
2856 if (strcmp(cp, "???") != 0)
2860 if (strcasecmp(ftype, "pdb") == 0) {
2861 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
2864 /* Try all formats once again */
2865 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf, errbufsize);
2871 MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
2877 int i, j, new_unit, retval;
2882 if (errbuf == NULL) {
2887 fp = fopen(fname, "rb");
2889 snprintf(errbuf, errbufsize, "Cannot open file");
2892 /* flockfile(fp); */
2893 if (mp->natoms == 0)
2897 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2898 if (strncmp(buf, "END", 3) == 0)
2900 if (strncmp(buf, "HETATM", 6) == 0 || strncmp(buf, "ATOM", 4) == 0) {
2902 Int serial, intCharge, resSeq;
2905 char segName[5], resName[4], atomName[5], resSeqStr[5], atomType[3], element[3], occStr[6];
2907 memset(&w, 0, sizeof(w));
2908 ReadFormat(buf, "x6 I5 x1 S4 x1 S3 x1 x1 S4 x1 x3 F8 F8 F8 S6 F6 x6 S4 S2 I2",
2909 &w.serial, w.atomName, w.resName, w.resSeqStr, &w.r.x, &w.r.y, &w.r.z,
2910 w.occStr, &w.temp, w.segName, w.element, &w.intCharge);
2911 if (w.atomName[0] == 0) {
2912 continue; /* Atom name is empty */
2914 /* A workaround for residue number >= 10000 (XPLOR style) */
2915 if (w.resSeqStr[0] >= 'A' && w.resSeqStr[0] <= 'Z') {
2916 w.resSeq = (w.resSeqStr[0] - 'A' + 10) * 1000 + atoi(w.resSeqStr + 1);
2918 w.resSeq = atoi(w.resSeqStr);
2920 if (w.element[0] == 0) {
2921 /* $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl */
2922 for (p = w.atomName; *p != 0; p++) {
2923 if (isalpha(*p) && *p != '_') {
2924 w.element[0] = toupper(*p);
2925 if (isalpha(p[1]) && p[1] != '_') {
2926 w.element[1] = toupper(p[1]);
2935 if (w.occStr[0] == 0)
2938 w.occ = atof(w.occStr);
2939 if (w.serial <= 0) {
2940 snprintf(errbuf, errbufsize, "line %d: non-positive atom number %d", lineNumber, w.serial);
2944 w.serial--; /* The internal atom number is 0-based */
2945 if (w.serial >= mp->natoms) {
2947 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, w.serial, NULL);
2949 snprintf(errbuf, errbufsize, "line %d: the atom number %d does not exist in the structure file", lineNumber, w.serial+1);
2954 ap = ATOM_AT_INDEX(mp->atoms, w.serial);
2956 ap->occupancy = w.occ;
2957 ap->tempFactor = w.temp;
2959 if (w.segName[0] == 0)
2960 strncpy(w.segName, "MAIN", 4);
2961 strncpy(ap->segName, w.segName, 4);
2962 ap->resSeq = w.resSeq;
2963 strncpy(ap->resName, w.resName, 4);
2964 strncpy(ap->aname, w.atomName, 4);
2965 strncpy(ap->element, w.element, 2);
2966 ap->intCharge = w.intCharge;
2967 if (ap->resSeq > 0) {
2968 if (ap->resSeq < mp->nresidues) {
2969 /* Update the resName according to residues[] */
2970 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
2972 /* Register the resName to residues[] */
2973 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, w.resName);
2977 strcpy(ap->resName, "XXX");
2978 if (mp->nresidues == 0)
2979 AssignArray(&mp->residues, &mp->nresidues, 4, 0, ap->resName);
2981 i = ElementToInt(ap->element);
2983 ap->weight = gElementParameters[i].weight;
2986 } else if (strncmp(buf, "CONECT", 6) == 0 && new_unit) {
2987 i = ReadFormat(buf, "x6 I5I5I5I5I5I5I5I5I5I5I5I5",
2988 ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
2989 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7,
2990 ibuf + 8, ibuf + 9, ibuf + 10, ibuf + 11);
2994 for (j = 0; j < i; j++) {
2995 if (ibuf[j] < 0 || ibuf[j] > mp->natoms) {
2996 snprintf(errbuf, errbufsize, "line %d: The CONECT record contains non-existent atom %d", lineNumber, ibuf[j]);
2999 } else if (ibuf[j] == 0)
3005 for (j = 1, bi = 0; j < i; j++) {
3006 if (ibuf[0] < ibuf[j]) {
3007 bbuf[bi * 2] = ibuf[0] - 1;
3008 bbuf[bi * 2 + 1] = ibuf[j] - 1;
3015 retval = MoleculeAddBonds(mp, bi, bbuf);
3017 snprintf(errbuf, errbufsize, "line %d: bad bond specification", lineNumber);
3024 /* funlockfile(fp); */
3027 /* Renumber atoms if some atom number is unoccupied */
3028 int *old2new, oldidx, newidx;
3029 old2new = (int *)calloc(sizeof(int), mp->natoms);
3030 if (old2new == NULL) {
3031 snprintf(errbuf, errbufsize, "Out of memory");
3035 for (oldidx = newidx = 0; oldidx < mp->natoms; oldidx++) {
3036 ap = ATOM_AT_INDEX(mp->atoms, oldidx);
3037 if (ap->aname[0] != 0) {
3038 old2new[oldidx] = newidx;
3039 if (oldidx > newidx)
3040 memmove(ATOM_AT_INDEX(mp->atoms, newidx), ap, gSizeOfAtomRecord);
3044 mp->natoms = newidx;
3045 if (oldidx > newidx) {
3046 /* Renumber the connects and bonds */
3047 for (i = 0; i < mp->natoms; i++) {
3048 ap = ATOM_AT_INDEX(mp->atoms, i);
3049 for (j = 0; j < ap->nconnects; j++) {
3050 ap->connects[j] = old2new[ap->connects[j]];
3053 for (i = 0; i < mp->nbonds * 2; i++) {
3054 mp->bonds[i] = old2new[mp->bonds[i]];
3057 retval = MoleculeRebuildTablesFromConnects(mp);
3059 /* This error may not happen */
3060 snprintf(errbuf, errbufsize, "Cannot build angle/dihedral/improper tables");
3066 mp->nframes = -1; /* Should be recalculated later */
3068 return 1; /* No atoms */
3072 /* funlockfile(fp); */
3076 return 1; /* Maybe different format? */
3081 MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3084 SFloat32 *xp, *yp, *zp;
3089 if (mp == NULL || mp->natoms == 0) {
3090 snprintf(errbuf, errbufsize, "Molecule is empty");
3093 n = DcdOpen(fname, &dcd);
3096 case -2: snprintf(errbuf, errbufsize, "Cannot open file"); break;
3097 case 1: snprintf(errbuf, errbufsize, "Premature EOF encountered"); break;
3098 case 2: snprintf(errbuf, errbufsize, "Bad block length of the first section"); break;
3099 case 3: snprintf(errbuf, errbufsize, "\"CORD\" signature is missing"); break;
3100 case 4: snprintf(errbuf, errbufsize, "Bad termination of the first section"); break;
3101 case 5: snprintf(errbuf, errbufsize, "The title section is not correct"); break;
3102 case 6: snprintf(errbuf, errbufsize, "The atom number section is not correct"); break;
3103 default: snprintf(errbuf, errbufsize, "Read error in dcd file"); break;
3106 if (dcd.natoms == 0)
3107 snprintf(errbuf, errbufsize, "No atoms were found in the dcd file");
3108 else if (dcd.nframes == 0)
3109 snprintf(errbuf, errbufsize, "No frames were found in the dcd file");
3111 if (errbuf[0] != 0) {
3117 vp = (Vector *)calloc(sizeof(Vector), mp->natoms * dcd.nframes);
3118 xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3119 yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3120 zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3121 ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mp), dcd.nframes, -1);
3122 if (vp == NULL || xp == NULL || yp == NULL || zp == NULL || ig == NULL) {
3123 snprintf(errbuf, errbufsize, "Cannot allocate memory");
3128 if (ig) IntGroupRelease(ig);
3131 for (n = 0; n < dcd.nframes; n++) {
3134 if (DcdReadFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
3135 snprintf(errbuf, errbufsize, "Read error in dcd file");
3138 for (i = 0, vpp = &vp[n * mp->natoms]; i < dcd.natoms && i < mp->natoms; i++, vpp++) {
3144 /* TODO: implement frame-specific cells */
3145 if (MoleculeInsertFrames(mp, ig, vp, NULL) < 0)
3146 snprintf(errbuf, errbufsize, "Cannot insert frames");
3147 if (dcd.with_unitcell) {
3148 Vector ax, ay, az, orig;
3149 char flags[3] = {1, 1, 1};
3150 ax.x = dcd.globalcell[0]; ax.y = ax.z = 0;
3151 ay.y = dcd.globalcell[1]; ay.x = ay.z = 0;
3152 az.z = dcd.globalcell[2]; az.x = az.y = 0;
3153 orig.x = dcd.globalcell[3];
3154 orig.y = dcd.globalcell[4];
3155 orig.z = dcd.globalcell[5];
3156 if (MoleculeSetPeriodicBox(mp, &ax, &ay, &az, &orig, flags) != 0) {
3157 snprintf(errbuf, errbufsize, "Cannot set unit cell");
3161 mp->startStep = dcd.nstart;
3162 mp->stepsPerFrame = dcd.ninterval;
3163 mp->psPerStep = dcd.delta;
3170 IntGroupRelease(ig);
3177 MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3187 fp = fopen(fname, "rb");
3189 snprintf(errbuf, errbufsize, "Cannot open file");
3195 flags[0] = flags[1] = flags[2] = 0;
3196 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3197 if (strncmp(buf, "Bounding box:", 13) == 0) {
3198 for (i = 0; i < 3; i++) {
3199 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3200 snprintf(errbuf, errbufsize, "line %d: missing %d component of the bounding box", lineNumber, i + 1);
3204 n = sscanf(buf, "%lf %lf %lf %d", &d[0], &d[1], &d[2], &flag);
3206 vv.x = vv.y = vv.z = 0.0;
3208 case 0: vv.x = d[0]; break;
3209 case 1: vv.y = d[0]; break;
3210 case 2: vv.z = d[0]; break;
3212 if (n == 1 || (n == 2 && d[1] != 0.0))
3219 flags[i] = (flag != 0);
3221 flags[i] = (VecLength2(vv) != 0);
3225 if (mp->cell != NULL)
3226 vv = mp->cell->origin;
3228 vv.x = vv.y = vv.z = 0.0;
3229 MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags);
3230 } else if (strncmp(buf, "Bounding box origin:", 20) == 0) {
3231 if (mp->cell != NULL) {
3232 v[0] = mp->cell->axes[0];
3233 v[1] = mp->cell->axes[1];
3234 v[2] = mp->cell->axes[2];
3235 memmove(flags, mp->cell->flags, 3);
3237 v[0].x = 1.0; v[0].y = v[0].z = 0.0;
3238 v[1].y = 1.0; v[1].x = v[1].z = 0.0;
3239 v[2].z = 1.0; v[2].x = v[2].y = 0.0;
3240 flags[0] = flags[1] = flags[2] = 1.0;
3242 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0 || (n = sscanf(buf, "%lf %lf %lf", &d[0], &d[1], &d[2]) < 3)) {
3243 snprintf(errbuf, errbufsize, "line %d: wrong format for the bounding box origin", lineNumber);
3250 MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags);
3262 MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char *errbuf, int errbufsize)
3265 if (ftype == NULL || *ftype == 0) {
3267 cp = strrchr(fname, '.');
3271 cp = guessMoleculeType(fname);
3272 if (strcmp(cp, "???") != 0)
3276 if (strcasecmp(ftype, "psf") == 0) {
3277 retval = MoleculeWriteToPsfFile(mp, fname, errbuf, errbufsize);
3278 } else if (strcasecmp(ftype, "pdb") == 0) {
3279 retval = MoleculeWriteToPdbFile(mp, fname, errbuf, errbufsize);
3280 } else if (strcasecmp(ftype, "tep") == 0) {
3281 retval = MoleculeWriteToTepFile(mp, fname, errbuf, errbufsize);
3283 snprintf(errbuf, errbufsize, "The file format should be specified");
3287 MoleculeSetPath(mp, fname);
3292 MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3295 int i, j, k, n1, n2, n3;
3299 fp = fopen(fname, "wb");
3301 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
3306 fprintf(fp, "!:atoms\n");
3307 fprintf(fp, "! idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge\n");
3309 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3310 strncpy(bufs[0], ap->segName, 4);
3311 strncpy(bufs[1], ap->resName, 4);
3312 strncpy(bufs[2], ap->aname, 4);
3313 AtomTypeDecodeToString(ap->type, bufs[3]);
3314 strncpy(bufs[4], ap->element, 4);
3315 for (j = 0; j < 5; j++) {
3317 if (bufs[j][0] == 0) {
3321 for (k = 0; k < 4; k++) {
3322 if (bufs[j][k] > 0 && bufs[j][k] < ' ')
3326 if (SYMOP_ALIVE(ap->symop))
3328 if (ap->fix_force != 0)
3330 if (ap->mm_exclude || ap->periodic_exclude)
3332 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);
3337 fprintf(fp, "!:atoms_symop\n");
3338 fprintf(fp, "! idx symop symbase\n");
3339 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3341 n = ap->symop.sym * 1000000 + ap->symop.dx * 10000 + ap->symop.dy * 100 + ap->symop.dz;
3342 fprintf(fp, "%d %d %d\n", i, n, ap->symbase);
3348 fprintf(fp, "!:atoms_fix\n");
3349 fprintf(fp, "! idx fix_force fix_pos\n");
3350 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3351 fprintf(fp, "%d %f %f %f %f\n", i, ap->fix_force, ap->fix_pos.x, ap->fix_pos.y, ap->fix_pos.z);
3357 fprintf(fp, "!:mm_exclude\n");
3358 fprintf(fp, "! idx mm_exclude periodic_exclude\n");
3359 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3360 fprintf(fp, "%d %d %d\n", i, ap->mm_exclude, ap->periodic_exclude);
3365 if ((n1 = MoleculeGetNumberOfFrames(mp)) > 0)
3369 for (i = 0; (i == n2 || i < n1); i++) {
3370 fprintf(fp, "!:positions ; frame %d\n", i);
3371 fprintf(fp, "! idx x y z\n");
3372 for (j = 0, ap = mp->atoms; j < mp->natoms; j++, ap = ATOM_NEXT(ap)) {
3374 if (i != n2 && i < ap->nframes)
3375 vp = ap->frames + i;
3378 fprintf(fp, "%d %.8f %.8f %.8f\n", j, vp->x, vp->y, vp->z);
3383 if (mp->nbonds > 0) {
3384 fprintf(fp, "!:bonds\n");
3385 fprintf(fp, "! from1 to1 from2 to2 from3 to3 from4 to4\n");
3386 for (i = 0; i < mp->nbonds; i++) {
3387 fprintf(fp, "%d %d%c", mp->bonds[i * 2], mp->bonds[i * 2 + 1], (i % 4 == 3 || i == mp->nbonds - 1 ? '\n' : ' '));
3392 if (mp->nangles > 0) {
3393 fprintf(fp, "!:angles\n");
3394 fprintf(fp, "! a1 b1 c1 a2 b2 c2 a3 b3 c3\n");
3395 for (i = 0; i < mp->nangles; i++) {
3396 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' : ' '));
3401 if (mp->ndihedrals > 0) {
3402 fprintf(fp, "!:dihedrals\n");
3403 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3404 for (i = 0; i < mp->ndihedrals; i++) {
3405 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' : ' '));
3410 if (mp->nimpropers > 0) {
3411 fprintf(fp, "!:impropers\n");
3412 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3413 for (i = 0; i < mp->nimpropers; i++) {
3414 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' : ' '));
3419 if (mp->cell != NULL) {
3420 fprintf(fp, "!:periodic_box\n");
3421 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz; fa fb fc\n");
3422 for (i = 0; i < 3; i++)
3423 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->axes[i].x, mp->cell->axes[i].y, mp->cell->axes[i].z);
3424 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->origin.x, mp->cell->origin.y, mp->cell->origin.z);
3425 fprintf(fp, "%d %d %d\n", mp->cell->flags[0], mp->cell->flags[1], mp->cell->flags[2]);
3428 fprintf(fp, "!:xtalcell\n");
3429 fprintf(fp, "! a b c alpha beta gamma; this info is redundant, periodic_box is used instead\n");
3430 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]);
3434 if (mp->nsyms > 0) {
3435 fprintf(fp, "!:symmetry_operations\n");
3436 fprintf(fp, "! a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3\n");
3437 for (i = 0; i < mp->nsyms; i++) {
3438 Transform *tp = mp->syms + i;
3439 for (j = 0; j < 12; j++)
3440 fprintf(fp, "%11.6f%c", (*tp)[j], (j % 3 == 2 ? '\n' : ' '));
3445 if (mp->arena != NULL) {
3446 MDArena *arena = mp->arena;
3447 fprintf(fp, "!:md_parameters\n");
3448 fprintf(fp, "log_file %s\n", arena->log_result_name);
3449 fprintf(fp, "coord_file %s\n", arena->coord_result_name);
3450 fprintf(fp, "vel_file %s\n", arena->vel_result_name);
3451 fprintf(fp, "force_file %s\n", arena->force_result_name);
3452 fprintf(fp, "debug_file %s\n", arena->debug_result_name);
3453 fprintf(fp, "debug_output_level %d\n", arena->debug_output_level);
3454 fprintf(fp, "step %d\n", arena->step);
3455 fprintf(fp, "coord_output_freq %d\n", arena->coord_output_freq);
3456 fprintf(fp, "energy_output_freq %d\n", arena->energy_output_freq);
3457 fprintf(fp, "coord_frame %d\n", arena->coord_result_frame);
3458 fprintf(fp, "timestep %g\n", arena->timestep);
3459 fprintf(fp, "cutoff %g\n", arena->cutoff);
3460 fprintf(fp, "electro_cutoff %g\n", arena->electro_cutoff);
3461 fprintf(fp, "pairlist_distance %g\n", arena->pairlist_distance);
3462 fprintf(fp, "temperature %g\n", arena->temperature);
3463 fprintf(fp, "andersen_freq %d\n", arena->andersen_thermo_freq);
3464 fprintf(fp, "andersen_coupling %g\n", arena->andersen_thermo_coupling);
3465 fprintf(fp, "random_seed %d\n", arena->random_seed);
3466 fprintf(fp, "dielectric %g\n", arena->dielectric);
3467 fprintf(fp, "gradient_convergence %g\n", arena->gradient_convergence);
3468 fprintf(fp, "coordinate_convergence %g\n", arena->coordinate_convergence);
3469 fprintf(fp, "use_xplor_shift %d\n", arena->use_xplor_shift);
3470 fprintf(fp, "scale14_vdw %g\n", arena->scale14_vdw);
3471 fprintf(fp, "scale14_elect %g\n", arena->scale14_elect);
3472 fprintf(fp, "relocate_center %d\n", arena->relocate_center);
3473 fprintf(fp, "surface_probe_radius %g\n", arena->probe_radius);
3474 fprintf(fp, "surface_tension %g\n", arena->surface_tension);
3475 fprintf(fp, "surface_potential_freq %d\n", arena->surface_potential_freq);
3476 fprintf(fp, "use_graphite %d\n", arena->use_graphite);
3477 fprintf(fp, "alchemical_lambda %g\n", arena->alchem_lambda);
3478 fprintf(fp, "alchemical_delta_lambda %g\n", arena->alchem_dlambda);
3479 if (arena->nalchem_flags > 0) {
3480 fprintf(fp, "alchem_flags %d", arena->nalchem_flags);
3481 for (i = 0; i < arena->nalchem_flags; i++) {
3484 else if (i % 10 == 0)
3486 fputc('0' + arena->alchem_flags[i], fp);
3490 if (arena->pressure != NULL) {
3492 fprintf(fp, "pressure_freq %d\n", arena->pressure->freq);
3493 fprintf(fp, "pressure_coupling %g\n", arena->pressure->coupling);
3494 dp = arena->pressure->apply;
3495 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]);
3496 dp = arena->pressure->cell_flexibility;
3497 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]);
3498 fprintf(fp, "pressure_fluctuate_cell_origin %g\n", arena->pressure->fluctuate_cell_origin);
3499 fprintf(fp, "pressure_fluctuate_cell_orientation %g\n", arena->pressure->fluctuate_cell_orientation);
3503 if (mp->par != NULL) {
3504 Parameter *par = mp->par;
3505 fprintf(fp, "!:parameters\n");
3506 ParameterAppendToFile(par, fp);
3510 fprintf(fp, "!:velocity\n");
3511 fprintf(fp, "! idx vx vy vz\n");
3512 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3513 fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->v.x, ap->v.y, ap->v.z);
3517 fprintf(fp, "!:force\n");
3518 fprintf(fp, "! idx fx fy fz\n");
3519 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3520 fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->f.x, ap->f.y, ap->f.z);
3525 if (mp->mview != NULL) {
3527 if (mp->mview->track != NULL) {
3528 fprintf(fp, "!:trackball\n");
3529 fprintf(fp, "! scale; trx try trz; theta_deg x y z\n");
3530 f[0] = TrackballGetScale(mp->mview->track);
3531 fprintf(fp, "%f\n", f[0]);
3532 TrackballGetTranslate(mp->mview->track, f);
3533 fprintf(fp, "%f %f %f\n", f[0], f[1], f[2]);
3534 TrackballGetRotate(mp->mview->track, f);
3535 fprintf(fp, "%f %f %f %f\n", f[0], f[1], f[2], f[3]);
3538 fprintf(fp, "!:view\n");
3539 fprintf(fp, "show_unit_cell %d\n", mp->mview->showUnitCell);
3540 fprintf(fp, "show_periodic_box %d\n", mp->mview->showPeriodicBox);
3541 fprintf(fp, "show_expanded_atoms %d\n", mp->mview->showExpandedAtoms);
3542 fprintf(fp, "show_ellipsoids %d\n", mp->mview->showEllipsoids);
3543 fprintf(fp, "show_hydrogens %d\n", mp->mview->showHydrogens);
3544 fprintf(fp, "show_dummy_atoms %d\n", mp->mview->showDummyAtoms);
3545 fprintf(fp, "show_rotation_center %d\n", mp->mview->showRotationCenter);
3546 fprintf(fp, "show_graphite_flag %d\n", mp->mview->showGraphiteFlag);
3547 fprintf(fp, "show_graphite %d\n", mp->mview->showGraphite);
3548 fprintf(fp, "show_periodic_image_flag %d\n", mp->mview->showPeriodicImageFlag);
3549 fprintf(fp, "show_periodic_image %d %d %d %d %d %d\n",
3550 mp->mview->showPeriodicImage[0], mp->mview->showPeriodicImage[1],
3551 mp->mview->showPeriodicImage[2], mp->mview->showPeriodicImage[3],
3552 mp->mview->showPeriodicImage[4], mp->mview->showPeriodicImage[5]);
3561 MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3566 fp = fopen(fname, "wb");
3568 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
3572 fprintf(fp, "PSF\n\n");
3573 fprintf(fp, " 1 !NTITLE\n");
3574 fprintf(fp, " REMARKS FILENAME=\n");
3578 fprintf(fp, "%8d !NATOM\n", mp->natoms);
3579 for (i = 0; i < mp->natoms; i++) {
3581 ap = ATOM_AT_INDEX(mp->atoms, i);
3582 fprintf(fp, "%8d ", i + 1);
3583 if (ap->resSeq >= 10000) {
3584 fmt = "%-3.3s %-5d ";
3586 fmt = "%-4.4s %-4d ";
3588 fprintf(fp, fmt, ap->segName, ap->resSeq);
3589 fprintf(fp, "%-3.3s %-4.4s %-4.4s %12.6f %8.4f 0\n",
3590 ap->resName, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->charge, ap->weight);
3595 fprintf(fp, "%8d !NBOND: bonds\n", mp->nbonds);
3596 for (i = 0; i < mp->nbonds * 2; i++) {
3597 fprintf(fp, "%8d", mp->bonds[i] + 1);
3606 fprintf(fp, "%8d !NTHETA: angles\n", mp->nangles);
3607 for (i = 0; i < mp->nangles * 3; i++) {
3608 fprintf(fp, "%8d", mp->angles[i] + 1);
3617 fprintf(fp, "%8d !NPHI: dihedrals\n", mp->ndihedrals);
3618 for (i = 0; i < mp->ndihedrals * 4; i++) {
3619 fprintf(fp, "%8d", mp->dihedrals[i] + 1);
3628 fprintf(fp, "%8d !NIMPHI: impropers\n", mp->nimpropers);
3629 for (i = 0; i < mp->nimpropers * 4; i++) {
3630 fprintf(fp, "%8d", mp->impropers[i] + 1);
3638 fprintf(fp, "%8d !NDON: donors\n\n", 0);
3639 fprintf(fp, "%8d !NACC: acceptors\n\n", 0);
3640 fprintf(fp, "%8d !NNB: non-bonding exclusions\n\n", 0);
3641 for (i = 0; i < mp->natoms; i++) {
3642 fprintf(fp, "%8d", 0);
3649 fprintf(fp, "%8d !NGRP: groups\n", 1);
3650 fprintf(fp, " 0 0 0\n");
3654 if (i > 5 && strcmp(fname + i - 5, ".psfx") == 0) {
3655 /* Extended psf (with coordinates and other info) */
3656 fprintf(fp, "%8d !COORD: coordinates\n", mp->natoms);
3657 for (i = 0; i < mp->natoms; i++) {
3659 ap = ATOM_AT_INDEX(mp->atoms, i);
3661 fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->aname);
3665 if (mp->nframes > 0) {
3666 int fn; /* Frame number */
3667 for (fn = 0; fn < ap->nframes; fn++) {
3668 fprintf(fp, "%8d !COORD: coordinates for frame %d\n", mp->natoms, fn);
3669 for (i = 0; i < mp->natoms; i++) {
3671 ap = ATOM_AT_INDEX(mp->atoms, i);
3672 if (ap->frames == NULL || fn >= ap->nframes)
3676 fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->name);
3689 MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3694 fp = fopen(fname, "wb");
3696 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
3700 for (i = 0; i < mp->natoms; i++) {
3702 ap = ATOM_AT_INDEX(mp->atoms, i);
3703 if (ap->resSeq >= 10000) {
3704 snprintf(buf, sizeof buf, "%c%03d", 'A' + (ap->resSeq - 10000) / 1000, ap->resSeq % 1000);
3706 snprintf(buf, sizeof buf, "%4d", ap->resSeq);
3708 fprintf(fp, "ATOM %5d %-4.4s%1.1s%-3.3s %1.1s%4.4s%1.1s "
3709 "%8.3f%8.3f%8.3f %5.2f %5.2f "
3710 "%-4.4s%-2.2s%-2d\n",
3711 i + 1, ap->aname, " ", ap->resName, " ", buf, " ",
3712 ap->r.x, ap->r.y, ap->r.z, ap->occupancy, ap->tempFactor,
3713 ap->segName, ap->element, ap->intCharge);
3715 for (i = 0; i < mp->natoms; i++) {
3716 ap = ATOM_AT_INDEX(mp->atoms, i);
3717 for (j = 0; j < ap->nconnects; j++) {
3721 fprintf(fp, "CONECT%5d", i + 1);
3723 fprintf(fp, "%5d", ap->connects[j] + 1);
3728 fprintf(fp, "END\n");
3734 MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3737 SFloat32 *xp, *yp, *zp;
3740 if (mp == NULL || mp->natoms == 0) {
3741 snprintf(errbuf, errbufsize, "Molecule is empty");
3744 memset(&dcd, 0, sizeof(dcd));
3745 dcd.natoms = mp->natoms;
3746 dcd.nframes = MoleculeGetNumberOfFrames(mp);
3747 if (dcd.nframes == 0) {
3748 snprintf(errbuf, errbufsize, "no frame is present");
3751 dcd.nstart = mp->startStep;
3752 dcd.ninterval = mp->stepsPerFrame;
3753 if (dcd.ninterval == 0)
3755 dcd.nend = dcd.nstart + (dcd.nframes - 1) * dcd.ninterval;
3756 if (mp->cell != NULL) {
3757 dcd.with_unitcell = 1;
3758 dcd.globalcell[0] = VecLength(mp->cell->axes[0]);
3759 dcd.globalcell[1] = VecLength(mp->cell->axes[1]);
3760 dcd.globalcell[2] = VecLength(mp->cell->axes[2]);
3761 dcd.globalcell[3] = mp->cell->origin.x;
3762 dcd.globalcell[4] = mp->cell->origin.y;
3763 dcd.globalcell[5] = mp->cell->origin.z;
3765 dcd.delta = mp->psPerStep;
3766 if (dcd.delta == 0.0)
3768 n = DcdCreate(fname, &dcd);
3771 snprintf(errbuf, errbufsize, "Cannot create dcd file");
3773 snprintf(errbuf, errbufsize, "Cannot write dcd header");
3778 xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3779 yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3780 zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3781 if (xp == NULL || yp == NULL || zp == NULL) {
3782 snprintf(errbuf, errbufsize, "Cannot allocate memory");
3789 for (n = 0; n < dcd.nframes; n++) {
3792 for (i = 0, ap = mp->atoms; i < dcd.natoms && mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3794 if (ap->frames == NULL || n >= ap->nframes)
3802 if (i < dcd.natoms) {
3803 size_t sz = (dcd.natoms - i) * sizeof(SFloat32);
3804 memset(xp + i, 0, sz);
3805 memset(yp + i, 0, sz);
3806 memset(zp + i, 0, sz);
3808 if (DcdWriteFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
3809 snprintf(errbuf, errbufsize, "Write error in dcd file");
3825 MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
3831 fp = fopen(fname, "wb");
3833 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
3836 if (mp->cell != NULL) {
3837 fprintf(fp, "Bounding box:\n");
3838 for (i = 0; i < 3; i++) {
3839 v = mp->cell->axes[i];
3840 fprintf(fp, "%.3f %.3f %.3f %d\n", v.x, v.y, v.z, mp->cell->flags[i]);
3842 fprintf(fp, "Bounding box origin:\n");
3843 v = mp->cell->origin;
3844 fprintf(fp, "%.3f %.3f %.3f\n", v.x, v.y, v.z);
3851 sCompareByElement(const void *ap, const void *bp)
3853 return ((*(Atom **)bp)->atomicNumber - (*(Atom **)ap)->atomicNumber);
3857 sMakeAdc(int n, int base, Symop symop)
3860 if (SYMOP_ALIVE(symop)) {
3862 sym = (symop.dx + 5) * 10000 + (symop.dy + 5) * 1000 + (symop.dz + 5) * 100 + symop.sym + 1;
3867 return (an + 1) * 100000 + sym;
3871 sCompareAdc(const void *ap, const void *bp)
3873 int n = *((Int *)ap) % 100000 - *((Int *)bp) % 100000;
3875 n = *((Int *)ap) / 100000 - *((Int *)bp) / 100000;
3880 sOutputAtomListInstructions(FILE *fp, int natoms, Atom *atoms)
3882 int i, j, k, an, sym;
3885 adc = (Int *)malloc(sizeof(Int) * natoms);
3888 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
3889 if (ap->exflags & kAtomHiddenFlag)
3891 adc[i] = sMakeAdc(i, ap->symbase, ap->symop);
3893 mergesort(adc, natoms, sizeof(Int), sCompareAdc);
3895 /* Create the atom list */
3897 for (i = j = k = 0; i < natoms; i++) {
3898 int an1 = adc[i] / 100000;
3899 int sym1 = adc[i] % 100000;
3900 if (sym == sym1 && an1 == an + 1) {
3907 /* Output the last atom with a minus sign */
3908 adc[j++] = -(an * 100000 + sym);
3909 /* Output this atom */
3916 adc[j++] = -(an * 100000 + sym);
3918 /* Create the instruction cards */
3919 for (i = k = 0; i < j; i++) {
3921 fprintf(fp, " 401");
3922 fprintf(fp, "%9d", adc[i]);
3924 if (i == j - 1 || k == 6 || (k == 5 && i < j - 2 && adc[i + 2] < 0)) {
3933 sEllipsoidType(int an)
3935 return (an >= 18 ? 3 : (an >= 2 && an != 6 ? 2 : (an > 0 ? 1 : 0)));
3939 sOutputAtomTypeInstructions(FILE *fp, int natoms, Atom *atoms)
3943 int etype, elast, istart, ilast, n1, n2;
3944 elast = istart = ilast = -1;
3945 for (i = 0, ap = atoms; i <= natoms; i++, ap++) {
3947 if (SYMOP_ALIVE(ap->symop))
3949 if (ap->exflags & kAtomHiddenFlag)
3951 etype = sEllipsoidType(ap->atomicNumber);
3956 } else if (elast == etype && ilast == i - 1) {
3961 /* Output the instruction card for the 'last' block of atoms */
3964 n1 = 4; n2 = 0; break;
3966 n1 = 4; n2 = 5; break;
3968 n1 = 1; n2 = 0; break;
3970 fprintf(fp, " 1 715 %8d 0 %8d 0 0.100 0.000 0.000\n", n1, n2);
3971 fprintf(fp, " %9d%9d\n", istart + 1, ilast + 1);
3978 sCompareBondType(const void *ap, const void *bp)
3980 /* Descending order */
3981 return *((int *)bp) - *((int *)ap);
3985 sOutputBondInstructions(FILE *fp, int natoms, Atom *atoms, int overlap_correction)
3989 int i, j, n[5], an, count, n1, n2, k;
3992 static const float sBondRad[4] = {0.060, 0.060, 0.060, 0.040};
3993 static const int sBondShade[4] = {5, 3, 1, 1};
3995 n[0] = n[1] = n[2] = n[3] = 0; /* Start index of 3rd row atoms (and higher), 2nd row, 1st row, and H */
3997 for (i = natoms - 1, ap = atoms + i; i >= 0; i--, ap--) {
3998 an = ap->atomicNumber;
4010 if (overlap_correction)
4011 strcpy(buf, " 2 1001 0.000\n");
4013 strcpy(buf, " 2 812\n");
4015 for (i = 0; i < 4; i++) {
4016 for (j = i; j < 4; j++) {
4017 /* Examine bonds between "group i" and "group j" */
4020 double min_bond = 10000.0; /* Minimum distance between bound atoms */
4021 double min_nonbond = 10000.0; /* Minimum distance between non-bound atoms */
4022 double max_bond = -10000.0; /* Maximum distance between bound atoms */
4023 int count_exbond = 0; /* Number of explicit bonds in this group */
4024 for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
4025 for (n2 = n[j], ap2 = atoms + n2; n2 < n[j + 1]; n2++, ap2++) {
4028 VecSub(dr, ap->r, ap2->r);
4030 for (k = ap->nconnects - 1; k >= 0; k--) {
4031 if (ap->connects[k] == n2)
4035 /* n1 and n2 are bound */
4041 /* n1 and n2 are not bound */
4042 if (d < min_nonbond)
4047 if (min_bond == 10000.0)
4048 continue; /* No bonds between these groups */
4050 if (max_bond + 0.002 < min_nonbond)
4053 max_bond = min_nonbond - 0.002;
4054 /* Some bonds may be omitted, so scan all bonds again */
4055 for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
4056 for (k = ap->nconnects - 1; k >= 0; k--) {
4057 n2 = ap->connects[k];
4058 if (n2 < n[j] || n2 >= n[j + 1])
4061 VecSub(dr, ap->r, ap2->r);
4064 /* This bond should be explicitly defined */
4066 if (count_exbond == 0) {
4067 adc1 = -(i + 1); /* Bond type */
4068 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4070 adc1 = sMakeAdc(n1, ap->symbase, ap->symop);
4071 adc2 = sMakeAdc(n2, ap2->symbase, ap2->symop);
4072 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4073 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc2);
4079 /* Output the last instruction card */
4081 /* Make a new trailer card */
4082 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]);
4087 /* Output the last trailer card */
4092 if (count == 0 && overlap_correction) {
4093 /* 1001 card is not yet written, so write it */
4097 snprintf(buf, sizeof(buf), " 1 %3d", (overlap_correction ? 821 : 811));
4098 k = -exbonds[0] - 1; /* Bond type for the first block */
4099 i = 1; /* Index for exbonds[] */
4100 j = 0; /* Count in this block */
4101 while (i <= nexbonds) {
4102 if (j >= 29 || i == nexbonds || exbonds[i] < 0) {
4106 /* The trailer card */
4107 fprintf(fp, " %3d %6.3f\n", sBondShade[k], sBondRad[k]);
4111 k = -exbonds[i++] - 1; /* The new bond type */
4113 } else if (j > 0 && j % 3 == 0) {
4119 snprintf(buf + 9 + (j % 3) * 18, sizeof(buf) - 9 - (j % 3) * 18, "%9d%9d\n", n1, n2);
4128 /* Explicit bond table, sorted by bond type */
4129 for (i = j = 0; i < mp->nbonds; i++) {
4130 n1 = mp->bonds[i * 2];
4131 n2 = mp->bonds[i * 2 + 1];
4132 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
4133 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
4134 if ((ap1->exflags & kAtomHiddenFlag) || (ap2->exflags & kAtomHiddenFlag))
4136 if (ap1->atomicNumber > 18 || ap2->atomicNumber > 18) {
4138 } else if (ap1->atomicNumber > 1 && ap1->atomicNumber > 1) {
4144 ip[j * 3 + 1] = sMakeAdc(n1, ap1->symbase, ap1->symop);
4145 ip[j * 3 + 2] = sMakeAdc(n2, ap2->symbase, ap2->symop);
4148 mergesort(ip, j, sizeof(int) * 3, sCompareBondType);
4150 /* Output instruction cards */
4151 strcpy(buf, " 1 811");
4152 for (i = n1 = 0; i < j; i++) {
4153 n2 = (n1 % 3) * 18 + 9;
4154 snprintf(buf + n2, 80 - n2, "%9d%9d\n", ip[i * 3 + 1], ip[i * 3 + 2]);
4155 if (i == j - 1 || n1 >= 29 || ip[i * 3] != ip[i * 3 + 3]) {
4156 /* End of this instruction */
4159 switch (ip[i * 3]) {
4160 case 3: rad = 0.06; nshades = 5; break;
4161 case 2: rad = 0.06; nshades = 1; break;
4162 default: rad = 0.04; nshades = 1; break;
4164 fprintf(fp, " %3d %6.3f\n", nshades, rad);
4165 strcpy(buf, " 1 811");
4168 } else if (n1 % 3 == 2) {
4179 MoleculeWriteToTepFile(Molecule *mp, const char *fname, char *errbuf, int errbufsize)
4182 int i, j, natoms, *ip;
4183 Atom *ap, *atoms, **app;
4188 /* Create sorted array of atoms */
4189 natoms = mp->natoms;
4190 atoms = (Atom *)calloc(sizeof(Atom), natoms);
4191 app = (Atom **)calloc(sizeof(Atom *), natoms);
4192 ip = (int *)calloc(sizeof(int), natoms);
4193 if (atoms == NULL || app == NULL || ip == NULL) {
4194 snprintf(errbuf, errbufsize, "Cannot allocate memory");
4197 /* Sort the atom pointer by atomic number */
4198 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
4200 mergesort(app, natoms, sizeof(Atom *), sCompareByElement);
4201 for (i = 0; i < natoms; i++) {
4202 /* ip[old_index] is new_index */
4203 ip[app[i] - mp->atoms] = i;
4205 /* Move the atom record to atoms[] */
4206 /* aniso and frames share the memory with the original */
4207 /* The 'v' member contains crystallographic coordinates */
4208 /* The connection table and symbase are renumbered */
4209 /* Hidden flags are modified to reflect the visibility in the MainView */
4210 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4211 memmove(ap, app[i], gSizeOfAtomRecord);
4212 MoleculeCartesianToXtal(mp, &(ap->v), &(ap->r));
4213 for (j = ap->nconnects - 1; j >= 0; j--) {
4214 ap->connects[j] = ip[ap->connects[j]];
4216 if (SYMOP_ALIVE(ap->symop))
4217 ap->symbase = ip[ap->symbase];
4218 if (MainView_isAtomHidden(mp->mview, i)) {
4219 ap->exflags |= kAtomHiddenFlag;
4221 ap->exflags &= ~kAtomHiddenFlag;
4227 fp = fopen(fname, "wb");
4229 snprintf(errbuf, errbufsize, "Cannot write to file %s", fname);
4235 fprintf(fp, "Generated by MyMolView\n");
4238 if (mp->cell != NULL) {
4239 cp = mp->cell->cell;
4241 static Double sUnit[] = {1, 1, 1, 90, 90, 90};
4244 fprintf(fp, "%9.3f%9.3f%9.3f%9.3f%9.3f%9.3f\n", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
4246 /* Symmetry operations */
4247 if (mp->nsyms > 0) {
4248 for (i = 0; i < mp->nsyms; i++) {
4250 fprintf(fp, "%c%14g%3g%3g%3g%15g%3g%3g%3g%15g%3g%3g%3g\n", (i == mp->nsyms - 1 ? '1' : ' '), cp[9], cp[0], cp[1], cp[2], cp[10], cp[3], cp[4], cp[5], cp[11], cp[6], cp[7], cp[8]);
4253 fprintf(fp, "1 0 1 0 0 0 0 1 0 0 0 0 1\n");
4257 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4258 /* The 'v' field contains crystallographic coordinates */
4259 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);
4260 if (ap->aniso != NULL) {
4261 cp = ap->aniso->bij;
4262 fprintf(fp, " %8.5f%9.6f%9.6f%9.6f%9.6f%9.6f%9d\n", cp[0], cp[1], cp[2], cp[3], cp[5], cp[4], 0);
4264 Double temp = ap->tempFactor;
4267 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", temp, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4270 /* Special points */
4272 Vector camera, lookat, up, xvec, yvec, zvec;
4273 MainView_getCamera(mp->mview, &camera, &lookat, &up);
4274 VecSub(zvec, lookat, camera);
4275 VecCross(xvec, zvec, up);
4276 NormalizeVec(&xvec, &xvec);
4277 NormalizeVec(&yvec, &up);
4278 VecInc(xvec, lookat);
4279 VecInc(yvec, lookat);
4280 MoleculeCartesianToXtal(mp, &lookat, &lookat);
4281 MoleculeCartesianToXtal(mp, &xvec, &xvec);
4282 MoleculeCartesianToXtal(mp, &yvec, &yvec);
4283 fprintf(fp, " ORGN %9g%9g%9g 0\n", 0.0, 0.0, 0.0);
4284 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4285 fprintf(fp, " CNTR %9g%9g%9g 0\n", lookat.x, lookat.y, lookat.z);
4286 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4287 fprintf(fp, " X %9g%9g%9g 0\n", xvec.x, xvec.y, xvec.z);
4288 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4289 fprintf(fp, " Y %9g%9g%9g 0\n", yvec.x, yvec.y, yvec.z);
4290 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);
4294 fprintf(fp, " 201\n");
4295 fprintf(fp, " 205 12\n");
4296 fprintf(fp, " 301 6.6 6.6 0 0.8\n");
4297 sOutputAtomListInstructions(fp, natoms, atoms);
4298 fprintf(fp, " 501%4d55501%4d55501%4d55501%4d55501%4d55501 1\n", natoms + 2, natoms + 2, natoms + 3, natoms + 2, natoms + 4);
4299 fprintf(fp, " 502 1 0.0 2 0.0 3 0.0\n");
4300 fprintf(fp, " 604 1.538\n");
4302 sOutputBondInstructions(fp, natoms, atoms, 1);
4303 sOutputAtomTypeInstructions(fp, natoms, atoms);
4304 sOutputBondInstructions(fp, natoms, atoms, 0);
4306 fprintf(fp, " 202\n");
4307 fprintf(fp, " 0 -1\n");
4313 MoleculeDump(Molecule *mol)
4317 for (i = 0; i < mol->natoms; i++) {
4319 ap = ATOM_AT_INDEX(mol->atoms, i);
4320 snprintf(buf1, sizeof buf1, "%3.4s.%d", ap->resName, ap->resSeq);
4321 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);
4322 for (j = 0; j < ap->nconnects; j++) {
4323 fprintf(stderr, "%s%d", (j > 0 ? "," : ""), ap->connects[j]);
4325 fprintf(stderr, "]\n");
4329 #pragma mark ====== MD support (including modification of Molecule) ======
4331 /* Call md_prepare for the MDArena. If MDArena has not been created, a new arena is created.
4332 If something goes wrong, returns 1 (for missing parameters) or -1 (more serious error).
4333 If retmsg is not NULL, a message describing the problem is returned there. This message
4334 must be free'd by the caller. */
4336 MoleculePrepareMDArena(Molecule *mol, int check_only, char **retmsg)
4339 Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
4341 IntGroup *ig1, *ig2, *ig3;
4342 MDArena *arena = mol->arena;
4344 if (arena == NULL) {
4347 } else if (arena->xmol != mol)
4348 md_arena_set_molecule(arena, mol);
4350 arena->is_initialized = 0;
4352 /* Rebuild the tables */
4353 ig1 = ig2 = ig3 = NULL;
4354 nangles = MoleculeFindMissingAngles(mol, &angles);
4355 ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
4356 nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
4358 ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
4359 MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
4361 IntGroupRelease(ig1);
4363 if (ndihedrals > 0) {
4364 ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
4365 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
4367 IntGroupRelease(ig2);
4369 if (nimpropers > 0) {
4370 ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
4371 MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
4373 IntGroupRelease(ig3);
4376 /* Prepare parameters and internal information */
4377 msg = md_prepare(arena, check_only);
4379 /* Some parameters are missing? */
4381 if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
4385 asprintf(retmsg, "cannot initialize for MD: %s", msg);
4390 /* The local parameter list is updated */
4393 if (mol->par == NULL)
4394 mol->par = ParameterNew();
4395 for (parType = kFirstParType; parType <= kLastParType; parType++) {
4396 /* Delete global and undefined parameters */
4397 UnionPar *up, *upbuf;
4399 ig1 = IntGroupNew();
4400 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
4401 if (up->bond.src != 0)
4402 IntGroupAdd(ig1, idx, 1);
4404 if (IntGroupGetCount(ig1) > 0)
4405 MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
4406 IntGroupRelease(ig1);
4407 /* Copy global and undefined parameters from arena and insert to mol->par */
4408 nparams = ParameterGetCountForType(arena->par, parType);
4411 upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
4412 ig1 = IntGroupNew();
4413 ig2 = IntGroupNew();
4414 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
4415 if (up->bond.src > 0)
4416 IntGroupAdd(ig1, idx, 1); /* Global parameter */
4417 else if (up->bond.src < 0)
4418 IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
4420 if ((count = IntGroupGetCount(ig1)) > 0) {
4421 /* Insert global parameters (at the top) */
4422 ParameterCopy(arena->par, parType, upbuf, ig1);
4423 ig3 = IntGroupNewWithPoints(0, count, -1);
4424 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
4425 IntGroupRelease(ig3);
4427 if ((count = IntGroupGetCount(ig2)) > 0) {
4428 /* Insert undefined parameters (at the bottom) */
4429 ParameterCopy(arena->par, parType, upbuf, ig2);
4430 idx = ParameterGetCountForType(mol->par, parType);
4431 ig3 = IntGroupNewWithPoints(idx, count, -1);
4432 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
4433 IntGroupRelease(ig3);
4435 IntGroupRelease(ig2);
4436 IntGroupRelease(ig1);
4439 mol->needsMDRebuild = 0; /* We know the "modified" parameters are consistent with the MDArena */
4444 *retmsg = strdup(msg);
4449 #pragma mark ====== Serialize ======
4452 MoleculeDeserialize(const char *data, Int length, Int *timep)
4461 par = ParameterNew();
4465 while (length >= 12) {
4466 const char *ptr = data + 8 + sizeof(Int);
4467 int len = *((const Int *)(data + 8));
4469 if (strcmp(data, "ATOM") == 0) {
4470 n = len / gSizeOfAtomRecord;
4471 NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, n);
4472 memmove(mp->atoms, ptr, len);
4473 } else if (strcmp(data, "ANISO") == 0) {
4475 n = len / (sizeof(Int) + sizeof(Aniso));
4476 for (i = 0; i < n; i++) {
4477 j = *((const Int *)ptr);
4478 if (j < 0 || j >= mp->natoms)
4480 ap = ATOM_AT_INDEX(mp->atoms, j);
4481 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
4482 if (ap->aniso == NULL)
4484 *(ap->aniso) = *((Aniso *)(ptr + sizeof(Int)));
4485 ptr += sizeof(Int) + sizeof(Aniso);
4487 } else if (strcmp(data, "FRAME") == 0) {
4489 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4490 if (ap->nframes == 0)
4492 ap->frames = (Vector *)malloc(sizeof(Vector) * ap->nframes);
4493 if (ap->frames == NULL)
4495 memmove(ap->frames, ptr, sizeof(Vector) * ap->nframes);
4496 ptr += sizeof(Vector) * ap->nframes;
4498 } else if (strcmp(data, "BOND") == 0) {
4499 n = len / (sizeof(Int) * 2);
4500 NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, n);
4501 memmove(mp->bonds, ptr, len);
4502 } else if (strcmp(data, "ANGLE") == 0) {
4503 n = len / (sizeof(Int) * 3);
4504 NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, n);
4505 memmove(mp->angles, ptr, len);
4506 } else if (strcmp(data, "DIHED") == 0) {
4507 n = len / (sizeof(Int) * 4);
4508 NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, n);
4509 memmove(mp->dihedrals, ptr, len);
4510 } else if (strcmp(data, "IMPROP") == 0) {
4511 n = len / (sizeof(Int) * 4);
4512 NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, n);
4513 memmove(mp->impropers, ptr, len);
4514 } else if (strcmp(data, "RESIDUE") == 0) {
4516 NewArray(&mp->residues, &mp->nresidues, 4, n);
4517 memmove(mp->residues, ptr, len);
4518 } else if (strcmp(data, "CELL") == 0) {
4519 mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
4520 if (mp->cell == NULL)
4522 memmove(mp->cell, ptr, sizeof(XtalCell));
4523 } else if (strcmp(data, "SYMOP") == 0) {
4524 n = len / sizeof(Transform);
4525 NewArray(&mp->syms, &mp->nsyms, sizeof(Transform), n);
4526 memmove(mp->syms, ptr, len);
4527 } else if (strcmp(data, "EXATOM") == 0) {
4528 n = len / sizeof(ExAtom);
4529 NewArray(&mp->exatoms, &mp->nexatoms, sizeof(ExAtom), n);
4530 memmove(mp->exatoms, ptr, len);
4531 } else if (strcmp(data, "EXBOND") == 0) {
4532 n = len / (sizeof(Int) * 2);
4533 NewArray(&mp->exbonds, &mp->nexbonds, sizeof(Int) * 2, n);
4534 memmove(mp->exbonds, ptr, len);
4535 } else if (strcmp(data, "TIME") == 0) {
4537 *timep = *((Int *)ptr);
4538 } else if (strcmp(data, "BONDPAR") == 0) {
4540 n = len / sizeof(BondPar);
4541 NewArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), n);
4542 memmove(par->bondPars, ptr, len);
4543 } else if (strcmp(data, "ANGPAR") == 0) {
4545 n = len / sizeof(AnglePar);
4546 NewArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), n);
4547 memmove(par->anglePars, ptr, len);
4548 } else if (strcmp(data, "DIHEPAR") == 0) {
4550 n = len / sizeof(TorsionPar);
4551 NewArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), n);
4552 memmove(par->dihedralPars, ptr, len);
4553 } else if (strcmp(data, "IMPRPAR") == 0) {
4555 n = len / sizeof(TorsionPar);
4556 NewArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), n);
4557 memmove(par->improperPars, ptr, len);
4558 } else if (strcmp(data, "VDWPAR") == 0) {
4560 n = len / sizeof(VdwPar);
4561 NewArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), n);
4562 memmove(par->vdwPars, ptr, len);
4563 } else if (strcmp(data, "VDWPPAR") == 0) {
4565 n = len / sizeof(VdwPairPar);
4566 NewArray(&par->vdwpPars, &par->nvdwpPars, sizeof(VdwPairPar), n);
4567 memmove(par->vdwpPars, ptr, len);
4568 } else if (strcmp(data, "VCUTPAR") == 0) {
4570 n = len / sizeof(VdwCutoffPar);
4571 NewArray(&par->vdwCutoffPars, &par->nvdwCutoffPars, sizeof(VdwCutoffPar), n);
4572 memmove(par->vdwCutoffPars, ptr, len);
4574 len += 8 + sizeof(Int);
4578 if (mp->par == NULL)
4579 ParameterRelease(par);
4580 /* result = MoleculeRebuildTablesFromConnects(mp);
4586 Panic("Low memory while deserializing molecule data");
4587 return NULL; /* Not reached */
4590 Panic("internal error: bad format during deserializing molecule data");
4591 return NULL; /* Not reached */
4595 MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep)
4598 int len, len_all, i, naniso, nframes;
4600 /* Array of atoms */
4601 len = 8 + sizeof(Int) + gSizeOfAtomRecord * mp->natoms;
4602 ptr = (char *)malloc(len);
4605 memmove(ptr, "ATOM\0\0\0\0", 8);
4606 *((Int *)(ptr + 8)) = gSizeOfAtomRecord * mp->natoms;
4607 p = ptr + 8 + sizeof(Int);
4608 memmove(p, mp->atoms, gSizeOfAtomRecord * mp->natoms);
4609 naniso = nframes = 0;
4610 for (i = 0; i < mp->natoms; i++) {
4611 Atom *ap = ATOM_AT_INDEX(p, i);
4612 if (ap->aniso != NULL) {
4616 if (ap->frames != NULL) {
4617 nframes += ap->nframes;
4623 /* Array of aniso */
4625 len = 8 + sizeof(Int) + (sizeof(Int) + sizeof(Aniso)) * naniso;
4626 ptr = (char *)realloc(ptr, len_all + len);
4630 memmove(p, "ANISO\0\0\0", 8);
4631 *((Int *)(p + 8)) = (sizeof(Int) + sizeof(Aniso)) * naniso;
4632 p += 8 + sizeof(Int);
4633 for (i = 0; i < mp->natoms; i++) {
4634 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
4635 if (ap->aniso != NULL) {
4637 *((Aniso *)(p + sizeof(Int))) = *(ap->aniso);
4638 p += sizeof(Int) + sizeof(Aniso);
4644 /* Array of frames */
4646 len = 8 + sizeof(Int) + sizeof(Vector) * nframes;
4647 ptr = (char *)realloc(ptr, len_all + len);
4651 memmove(p, "FRAME\0\0\0", 8);
4652 *((Int *)(p + 8)) = sizeof(Vector) * nframes;
4653 p += 8 + sizeof(Int);
4654 for (i = 0; i < mp->natoms; i++) {
4655 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
4656 if (ap->frames != NULL) {
4657 memmove(p, ap->frames, sizeof(Vector) * ap->nframes);
4658 p += sizeof(Vector) * ap->nframes;
4664 /* Bonds, angles, dihedrals, impropers */
4665 if (mp->nbonds > 0) {
4666 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nbonds;
4667 ptr = (char *)realloc(ptr, len_all + len);
4671 memmove(p, "BOND\0\0\0\0", 8);
4672 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nbonds;
4673 p += 8 + sizeof(Int);
4674 memmove(p, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
4677 if (mp->nangles > 0) {
4678 len = 8 + sizeof(Int) + sizeof(Int) * 3 * mp->nangles;
4679 ptr = (char *)realloc(ptr, len_all + len);
4683 memmove(p, "ANGLE\0\0\0", 8);
4684 *((Int *)(p + 8)) = sizeof(Int) * 3 * mp->nangles;
4685 p += 8 + sizeof(Int);
4686 memmove(p, mp->angles, sizeof(Int) * 3 * mp->nangles);
4689 if (mp->ndihedrals > 0) {
4690 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->ndihedrals;
4691 ptr = (char *)realloc(ptr, len_all + len);
4695 memmove(p, "DIHED\0\0\0", 8);
4696 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->ndihedrals;
4697 p += 8 + sizeof(Int);
4698 memmove(p, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
4701 if (mp->nimpropers > 0) {
4702 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->nimpropers;
4703 ptr = (char *)realloc(ptr, len_all + len);
4707 memmove(p, "IMPROP\0\0", 8);
4708 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->nimpropers;
4709 p += 8 + sizeof(Int);
4710 memmove(p, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
4714 /* Array of residues */
4715 if (mp->nresidues > 0) {
4716 len = 8 + sizeof(Int) + 4 * mp->nresidues;
4717 ptr = (char *)realloc(ptr, len_all + len);
4721 memmove(p, "RESIDUE\0", 8);
4722 *((Int *)(p + 8)) = 4 * mp->nresidues;
4723 p += 8 + sizeof(Int);
4724 memmove(p, mp->residues, 4 * mp->nresidues);
4729 if (mp->cell != NULL) {
4730 len = 8 + sizeof(Int) + sizeof(XtalCell);
4731 ptr = (char *)realloc(ptr, len_all + len);
4735 memmove(p, "CELL\0\0\0\0", 8);
4736 *((Int *)(p + 8)) = sizeof(XtalCell);
4737 p += 8 + sizeof(Int);
4738 memmove(p, mp->cell, sizeof(XtalCell));
4742 /* Symmetry operations */
4743 if (mp->nsyms > 0) {
4744 len = 8 + sizeof(Int) + sizeof(Transform) * mp->nsyms;
4745 ptr = (char *)realloc(ptr, len_all + len);
4749 memmove(p, "SYMOP\0\0\0", 8);
4750 *((Int *)(p + 8)) = sizeof(Transform) * mp->nsyms;
4751 p += 8 + sizeof(Int);
4752 memmove(p, mp->syms, sizeof(Transform) * mp->nsyms);
4756 /* Expanded atoms */
4757 if (mp->nexatoms > 0) {
4758 len = 8 + sizeof(Int) + sizeof(ExAtom) * mp->nexatoms;
4759 ptr = (char *)realloc(ptr, len_all + len);
4763 memmove(p, "EXATOM\0\0", 8);
4764 *((Int *)(p + 8)) = sizeof(ExAtom) * mp->nexatoms;
4765 p += 8 + sizeof(Int);
4766 memmove(p, mp->exatoms, sizeof(ExAtom) * mp->nexatoms);
4767 for (i = 0; i < mp->nexatoms; i++) {
4768 /* Clear label id */
4769 ((ExAtom *)p)[i].labelid = 0;
4774 /* Expanded bonds */
4775 if (mp->nexbonds > 0) {
4776 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nexbonds;
4777 ptr = (char *)realloc(ptr, len_all + len);
4781 memmove(p, "EXBOND\0\0", 8);
4782 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nexbonds;
4783 p += 8 + sizeof(Int);
4784 memmove(p, mp->exbonds, sizeof(Int) * 2 * mp->nexbonds);
4789 if (mp->par != NULL) {
4791 for (type = kFirstParType; type <= kLastParType; type++) {
4792 const char *parname;
4793 Int parsize, parcount;
4797 parname = "BONDPAR\0";
4798 parsize = sizeof(BondPar);
4799 parcount = mp->par->nbondPars;
4800 parptr = mp->par->bondPars;
4803 parname = "ANGPAR\0\0";
4804 parsize = sizeof(AnglePar);
4805 parcount = mp->par->nanglePars;
4806 parptr = mp->par->anglePars;
4808 case kDihedralParType:
4809 parname = "DIHEPAR\0";
4810 parsize = sizeof(TorsionPar);
4811 parcount = mp->par->ndihedralPars;
4812 parptr = mp->par->dihedralPars;
4814 case kImproperParType:
4815 parname = "IMPRPAR\0";
4816 parsize = sizeof(TorsionPar);
4817 parcount = mp->par->nimproperPars;
4818 parptr = mp->par->improperPars;
4821 parname = "VDWPAR\0\0";
4822 parsize = sizeof(VdwPar);
4823 parcount = mp->par->nvdwPars;
4824 parptr = mp->par->vdwPars;
4826 case kVdwPairParType:
4827 parname = "VDWPPAR\0";
4828 parsize = sizeof(VdwPairPar);
4829 parcount = mp->par->nvdwpPars;
4830 parptr = mp->par->vdwpPars;
4832 case kVdwCutoffParType:
4833 parname = "VCUTPAR\0";
4834 parsize = sizeof(VdwCutoffPar);
4835 parcount = mp->par->nvdwCutoffPars;
4836 parptr = mp->par->vdwCutoffPars;
4842 len = 8 + sizeof(Int) + parsize * parcount;
4843 ptr = (char *)realloc(ptr, len_all + len);
4847 memmove(p, parname, 8);
4848 *((Int *)(p + 8)) = parsize * parcount;
4849 p += 8 + sizeof(Int);
4850 memmove(p, parptr, parsize * parcount);
4858 time_t tm = time(NULL);
4859 len = 8 + sizeof(Int) + sizeof(Int);
4860 ptr = (char *)realloc(ptr, len_all + len);
4864 memmove(p, "TIME\0\0\0\0", 8);
4865 *((Int *)(p + 8)) = sizeof(Int);
4866 p += 8 + sizeof(Int);
4867 *((Int *)p) = (Int)tm;
4873 if (outLength != NULL)
4874 *outLength = len_all;
4878 Panic("Low memory while serializing a molecule data");
4879 return NULL; /* Not reached */
4882 #pragma mark ====== Search for bonds, angles, dihedrals, impropers ======
4885 sMoleculeSearchIncludingAtoms(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
4889 IntGroup *gp = NULL;
4890 if (atomgroup == NULL)
4892 for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
4893 for (j = 0; j < nsize; j++) {
4894 if (IntGroupLookup(atomgroup, ip[j], NULL) != 0) {
4897 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
4898 Panic("Low memory while searching %s", msg);
4907 MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
4911 return sMoleculeSearchIncludingAtoms(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
4915 MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
4919 return sMoleculeSearchIncludingAtoms(mp->nangles, mp->angles, 3, atomgroup, "angles");
4923 MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
4927 return sMoleculeSearchIncludingAtoms(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
4931 MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
4935 return sMoleculeSearchIncludingAtoms(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
4939 sMoleculeSearchAcrossAtomGroup(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
4943 IntGroup *gp = NULL;
4944 if (atomgroup == NULL)
4946 for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
4948 for (j = 0; j < nsize; j++) {
4950 kk = (IntGroupLookup(atomgroup, ip[j], NULL) != 0);
4954 /* This bond etc. crosses the atom group border */
4957 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
4958 Panic("Low memory while searching %s", msg);
4967 MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
4971 return sMoleculeSearchAcrossAtomGroup(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
4975 MoleculeSearchAnglesAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
4979 return sMoleculeSearchAcrossAtomGroup(mp->nangles, mp->angles, 3, atomgroup, "angles");
4983 MoleculeSearchDihedralsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
4987 return sMoleculeSearchAcrossAtomGroup(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
4991 MoleculeSearchImpropersAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
4995 return sMoleculeSearchAcrossAtomGroup(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
4998 /* Guess the bonds from the coordinates */
5000 MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds)
5006 Double a1, a2, alim;
5008 ElementPar *p = gElementParameters;
5011 for (i = 0; i < mp->natoms; i++) {
5012 ap = ATOM_AT_INDEX(mp->atoms, i);
5013 n1 = ap->atomicNumber;
5014 if (n1 >= 0 && n1 < gCountElementParameters)
5016 else a1 = p[6].radius;
5018 /* if (mp->is_xtal_coord)
5019 TransformVec(&r1, mp->cell->tr, &r1); */
5020 for (j = 0; j < i; j++) {
5021 bp = ATOM_AT_INDEX(mp->atoms, j);
5022 n2 = bp->atomicNumber;
5023 if (n2 >= 0 && n2 < gCountElementParameters)
5025 else a2 = p[6].radius;
5027 /* if (mp->is_xtal_coord)
5028 TransformVec(&r2, mp->cell->tr, &r2); */
5030 alim = limit * (a1 + a2);
5031 if (VecLength2(dr) < alim * alim) {
5034 /* MoleculeAddBonds(mp, 1, newbonds); */
5035 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5040 newbond[0] = kInvalidIndex;
5042 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5045 if (outNbonds != NULL)
5046 *outNbonds = nbonds;
5047 if (outBonds != NULL)
5052 /* Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information */
5054 MoleculeRebuildTablesFromConnects(Molecule *mp)
5056 int i, j, k, retval;
5063 if (mp->nbonds == 0) {
5064 for (i = 0; i < mp->natoms; i++) {
5065 ap = ATOM_AT_INDEX(mp->atoms, i);
5066 for (j = 0; j < ap->nconnects; j++) {
5067 k = ap->connects[j];
5072 /* MoleculeAddBonds() should not be used, because it assumes connects[] and
5073 bonds are already in sync */
5074 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
5075 /* retval = MoleculeAddBonds(mp, 1, ibuf);
5083 if (mp->nangles == 0) {
5084 for (i = 0; i < mp->natoms; i++) {
5085 ap = ATOM_AT_INDEX(mp->atoms, i);
5086 for (j = 0; j < ap->nconnects; j++) {
5087 for (k = j + 1; k < ap->nconnects; k++) {
5088 ibuf[0] = ap->connects[j];
5090 ibuf[2] = ap->connects[k];
5092 retval = MoleculeAddAngles(mp, ibuf, NULL);
5100 /* Find dihedrals */
5101 if (mp->ndihedrals == 0) {
5102 for (i = 0; i < mp->natoms; i++) {
5103 ap = ATOM_AT_INDEX(mp->atoms, i);
5104 for (j = 0; j < ap->nconnects; j++) {
5107 jj = ap->connects[j];
5110 apjj = ATOM_AT_INDEX(mp->atoms, jj);
5111 for (k = 0; k < ap->nconnects; k++) {
5114 kk = ap->connects[k];
5115 for (m = 0; m < apjj->nconnects; m++) {
5116 mm = apjj->connects[m];
5117 if (mm == i || mm == kk)
5124 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
5133 /* Find impropers */
5134 if (mp->nimpropers == 0) {
5135 for (i = 0; i < mp->natoms; i++) {
5136 int i1, i2, i4, n1, n2, n4;
5137 ap = ATOM_AT_INDEX(mp->atoms, i);
5138 for (i1 = 0; i1 < ap->nconnects; i1++) {
5139 n1 = ap->connects[i1];
5140 for (i2 = i1 + 1; i2 < ap->nconnects; i2++) {
5141 n2 = ap->connects[i2];
5142 for (i4 = i2 + 1; i4 < ap->nconnects; i4++) {
5143 n4 = ap->connects[i4];
5149 retval = MoleculeAddImpropers(mp, ibuf, NULL);
5158 mp->needsMDRebuild = 1;
5159 __MoleculeUnlock(mp);
5163 __MoleculeUnlock(mp);
5167 #pragma mark ====== Atom names ======
5169 /* Look for the n1-th atom in resno-th residue (n1 is 0-based) */
5171 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
5175 if (mp == NULL || mp->natoms == 0)
5178 for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5179 if (ap->resSeq == resno) {
5186 return lasti; /* max */
5191 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
5195 n = strtol(s, &p, 0);
5207 if ((p = strchr(s, ':')) != NULL) {
5208 /* Residue is specified */
5210 if ((pp = strchr(s, '.')) != NULL && pp < p) {
5211 /* Residue number is also specified */
5214 *resSeq = strtol(pp + 1, &ppp, 0);
5216 return -2; /* Bad format */
5217 while (isspace(*ppp))
5220 return -2; /* Bad format */
5223 /* Check whether the "residue name" is an integer */
5224 n = strtol(s, &pp, 0);
5226 while (isspace(*pp))
5228 if (*pp == 0 || *pp == ':') {
5231 return -2; /* Bad format */
5239 if (n >= sizeof(resName))
5240 n = sizeof(resName) - 1;
5241 strncpy(resName, s, n);
5249 strncpy(atomName, p, 4);
5254 /* Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer */
5256 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
5263 n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
5264 if (atomName[0] == 0) {
5265 if (n >= mp->natoms)
5266 n = -1; /* Out of range */
5269 for (n = 0; n < mp->natoms; n++) {
5270 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
5271 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
5272 && (resSeq < 0 || ap->resSeq == resSeq)
5273 && strncmp(atomName, ap->aname, 4) == 0) {
5277 return -1; /* Not found */
5281 MoleculeAreAtomsConnected(Molecule *mp, int n1, int n2)
5285 if (mp == NULL || n1 < 0 || n1 >= mp->natoms || n2 < 0 || n2 >= mp->natoms)
5287 ap = ATOM_AT_INDEX(mp->atoms, n1);
5288 for (i = 0; i < ap->nconnects; i++)
5289 if (ap->connects[i] == n2)
5296 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
5300 if (mp == NULL || index < 0 || index >= mp->natoms) {
5304 ap = mp->atoms + index;
5305 if (ap->resSeq != 0) {
5306 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
5310 snprintf(buf, bufsize, "%.4s", ap->aname);
5313 #pragma mark ====== Selection ======
5316 sMoleculeNotifyChangeSelection(Molecule *mp)
5318 /* TODO: Finer control of notification types may be necessary */
5319 MoleculeCallback_notifyModification(mp, 0);
5323 MoleculeSetSelection(Molecule *mp, IntGroup *select)
5328 IntGroupRetain(select);
5329 if (mp->selection != NULL)
5330 IntGroupRelease(mp->selection);
5331 mp->selection = select;
5332 sMoleculeNotifyChangeSelection(mp);
5336 MoleculeGetSelection(Molecule *mp)
5340 else return mp->selection;
5344 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
5346 if (mp->selection == NULL)
5347 mp->selection = IntGroupNew();
5349 IntGroupClear(mp->selection);
5350 IntGroupAdd(mp->selection, n1, 1);
5351 sMoleculeNotifyChangeSelection(mp);
5355 MoleculeUnselectAtom(Molecule *mp, int n1)
5357 if (mp->selection != NULL)
5358 IntGroupRemove(mp->selection, n1, 1);
5359 sMoleculeNotifyChangeSelection(mp);
5363 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
5365 if (mp->selection == NULL)
5366 mp->selection = IntGroupNew();
5367 IntGroupReverse(mp->selection, n1, 1);
5368 sMoleculeNotifyChangeSelection(mp);
5372 MoleculeIsAtomSelected(Molecule *mp, int n1)
5374 if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
5380 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
5382 if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
5388 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
5391 IntGroup *remain, *ig1, *ig2;
5393 remain = IntGroupNewFromIntGroup(remove);
5397 status = IntGroupReverse(remain, 0, mp->natoms);
5399 ig1 = IntGroupNew();
5403 status = IntGroupDifference(selection, remove, ig1);
5406 ig2 = IntGroupNew();
5410 status = IntGroupDeconvolute(ig1, remain, ig2);
5413 IntGroupRelease(remain);
5415 IntGroupRelease(ig1);
5420 IntGroupRelease(ig2);
5425 #pragma mark ====== Atom Equivalence ======
5429 struct sEqList *next;
5430 struct sEqList *link;
5433 static struct sEqList *sListBase = NULL;
5434 static struct sEqList *sListFree = NULL;
5436 static struct sEqList *
5440 if (sListFree != NULL) {
5442 sListFree = lp->next;
5443 lp->i[0] = lp->i[1] = 0;
5447 lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
5448 lp->link = sListBase;
5454 sFreeEqList(struct sEqList *list)
5456 list->next = sListFree;
5461 sDeallocateEqLists(void)
5463 struct sEqList *lp, *lp_link;
5464 for (lp = sListBase; lp != NULL; lp = lp_link) {
5473 sExistInEqList(int i, int idx, struct sEqList *list)
5475 while (list != NULL) {
5476 if (list->i[idx] == i)
5483 static struct sEqList *
5484 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
5487 struct sEqList *list1, *list2;
5489 api = ATOM_AT_INDEX(mol->atoms, i);
5490 apj = ATOM_AT_INDEX(mol->atoms, j);
5491 if (api->atomicNumber != apj->atomicNumber)
5493 list1 = sAllocEqList();
5499 if (i == j || (db[i] != NULL && db[i] == db[j]))
5501 for (ni = 0; ni < api->nconnects; ni++) {
5502 ii = api->connects[ni];
5503 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
5505 if (sExistInEqList(ii, 0, list1))
5508 for (nj = 0; nj < apj->nconnects; nj++) {
5509 jj = apj->connects[nj];
5510 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
5512 if (sExistInEqList(jj, 1, list1))
5514 list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
5518 if (list2 == NULL) {
5520 return NULL; /* No equivalent to ii */
5522 list1 = list2; /* ii is OK, try next */
5528 sDBInclude(Int *ip, int i)
5533 for (j = ip[0] - 1; j >= 0; j--) {
5541 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
5543 Int **db; /* List of equivalents for each atom */
5545 Atom *api, *apj, *apk;
5546 int i, j, k, ii, jj, kk;
5547 if (mol == NULL || mol->natoms == 0)
5549 db = (Int **)calloc(sizeof(Int *), mol->natoms);
5551 /* Find the equivalent univalent atoms */
5552 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
5553 if (api->nconnects < 2)
5555 for (j = 0; j < api->nconnects; j++) {
5556 Int ibuf[ATOMS_MAX_CONNECTS], n;
5557 memset(ibuf, 0, sizeof(ibuf));
5559 jj = api->connects[j];
5560 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
5563 apj = ATOM_AT_INDEX(mol->atoms, jj);
5564 if (apj->nconnects != 1 || db[jj] != NULL)
5566 for (k = j + 1; k < api->nconnects; k++) {
5567 kk = api->connects[k];
5568 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
5570 apk = ATOM_AT_INDEX(mol->atoms, kk);
5571 if (apk->nconnects != 1 || db[kk] != NULL)
5573 if (apj->atomicNumber == apk->atomicNumber)
5577 ip = (Int *)calloc(sizeof(Int), n + 1);
5581 memmove(ip + 1, ibuf, sizeof(Int) * n);
5582 for (k = 0; k < n; k++)
5588 /* Try matching (i,j) pair */
5589 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
5590 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
5592 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
5593 struct sEqList *list;
5594 if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
5596 if (api->atomicNumber != apj->atomicNumber)
5597 continue; /* Different elements do not match */
5598 if (db[i] != NULL && db[i] == db[j])
5599 continue; /* Already equivalent */
5600 list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
5602 continue; /* (i,j) do not match */
5603 while (list != NULL) {
5606 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
5607 /* Merge db[ii] and db[jj] */
5608 k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
5609 ip = (Int *)calloc(sizeof(Int), k + 1);
5611 return NULL; /* Out of memory */
5612 if (db[ii] == NULL) {
5616 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
5619 if (db[jj] == NULL) {
5622 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
5631 for (k = 0; k < ip[0]; k++)
5635 printf("(%d,%d) matched: ", ii, jj);
5636 for (k = 0; k < ip[0]; k++) {
5637 printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
5647 /* Record the equivalent atoms with the lowest index for each atom */
5648 result = (Int *)calloc(sizeof(Int), mol->natoms);
5649 for (i = 0; i < mol->natoms; i++)
5651 for (i = 0; i < mol->natoms; i++) {
5652 if (result[i] >= 0 || (ip = db[i]) == NULL)
5655 for (j = 0; j < ip[0]; j++) {
5660 for (j = 0; j < ip[0]; j++) {
5661 result[ip[j + 1]] = k;
5662 db[ip[j + 1]] = NULL;
5666 sDeallocateEqLists();
5670 #pragma mark ====== Symmetry expansion ======
5673 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
5677 if (symop.sym >= mp->nsyms)
5679 if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
5680 TransformVec(vpout, mp->cell->rtr, vpin);
5681 TransformVec(vpout, mp->syms[symop.sym], vpout);
5682 vpout->x += symop.dx;
5683 vpout->y += symop.dy;
5684 vpout->z += symop.dz;
5685 TransformVec(vpout, mp->cell->tr, vpout);
5687 TransformVec(vpout, mp->syms[symop.sym], vpin);
5688 vpout->x += symop.dx;
5689 vpout->y += symop.dy;
5690 vpout->z += symop.dz;
5695 /* TODO: Decide whether the symmetry operations are expressed in internal coordinates or cartesian coordinates. */
5697 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group)
5699 int i, n, n0, n1, count, *table;
5701 IntGroupIterator iter;
5702 /* int debug = 0; */
5703 if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
5705 if (symop.sym >= mp->nsyms)
5707 /* fprintf(stderr, "symop = {%d %d %d %d}\n", symop.sym, symop.dx, symop.dy, symop.dz); */
5709 /* Create atoms, with avoiding duplicates */
5710 n0 = n1 = mp->natoms;
5711 table = (int *)malloc(sizeof(int) * n0);
5714 for (i = 0; i < n0; i++)
5716 IntGroupIteratorInit(group, &iter);
5718 for (i = 0; i < count; i++) {
5722 n = IntGroupIteratorNext(&iter);
5723 ap = ATOM_AT_INDEX(mp->atoms, n);
5724 if (SYMOP_ALIVE(ap->symop)) {
5725 /* Skip if the atom is expanded */
5728 /* Is this expansion already present? */
5729 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
5730 if (ap2->symbase == n && SYMOP_EQUAL(symop, ap2->symop))
5734 /* If yes, then skip it */
5737 /* Is the expanded position coincides with itself? */
5738 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
5739 VecSub(dr, ap->r, nr);
5740 if (VecLength2(dr) < 1e-6) {
5741 /* If yes, then this atom is included but no new atom is created */
5744 /* Create a new atom */
5746 AtomDuplicate(&newAtom, ap);
5747 MoleculeCreateAnAtom(mp, &newAtom, -1);
5748 AtomClean(&newAtom);
5749 ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
5753 ap2->symop.alive = (symop.dx != 0 || symop.dy != 0 || symop.dz != 0 || symop.sym != 0);
5754 table[n] = n1; /* The index of the new atom */
5758 IntGroupIteratorRelease(&iter);
5761 for (i = 0; i < n0; i++) {
5764 if (b[0] < 0 || b[0] == i)
5766 ap = ATOM_AT_INDEX(mp->atoms, i);
5767 for (n = 0; n < ap->nconnects; n++) {
5768 b[1] = table[ap->connects[n]];
5771 if (b[1] > n0 && b[0] > b[1])
5773 MoleculeAddBonds(mp, 1, b);
5776 mp->needsMDRebuild = 1;
5777 __MoleculeUnlock(mp);
5779 return n1 - n0; /* The number of added atoms */
5782 /* Recalculate the coordinates of symmetry expanded atoms.
5783 Returns the number of affected atoms.
5784 If group is non-NULL, only the expanded atoms whose base atoms are in the
5785 given group are considered.
5786 If groupout and vpout are non-NULL, the indices of the affected atoms
5787 and the original positions are returned (for undo operation).
5788 The pointers returned in *groupout and *vpout must be released and
5789 free()'ed by the caller */
5791 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
5795 if (mp == NULL || mp->natoms == 0 || mp->nsyms == 0)
5797 if (groupout != NULL && vpout != NULL) {
5798 *groupout = IntGroupNew();
5799 if (*groupout == NULL)
5801 *vpout = (Vector *)malloc(sizeof(Vector) * mp->natoms);
5802 if (*vpout == NULL) {
5803 IntGroupRelease(*groupout);
5806 } else groupout = NULL; /* To simplify test for validity of groupout/vpout */
5810 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5812 if (!SYMOP_ALIVE(ap->symop))
5814 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
5816 bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
5817 MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
5818 VecSub(dr, nr, ap->r);
5819 if (VecLength2(dr) < 1e-20)
5821 if (groupout != NULL) {
5822 (*vpout)[count] = ap->r;
5823 IntGroupAdd(*groupout, i, 1);
5828 mp->needsMDCopyCoordinates = 1;
5829 __MoleculeUnlock(mp);
5830 if (groupout != NULL) {
5834 IntGroupRelease(*groupout);
5837 *vpout = (Vector *)realloc(*vpout, sizeof(Vector) * count);
5843 #pragma mark ====== Show/hide atoms ======
5846 sMoleculeNotifyChangeAppearance(Molecule *mp)
5848 /* TODO: Finer control of notification types may be necessary */
5849 MoleculeCallback_notifyModification(mp, 0);
5854 sMoleculeUnselectHiddenAtoms(Molecule *mp)
5857 if (mp == NULL || mp->selection == NULL)
5859 for (i = 0; i < mp->natoms; i++) {
5860 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
5861 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
5862 IntGroupRemove(mp->selection, i, 1);
5864 sMoleculeNotifyChangeAppearance(mp);
5868 MoleculeShowAllAtoms(Molecule *mp)
5873 for (i = 0; i < mp->natoms; i++) {
5874 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
5875 ap->exflags &= ~kAtomHiddenFlag;
5877 sMoleculeNotifyChangeAppearance(mp);
5882 MoleculeShowReverse(Molecule *mp)
5887 for (i = 0; i < mp->natoms; i++) {
5888 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
5889 ap->exflags ^= kAtomHiddenFlag;
5891 sMoleculeUnselectHiddenAtoms(mp);
5892 sMoleculeNotifyChangeAppearance(mp);
5897 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
5900 if (mp == NULL || ig == NULL)
5902 for (i = 0; i < mp->natoms; i++) {
5903 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
5904 if (ap->exflags & kAtomHiddenFlag)
5905 continue; /* Already hidden */
5906 if (IntGroupLookupPoint(ig, i) >= 0)
5907 ap->exflags |= kAtomHiddenFlag;
5909 sMoleculeUnselectHiddenAtoms(mp);
5910 sMoleculeNotifyChangeAppearance(mp);
5914 #pragma mark ====== Reversible Editing ======
5918 sMoleculeNotifyModification(Molecule *mp)
5920 ** TODO: Finer control of notification types may be necessary **
5921 MoleculeCallback_notifyModification(mp, 0);
5925 /* Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup */
5927 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
5930 if (where == NULL) {
5931 /* Append the new objects at the end */
5932 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
5935 n1 = IntGroupGetCount(where); /* Position to get new object */
5936 n2 = nobjs; /* Position to get old object */
5937 n3 = n1 + n2; /* Position to place new/old object */
5938 for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
5939 int start = IntGroupGetStartPoint(where, i);
5940 int end = IntGroupGetEndPoint(where, i);
5942 /* old[end-(n3-n2)..n2-1] is moved to old[end..n3-1] */
5943 memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
5944 n2 = end - (n3 - n2);
5947 /* new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1] */
5948 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
5955 /* Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
5957 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
5959 int n1, n2, n3, start, end, i;
5960 if (objs == NULL || where == NULL)
5961 return 1; /* Bad argument */
5962 n1 = 0; /* Position to move remaining elements to */
5963 n2 = 0; /* Position to move remaining elements from */
5964 n3 = 0; /* Position to move removed elements to */
5965 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
5966 end = IntGroupGetEndPoint(where, i);
5968 /* Move (start - n2) elements from objs[n2] to objs[n1] */
5970 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
5974 /* Move (end - start) elements from objs[n2] to clip[n3] */
5976 memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
5977 n3 += (end - start);
5978 n2 += (end - start);
5980 /* Move (nobjs - n2) elements from objs[n2] to objs[n1] */
5982 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
5986 /* Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
5988 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
5990 int n1, start, end, i;
5991 if (objs == NULL || where == NULL)
5992 return 1; /* Bad argument */
5993 n1 = 0; /* Position to move removed elements to */
5994 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
5995 end = IntGroupGetEndPoint(where, i);
5996 /* Copy (end - start) elements from objs[start] to clip[n1] */
5998 memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
5999 n1 += (end - start);
6004 /* Create a new atom with no bonding information. ap must _not_ be inside the given molecule
6005 (Use AtomDuplicate() first) */
6007 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
6011 if (mp == NULL || ap == NULL || mp->noModifyTopology)
6014 if (pos < 0 || pos >= mp->natoms)
6016 ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
6018 goto error; /* Out of memory */
6019 ap1 = ATOM_AT_INDEX(mp->atoms, pos);
6020 if (pos < mp->natoms - 1) {
6021 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6023 if (AtomDuplicate(ap1, ap) == NULL) {
6024 /* Cannot duplicate: restore the original state */
6025 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6030 if (ap1->resSeq >= mp->nresidues)
6031 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
6032 if (ap1->resName[0] == 0)
6033 strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
6034 if (ap1->segName[0] == 0)
6035 strncpy(ap1->segName, "MAIN", 4);
6036 if (pos < mp->natoms - 1) {
6037 /* Renumber the connect table, bonds, angles, etc. */
6038 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
6040 for (j = 0; j < api->nconnects; j++) {
6041 if (api->connects[j] >= pos)
6045 for (i = 0; i < mp->nbonds * 2; i++) {
6046 if (mp->bonds[i] >= pos)
6049 for (i = 0; i < mp->nangles * 3; i++) {
6050 if (mp->angles[i] >= pos)
6053 for (i = 0; i < mp->ndihedrals * 4; i++) {
6054 if (mp->dihedrals[i] >= pos)
6057 for (i = 0; i < mp->nimpropers * 4; i++) {
6058 if (mp->impropers[i] >= pos)
6062 mp->nframes = -1; /* Should be recalculated later */
6063 MoleculeIncrementModifyCount(mp);
6064 mp->needsMDRebuild = 1;
6065 __MoleculeUnlock(mp);
6068 __MoleculeUnlock(mp);
6072 /* Merge two molecules. We use this procedure for all add-atom operations. */
6073 /* resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
6075 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, int resSeqOffset)
6078 int i, j, n1, n2, n3, n4;
6079 Int *new2old, *old2new;
6081 if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
6082 return 0; /* Do nothing */
6084 if (dst->noModifyTopology)
6085 return 1; /* Prohibited operation */
6087 if (where != NULL && IntGroupGetCount(where) != src->natoms)
6088 return 1; /* Bad parameter */
6090 __MoleculeLock(dst);
6093 if (resSeqOffset < 0)
6096 /* Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
6097 and ndst..ndst+nsrc-1 are for atoms in src. */
6098 new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
6099 if (new2old == NULL)
6101 old2new = new2old + ndst + nsrc;
6102 n1 = 0; /* dst index */
6103 n2 = 0; /* src index */
6104 n3 = 0; /* "merged" index */
6106 while (n1 < ndst || n2 < nsrc) {
6107 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
6110 /* n4 elements from dst[n1] will go to merged[n3] */
6111 for (j = 0; j < n4; j++) {
6112 old2new[n1 + j] = n3 + j;
6113 new2old[n3 + j] = n1 + j;
6117 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
6119 /* n4 elements from src[n2] will go to merged[n3] */
6120 for (j = 0; j < n4; j++) {
6121 old2new[ndst + n2 + j] = n3 + j;
6122 new2old[n3 + j] = ndst + n2 + j;
6129 /* Expand the destination array */
6130 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
6133 /* Move the atoms */
6134 if (where == NULL) {
6135 /* Duplicate atoms to the end of the destination array */
6136 for (i = 0; i < nsrc; i++) {
6137 if (AtomDuplicate(ATOM_AT_INDEX(dst->atoms, ndst + i), ATOM_AT_INDEX(src->atoms, i)) == NULL)
6140 // memmove(ATOM_AT_INDEX(dst->atoms, ndst), src->atoms, gSizeOfAtomRecord * nsrc);
6142 /* Duplicate to a temporary storage and then insert */
6143 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
6144 if (tempatoms == NULL)
6146 for (i = 0; i < nsrc; i++) {
6147 if (AtomDuplicate(ATOM_AT_INDEX(tempatoms, i), ATOM_AT_INDEX(src->atoms, i)) == NULL)
6150 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
6154 dst->natoms = ndst + nsrc;
6156 /* Renumber the atom indices in connect[] and symbase, and modify the residue numbers */
6157 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
6158 if (new2old[i] < ndst) {
6159 /* This atom is from dst */
6162 /* This atom is from src */
6163 n1 = ndst; /* Offset to the internal number */
6164 if (ap->resSeq != 0)
6165 ap->resSeq += resSeqOffset; /* Modify residue number */
6167 for (j = 0; j < ap->nconnects; j++)
6168 ap->connects[j] = old2new[ap->connects[j] + n1];
6169 if (SYMOP_ALIVE(ap->symop))
6170 ap->symbase = old2new[ap->symbase + n1];
6173 /* Move the bonds, angles, dihedrals, impropers */
6174 for (i = 0; i < 4; i++) {
6175 Int *nitems, *nitems_src;
6176 Int **items, **items_src;
6177 Int nsize; /* Number of Ints in one element */
6180 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
6182 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
6184 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
6186 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
6188 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
6189 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
6190 /* Keep the old number of entries in dst, because it is updated by AssignArray() */
6192 /* Also keep the old number of entries in src, in case src and dst point the same molecule */
6194 /* Expand the array */
6195 if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
6197 /* Copy the items */
6198 memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
6200 for (j = 0; j < n1 * nsize; j++)
6201 (*items)[j] = old2new[(*items)[j]];
6202 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
6203 (*items)[j] = old2new[(*items)[j] + ndst];
6206 /* Merge parameters */
6207 if (src->par != NULL) {
6208 UnionPar *up1, *up2;
6212 if (dst->par == NULL)
6213 dst->par = ParameterNew();
6214 for (type = kFirstParType; type <= kLastParType; type++) {
6215 n1 = ParameterGetCountForType(src->par, type);
6216 n2 = ParameterGetCountForType(dst->par, type);
6219 for (i = 0; i < n1; i++) {
6220 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
6221 for (j = 0; j < n2; j++) {
6222 up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
6223 if (ParameterCompare(up1, up2, type))
6227 /* This is an unknown parameter; should be copied */
6228 IntGroupAdd(ig, i, 1);
6230 n1 = IntGroupGetCount(ig);
6233 up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
6236 /* Copy parameters and renumber indices if necessary */
6237 for (i = 0; i < n1; i++) {
6238 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
6242 ParameterRenumberAtoms(type, up1 + i, nsrc, old2new + ndst);
6244 /* Merge parameters */
6246 IntGroupAdd(ig, n2, n1);
6247 if (ParameterInsert(dst->par, type, up1, ig) < n1)
6254 /* Copy the residues if necessary */
6255 /* src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
6256 However, 1+resSeqOffset should not overwrite the existing residue in dst;
6257 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1]. */
6258 n1 = dst->nresidues;
6259 if (1 + resSeqOffset < n1) {
6261 } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
6262 if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
6263 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
6265 memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
6268 MoleculeCleanUpResidueTable(dst);
6271 dst->nframes = -1; /* Should be recalculated later */
6273 MoleculeIncrementModifyCount(dst);
6274 dst->needsMDRebuild = 1;
6275 __MoleculeUnlock(dst);
6279 __MoleculeUnlock(dst);
6280 Panic("Low memory while adding atoms");
6281 return 1; /* Not reached */
6285 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag)
6287 int nsrc, ndst, nsrcnew;
6288 int i, j, n1, n2, n3, n4;
6289 Int *new2old, *old2new;
6290 IntGroup *move_g, *del_g;
6294 if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
6301 if (src->noModifyTopology && moveFlag)
6302 return 1; /* Prohibit editing */
6304 if ((ndst = IntGroupGetCount(where)) > src->natoms)
6305 return 1; /* Bad parameter */
6307 __MoleculeLock(src);
6310 nsrcnew = nsrc - ndst;
6311 if (resSeqOffset < 0)
6314 /* Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
6315 and nsrcnew..nsrc-1 are for atoms moved into dst. */
6316 new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
6317 if (new2old == NULL)
6319 old2new = new2old + nsrc;
6320 n1 = 0; /* src index */
6321 n2 = 0; /* dst index */
6322 n3 = 0; /* src index after "unmerge" */
6324 while (n1 < nsrc || n2 < ndst) {
6325 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
6328 /* n4 elements from src[n1] will go to unmerged[n3] */
6329 for (j = 0; j < n4; j++) {
6330 old2new[n1 + j] = n3 + j;
6331 new2old[n3 + j] = n1 + j;
6335 if ((n4 = IntGroupGetInterval(where, i)) < 0)
6337 /* n4 elements from src[n1] will go to dst[n2] */
6338 for (j = 0; j < n4; j++) {
6339 old2new[n1 + j] = nsrcnew + n2 + j;
6340 new2old[nsrcnew + n2 + j] = n1 + j;
6347 /* Make sure that no bond between the two fragments exists */
6348 /* for (i = 0; i < src->nbonds; i++) {
6349 n1 = old2new[src->bonds[i * 2]];
6350 n2 = old2new[src->bonds[i * 2 + 1]];
6351 if ((n1 < nsrcnew && n2 >= nsrcnew) || (n1 >= nsrcnew && n2 < nsrcnew)) {
6357 /* Make a new molecule */
6359 dst = MoleculeNew();
6362 /* Expand the destination array */
6363 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
6365 dst_ap = dst->atoms;
6368 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
6373 /* Move the atoms */
6375 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
6377 src->natoms = nsrcnew;
6379 /* The atom record must be deallocated correctly */
6380 for (i = 0; i < ndst; i++)
6381 AtomClean(ATOM_AT_INDEX(dst_ap, i));
6385 for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
6386 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
6389 if (sCopyElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
6392 /* The atom record must be deep-copied correctly */
6393 for (i = 0; i < ndst; i++) {
6394 if (AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, i)) == NULL)
6402 /* The dummy destination array is no longer needed */
6407 /* Renumber the atom indices in connect[] */
6409 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
6410 for (j = n1 = 0; j < ap->nconnects; j++) {
6411 n2 = old2new[ap->connects[j]];
6413 ap->connects[n1++] = n2;
6419 /* Renumber the atom indices in connect[] and the residue indices */
6421 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
6422 if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
6423 ap->resSeq -= resSeqOffset;
6424 else ap->resSeq = 0;
6425 for (j = n1 = 0; j < ap->nconnects; j++) {
6426 n2 = old2new[ap->connects[j]] - nsrcnew;
6428 ap->connects[n1++] = n2;
6434 /* Separate the bonds, angles, dihedrals, impropers */
6435 /* TODO: Improper torsions should also be copied! */
6436 move_g = IntGroupNew();
6437 del_g = IntGroupNew();
6438 if (move_g == NULL || del_g == NULL)
6440 for (i = 0; i < 4; i++) {
6441 Int *nitems, *nitems_dst;
6442 Int **items, **items_dst;
6443 Int nsize; /* Number of Ints in one element */
6444 unsigned char *counts;
6447 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
6449 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
6451 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
6453 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
6455 nitems = NULL; items = NULL; nsize = 0; break; /* Not reached */
6458 nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
6459 items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
6464 counts = (unsigned char *)calloc(1, *nitems);
6465 /* Find the entries that should be moved to dst */
6467 for (j = 0; j < *nitems * nsize; j++) {
6468 n1 = old2new[(*items)[j]];
6470 counts[j / nsize]++; /* Count the atom belonging to dst */
6471 /* if (n1 >= nsrcnew) {
6473 if (j % nsize == 0) {
6474 if (IntGroupAdd(sep, j / nsize, 1) != 0)
6479 (*items)[j] = n1; */
6481 for (j = n2 = n3 = 0; j < *nitems; j++) {
6482 if (counts[j] > 0) {
6483 /* Remove from src */
6485 if (IntGroupAdd(del_g, j, 1) != 0)
6487 if (counts[j] == nsize) {
6490 if (IntGroupAdd(move_g, j, 1) != 0)
6496 /* Expand the destination array */
6497 if (items_dst != NULL && n3 > 0) {
6498 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
6500 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
6503 /* Remove from src */
6505 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
6510 /* Renumber the entries */
6512 for (j = 0; j < *nitems * nsize; j++) {
6513 (*items)[j] = old2new[(*items)[j]];
6516 if (items_dst != NULL) {
6517 for (j = 0; j < *nitems_dst * nsize; j++) {
6518 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
6522 IntGroupClear(move_g);
6523 IntGroupClear(del_g);
6525 IntGroupRelease(move_g);
6526 IntGroupRelease(del_g);
6528 /* Copy the residues */
6530 /* src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset) */
6531 n1 = src->nresidues - resSeqOffset; /* This will be dst->nresidues (if >0) */
6532 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
6535 memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
6539 /* Copy the parameters */
6540 if (dst != NULL && src->par != NULL) {
6541 UnionPar *up, *upary;
6542 if (dst->par == NULL)
6543 dst->par = ParameterNew();
6544 for (i = 0; i < nsrc; i++) {
6545 old2new[i] -= nsrcnew; /* new indices for atoms in dst; otherwise negative numbers */
6547 move_g = IntGroupNew();
6548 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
6549 n2 = ParameterGetCountForType(src->par, n1);
6552 /* Find parameters to be copied to dst */
6553 for (i = 0; i < n2; i++) {
6554 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
6555 for (j = 0, ap = dst->atoms; j < dst->natoms; j++, ap = ATOM_NEXT(ap)) {
6556 if (ParameterDoesContainAtom(n1, up, new2old[j + nsrcnew], kParameterLookupNoWildcard) || ParameterDoesContainAtom(n1, up, ap->type, kParameterLookupNoWildcard)) {
6557 IntGroupAdd(move_g, i, 1);
6562 n2 = IntGroupGetCount(move_g);
6565 upary = (UnionPar *)calloc(sizeof(UnionPar), n2);
6568 /* Copy parameters and renumber indices if necessary */
6569 for (i = 0; i < n2; i++) {
6570 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, IntGroupGetNthPoint(move_g, i));
6572 ParameterRenumberAtoms(n1, upary + i, nsrc, old2new);
6574 IntGroupClear(move_g);
6575 IntGroupAdd(move_g, ParameterGetCountForType(dst->par, n1), n2);
6576 /* Insert new parameters */
6577 if (ParameterInsert(dst->par, n1, upary, move_g) < n2)
6579 IntGroupClear(move_g);
6582 for (i = 0; i < nsrc; i++) {
6583 old2new[i] += nsrcnew; /* Restore indices */
6585 IntGroupRelease(move_g);
6589 MoleculeCleanUpResidueTable(src);
6591 MoleculeCleanUpResidueTable(dst);
6594 src->nframes = -1; /* Should be recalculated later */
6596 dst->nframes = -1; /* Should be recalculated later */
6602 MoleculeIncrementModifyCount(src);
6603 src->needsMDRebuild = 1;
6604 __MoleculeUnlock(src);
6609 __MoleculeUnlock(src);
6610 /* Panic("Low memory while removing atoms"); */
6614 /* Separate molecule into two parts. The atoms specified by 'where' are moved
6615 from src to a new molecule, which is returned as *dstp. Dstp can be NULL,
6616 in which case the moved atoms are discarded. */
6618 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset)
6620 return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1);
6623 /* Extract atoms from a given molecule into two parts. The atoms specified by
6624 'where' are copied from src to a new molecule, which is returned as *dstp.
6625 If dummyFlag is non-zero, then the atoms that are not included in the group
6626 but are connected to any atoms in the group are converted to "dummy" atoms
6627 (i.e. with element "Du" and names beginning with an underscore) and included
6628 in the new molecule object. */
6630 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
6634 /* Extract the fragment */
6635 retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0);
6641 /* Search bonds crossing the molecule border */
6642 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
6644 IntGroupIterator iter;
6647 IntGroupIteratorInit(ig, &iter);
6648 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6649 /* The atoms at the border */
6652 n1 = src->bonds[i*2];
6653 n2 = src->bonds[i*2+1];
6654 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
6658 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
6659 continue; /* Actually this is an internal error */
6661 /* n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule */
6662 /* Create a new dummy atom with the same segment/residue info with n1
6663 and the same position as n2 */
6664 ap = ATOM_AT_INDEX(src->atoms, n1);
6665 memset(&a, 0, gSizeOfAtomRecord);
6666 a.segSeq = ap->segSeq;
6667 memmove(a.segName, ap->segName, 4);
6668 a.resSeq = ap->resSeq;
6669 memmove(a.resName, ap->resName, 4);
6670 ElementToString(0, a.element); /* "Du" */
6671 snprintf(a.aname, 4, "_%d", idx++);
6672 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
6673 /* Add the dummy atom to the new molecule; nn[1] is the index
6674 of the new dummy atom in the new molecule */
6675 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
6676 /* Connect nn1 and nn2 */
6677 nn[2] = kInvalidIndex;
6678 MoleculeAddBonds(*dstp, 1, nn);
6680 IntGroupIteratorRelease(&iter);
6681 IntGroupRelease(ig);
6689 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds)
6691 int i, j, n1, n2, n;
6695 if (mp == NULL || bonds == NULL || nbonds <= 0)
6697 if (mp->noModifyTopology)
6698 return -4; /* Prohibited operation */
6700 /* Check the bonds */
6701 bonds_tmp = (Int *)malloc(sizeof(Int) * nbonds * 2);
6702 if (bonds_tmp == NULL)
6703 return -4; /* Out of memory */
6705 for (i = 0; i < nbonds; i++) {
6707 n2 = bonds[i * 2 + 1];
6708 if (n1 < 0 || n1 >= mp->natoms || n2 < 0 || n2 >= mp->natoms)
6709 return -1; /* Bad bond specification */
6712 ap = ATOM_AT_INDEX(mp->atoms, n1);
6713 if (ap->nconnects >= ATOMS_MAX_CONNECTS - 1 || ATOM_AT_INDEX(mp->atoms, n2)->nconnects >= ATOMS_MAX_CONNECTS - 1)
6714 return -2; /* Too many bonds */
6715 /* Check duplicates */
6716 for (j = 0; j < ap->nconnects; j++) {
6717 if (ap->connects[j] == n2)
6720 if (j == ap->nconnects) {
6721 bonds_tmp[n * 2] = n1;
6722 bonds_tmp[n * 2 + 1] = n2;
6727 /* No bonds to add */
6734 /* Add connects[] */
6735 for (i = 0; i < n; i++) {
6736 n1 = bonds_tmp[i * 2];
6737 n2 = bonds_tmp[i * 2 + 1];
6738 ap = ATOM_AT_INDEX(mp->atoms, n1);
6739 ap->connects[ap->nconnects++] = n2;
6740 ap = ATOM_AT_INDEX(mp->atoms, n2);
6741 ap->connects[ap->nconnects++] = n1;
6744 /* Expand the array and insert */
6746 /* if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, mp->nbonds + nb - 1, NULL) == NULL
6747 || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nb, sizeof(Int) * 2, where) != 0) */
6748 if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, mp->nbonds + n - 1, NULL) == NULL)
6750 memmove(mp->bonds + n1 * 2, bonds_tmp, sizeof(Int) * 2 * n);
6752 /* Add angles, dihedrals, impropers */
6754 Int nangles, ndihedrals, nimpropers;
6755 Int *angles, *dihedrals, *impropers;
6761 angles = dihedrals = impropers = NULL;
6762 nangles = ndihedrals = nimpropers = 0;
6764 for (i = 0; i < n; i++) {
6765 n1 = bonds_tmp[i * 2];
6766 n2 = bonds_tmp[i * 2 + 1];
6767 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
6768 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
6769 /* Angles X-n1-n2 */
6770 for (j = 0; j < ap1->nconnects; j++) {
6771 n3 = ap1->connects[j];
6777 for (k = 0; k < nangles; k++) {
6778 ip = angles + k * 3;
6779 if (ip[1] == n1 && ((ip[0] == n3 && ip[2] == n2) || (ip[0] == n2 && ip[2] == n3)))
6783 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
6786 /* Dihedrals X-n1-n2-X */
6787 for (k = 0; k < ap2->nconnects; k++) {
6788 n4 = ap2->connects[k];
6789 if (n4 == n1 || n4 == n3)
6792 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
6795 /* Impropers X-n2-n1-X */
6798 for (k = 0; k < ap1->nconnects; k++) {
6799 n4 = ap1->connects[k];
6800 if (n4 == n2 || n4 <= n3)
6803 if (AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, temp) == NULL)
6807 /* Angles X-n2-n1 */
6808 for (j = 0; j < ap2->nconnects; j++) {
6809 n3 = ap2->connects[j];
6815 for (k = 0; k < nangles; k++) {
6816 ip = angles + k * 3;
6817 if (ip[1] == n2 && ((ip[0] == n3 && ip[2] == n1) || (ip[0] == n1 && ip[2] == n3)))
6821 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
6826 temp[0] = kInvalidIndex;
6827 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
6829 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
6831 if (AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, temp) == NULL)
6833 MoleculeAddAngles(mp, angles, NULL);
6834 MoleculeAddDihedrals(mp, dihedrals, NULL);
6835 MoleculeAddImpropers(mp, impropers, NULL);
6838 if (dihedrals != NULL)
6840 if (impropers != NULL)
6844 MoleculeIncrementModifyCount(mp);
6845 mp->needsMDRebuild = 1;
6846 __MoleculeUnlock(mp);
6852 __MoleculeUnlock(mp);
6853 Panic("Low memory while adding bonds");
6854 return -1; /* Not reached */
6858 MoleculeDeleteBonds(Molecule *mp, Int nbonds, const Int *bonds)
6863 if (mp == NULL || nbonds <= 0)
6865 if (mp->noModifyTopology)
6866 return -4; /* Prohibited operation */
6870 /* Update connects[] */
6871 for (i = 0; i < nbonds; i++) {
6873 n2 = bonds[i * 2 + 1];
6874 ap = ATOM_AT_INDEX(mp->atoms, n1);
6875 for (j = 0; j < ap->nconnects; j++) {
6876 if (ap->connects[j] == n2) {
6877 memmove(&ap->connects[j], &ap->connects[j + 1], sizeof(Int) * (ap->nconnects - j - 1));
6882 ap = ATOM_AT_INDEX(mp->atoms, n2);
6883 for (j = 0; j < ap->nconnects; j++) {
6884 if (ap->connects[j] == n1) {
6885 memmove(&ap->connects[j], &ap->connects[j + 1], sizeof(Int) * (ap->nconnects - j - 1));
6892 /* Remove bonds, angles, dihedrals, impropers */
6894 IntGroup *bg, *ag, *dg, *ig;
6901 if (bg == NULL || ag == NULL || dg == NULL || ig == NULL)
6903 for (i = 0; i < nbonds; i++) {
6905 n2 = bonds[i * 2 + 1];
6906 for (j = 0; j < mp->nbonds; j++) {
6907 ip = mp->bonds + j * 2;
6908 if ((ip[0] == n1 && ip[1] == n2)
6909 || (ip[1] == n1 && ip[0] == n2)) {
6910 if (IntGroupAdd(bg, j, 1) != 0)
6914 for (j = 0; j < mp->nangles; j++) {
6915 ip = mp->angles + j * 3;
6916 if ((ip[0] == n1 && ip[1] == n2)
6917 || (ip[1] == n1 && ip[0] == n2)
6918 || (ip[1] == n1 && ip[2] == n2)
6919 || (ip[2] == n1 && ip[1] == n2)) {
6920 if (IntGroupAdd(ag, j, 1) != 0)
6924 for (j = 0; j < mp->ndihedrals; j++) {
6925 ip = mp->dihedrals + j * 4;
6926 if ((ip[1] == n1 && ip[2] == n2)
6927 || (ip[2] == n1 && ip[1] == n2)) {
6928 if (IntGroupAdd(dg, j, 1) != 0)
6932 for (j = 0; j < mp->nimpropers; j++) {
6933 ip = mp->impropers + j * 4;
6934 if ((ip[0] == n1 && ip[2] == n2)
6935 || (ip[1] == n1 && ip[2] == n2)
6936 || (ip[3] == n1 && ip[2] == n2)
6937 || (ip[0] == n2 && ip[2] == n1)
6938 || (ip[1] == n2 && ip[2] == n1)
6939 || (ip[3] == n2 && ip[2] == n1)) {
6940 if (IntGroupAdd(ig, j, 1) != 0)
6945 if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, NULL, sizeof(Int) * 2, bg) != 0)
6947 mp->nbonds -= IntGroupGetCount(bg);
6949 if (IntGroupGetCount(ag) > 0)
6950 MoleculeDeleteAngles(mp, NULL, ag);
6951 if (IntGroupGetCount(dg) > 0)
6952 MoleculeDeleteDihedrals(mp, NULL, dg);
6953 if (IntGroupGetCount(ig) > 0)
6954 MoleculeDeleteImpropers(mp, NULL, ig);
6955 IntGroupRelease(bg);
6956 IntGroupRelease(ag);
6957 IntGroupRelease(dg);
6958 IntGroupRelease(ig);
6961 MoleculeIncrementModifyCount(mp);
6962 mp->needsMDRebuild = 1;
6963 __MoleculeUnlock(mp);
6968 __MoleculeUnlock(mp);
6969 Panic("Low memory while removing bonds");
6970 return -1; /* Not reached */
6974 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
6977 if (mp == NULL || angles == NULL)
6979 if (mp->noModifyTopology)
6980 return -4; /* Prohibited operation */
6984 nc = IntGroupGetCount(where);
6986 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
6992 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
6993 || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
6994 __MoleculeUnlock(mp);
6995 Panic("Low memory while adding angles");
6998 mp->needsMDRebuild = 1;
6999 __MoleculeUnlock(mp);
7004 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
7007 if (mp == NULL || where == NULL)
7009 if (mp->noModifyTopology)
7010 return -4; /* Prohibited operation */
7012 if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
7013 __MoleculeUnlock(mp);
7014 Panic("Low memory while adding angles");
7016 mp->nangles -= (nc = IntGroupGetCount(where));
7017 mp->needsMDRebuild = 1;
7018 __MoleculeUnlock(mp);
7023 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
7026 if (mp == NULL || dihedrals == NULL)
7028 if (mp->noModifyTopology)
7029 return -4; /* Prohibited operation */
7031 nc = IntGroupGetCount(where);
7033 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
7039 n1 = mp->ndihedrals;
7041 if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
7042 || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
7043 __MoleculeUnlock(mp);
7044 Panic("Low memory while adding dihedrals");
7046 mp->needsMDRebuild = 1;
7047 __MoleculeUnlock(mp);
7052 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
7055 if (mp == NULL || where == NULL)
7057 if (mp->noModifyTopology)
7058 return -4; /* Prohibited operation */
7060 if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
7061 __MoleculeUnlock(mp);
7062 Panic("Low memory while adding dihedrals");
7064 mp->ndihedrals -= (nc = IntGroupGetCount(where));
7065 mp->needsMDRebuild = 1;
7066 __MoleculeUnlock(mp);
7071 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
7074 if (mp == NULL || impropers == NULL)
7076 if (mp->noModifyTopology)
7077 return -4; /* Prohibited operation */
7079 nc = IntGroupGetCount(where);
7081 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
7087 n1 = mp->nimpropers;
7089 if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
7090 || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
7091 __MoleculeUnlock(mp);
7092 Panic("Low memory while adding impropers");
7094 mp->needsMDRebuild = 1;
7095 __MoleculeUnlock(mp);
7100 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
7103 if (mp == NULL || where == NULL)
7105 if (mp->noModifyTopology)
7106 return -4; /* Prohibited operation */
7108 if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
7109 __MoleculeUnlock(mp);
7110 Panic("Low memory while adding impropers");
7112 mp->nimpropers -= (nc = IntGroupGetCount(where));
7113 __MoleculeUnlock(mp);
7118 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
7121 if (mp == NULL || mp->bonds == NULL)
7123 for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
7124 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
7131 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
7134 if (mp == NULL || mp->angles == NULL)
7136 for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
7137 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
7138 (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
7145 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
7148 if (mp == NULL || mp->dihedrals == NULL)
7150 for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
7151 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
7152 (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
7159 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
7162 if (mp == NULL || mp->impropers == NULL)
7164 for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
7167 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
7168 (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
7169 (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
7175 /* Remove the bond at bondIndex and create two dummy atoms instead.
7176 The dummy atoms are placed at the end of atoms[], and the residue
7177 numbers are the same as the root atoms (i.e. the atoms to which
7178 the dummy atoms are connected). The indices are returned in
7179 dummyIndices[0,1]. */
7181 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
7183 Int roots[3], newBonds[5];
7188 if (mp == NULL || mp->noModifyTopology)
7190 if (bondIndex < 0 || bondIndex >= mp->nbonds)
7192 roots[0] = mp->bonds[bondIndex * 2];
7193 roots[1] = mp->bonds[bondIndex * 2 + 1];
7194 roots[2] = kInvalidIndex;
7195 rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
7196 rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
7197 VecSub(dr, rootp[0]->r, rootp[1]->r);
7198 for (i = 0; i < 2; i++) {
7201 memmove(nap, rootp[i], sizeof(na));
7202 nap->aname[0] = '*';
7203 strcpy(nap->element, "Du");
7205 nap->charge = nap->weight = 0.0;
7206 nap->atomicNumber = 0;
7208 w = (i == 0 ? 0.4 : -0.4);
7209 VecScaleInc(nap->r, dr, w);
7216 /* Expand atoms array and append the dummy atoms at the end */
7218 natoms = mp->natoms;
7219 if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
7221 memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
7222 dummyIndices[0] = natoms;
7223 dummyIndices[1] = natoms + 1;
7225 /* Remove the old bond and create new bonds */
7226 /* ig = IntGroupNewWithPoints(bondIndex, 1, -1);
7229 MoleculeDeleteBonds(mp, NULL, ig);
7230 IntGroupRelease(ig); */
7231 MoleculeDeleteBonds(mp, 1, roots);
7232 newBonds[0] = roots[0];
7233 newBonds[1] = dummyIndices[0];
7234 newBonds[2] = roots[1];
7235 newBonds[3] = dummyIndices[1];
7236 newBonds[4] = kInvalidIndex;
7238 i = (MoleculeAddBonds(mp, 2, newBonds) < 0 ? -1 : 0);
7239 mp->needsMDRebuild = 1;
7240 __MoleculeUnlock(mp);
7244 __MoleculeUnlock(mp);
7245 Panic("Low memory during creating dummy atoms");
7249 /* Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
7250 a bond between the two root atoms. The value bondIndex is used as a
7251 hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
7252 the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
7253 is ignored and the new bond is stored at the end of bonds[]. */
7255 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
7262 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
7265 if (mol == NULL || mol->noModifyTopology)
7272 __MoleculeLock(mol);
7273 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
7274 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
7275 mol->needsMDRebuild = 1;
7276 __MoleculeUnlock(mol);
7283 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
7286 if (mol == NULL || mol->noModifyTopology)
7288 n1 = mol->ndihedrals;
7289 np1 = mol->dihedrals;
7290 mol->ndihedrals = 0;
7291 mol->dihedrals = NULL;
7292 if (ndihedrals > 0) {
7293 __MoleculeLock(mol);
7294 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
7295 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
7296 mol->needsMDRebuild = 1;
7297 __MoleculeUnlock(mol);
7299 *outDihedrals = np1;
7304 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
7307 if (mol == NULL || mol->noModifyTopology)
7309 n1 = mol->nimpropers;
7310 np1 = mol->impropers;
7311 mol->nimpropers = 0;
7312 mol->impropers = NULL;
7313 if (nimpropers > 0) {
7314 __MoleculeLock(mol);
7315 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
7316 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
7317 mol->needsMDRebuild = 1;
7318 __MoleculeUnlock(mol);
7320 *outImpropers = np1;
7326 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
7333 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
7334 return 0; /* molecule is empty */
7335 if (mol->noModifyTopology)
7339 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7340 Int *cp = ap->connects;
7341 for (j = 0; j < ap->nconnects; j++) {
7343 for (k = j + 1; k < ap->nconnects; k++) {
7345 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
7346 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
7355 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
7359 if (outAngles != NULL)
7360 *outAngles = angles;
7365 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
7367 Int n1, n2, n3, n4, *ip;
7372 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
7373 return 0; /* molecule is empty */
7376 for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
7377 Int i1, i3, i4, *ip;
7378 for (i3 = 0; i3 < ap2->nconnects; i3++) {
7379 n3 = ap2->connects[i3];
7382 ap3 = ATOM_AT_INDEX(mol->atoms, n3);
7383 for (i1 = 0; i1 < ap2->nconnects; i1++) {
7384 n1 = ap2->connects[i1];
7387 for (i4 = 0; i4 < ap3->nconnects; i4++) {
7388 n4 = ap3->connects[i4];
7389 if (n2 == n4 || n1 == n4)
7391 if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
7392 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
7402 if (ndihedrals > 0) {
7403 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
7407 if (outDihedrals != NULL)
7408 *outDihedrals = dihedrals;
7413 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
7415 Int n1, n2, n3, n4, t1, t2, t3, t4, *ip;
7416 Parameter *par = mol->par;
7421 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
7422 return 0; /* molecule is empty */
7423 if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
7424 return 0; /* No improper parameters are defined */
7428 for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
7429 Int i1, i2, i4, found, *ip;
7431 for (i1 = 0; i1 < ap3->nconnects; i1++) {
7432 n1 = ap3->connects[i1];
7433 t1 = ATOM_AT_INDEX(ap, n1)->type;
7434 for (i2 = i1 + 1; i2 < ap3->nconnects; i2++) {
7435 n2 = ap3->connects[i2];
7436 t2 = ATOM_AT_INDEX(ap, n2)->type;
7437 for (i4 = i2 + 1; i4 < ap3->nconnects; i4++) {
7438 n4 = ap3->connects[i4];
7439 t4 = ATOM_AT_INDEX(ap, n4)->type;
7441 if (ParameterLookupImproperPar(par, n1, n2, n3, n4, 1) != NULL)
7443 else if (ParameterLookupImproperPar(par, t1, t2, t3, t4, 0) != NULL)
7445 else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, 0) != NULL)
7447 if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
7448 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
7458 if (nimpropers > 0) {
7459 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
7463 if (outImpropers != NULL)
7464 *outImpropers = impropers;
7468 #pragma mark ====== Residues ======
7471 MoleculeCleanUpResidueTable(Molecule *mp)
7475 if (mp == NULL || mp->natoms == 0)
7479 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7480 if (ap->resSeq >= maxres)
7481 maxres = ap->resSeq + 1;
7482 if (ap->resSeq < mp->nresidues) {
7483 if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
7484 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
7486 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
7489 if (maxres < mp->nresidues)
7490 mp->nresidues = maxres;
7491 __MoleculeUnlock(mp);
7494 /* Change the number of residues. If nresidues is greater than the current value,
7495 then the array mp->residues is expanded with null names. If nresidues is smaller
7496 than the current value, mp->nresidues is set to the smallest possible value
7497 that is no smaller than nresidues and larger than any of the resSeq values. */
7499 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
7504 if (mp->nresidues == nresidues)
7506 else if (mp->nresidues < nresidues) {
7509 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
7510 while (n < nresidues)
7511 mp->residues[n++][0] = 0;
7512 __MoleculeUnlock(mp);
7518 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7519 if (ap->resSeq >= n)
7528 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
7530 IntGroupIterator iter;
7531 int withArray, resSeq, maxSeq;
7535 /* If LSB of resSeqs is 1, then a constant value is used for all specified atoms */
7536 if (((int)resSeqs & 1) == 0) {
7541 resSeq = ((int)resSeqs - 1) / 2;
7544 IntGroupIteratorInit(group, &iter);
7546 /* Change resSeqs */
7550 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7551 ap = ATOM_AT_INDEX(mp->atoms, i);
7553 resSeq = resSeqs[j++];
7554 if (resSeq > maxSeq)
7556 ap->resSeq = resSeq;
7558 __MoleculeUnlock(mp);
7560 /* Expand array if necessary */
7561 if (maxSeq >= mp->nresidues)
7562 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
7564 /* Synchronize resName and residues[] */
7566 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7567 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
7569 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
7571 IntGroupIteratorRelease(&iter);
7572 __MoleculeUnlock(mp);
7574 MoleculeIncrementModifyCount(mp);
7580 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
7582 return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(resSeq * 2 + 1));
7585 /* Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
7586 specifies the mp->nresidues after modifying the residue numbers.
7587 If all atoms are modified, then the table of residue names is also shifted. Otherwise,
7588 the table of residue names is not touched. */
7590 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
7592 int i, maxSeq, nmodatoms;
7594 IntGroupIterator iter;
7595 IntGroupIteratorInit(group, &iter);
7598 nresidues = mp->nresidues;
7601 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7602 ap = ATOM_AT_INDEX(mp->atoms, i);
7603 ap->resSeq += offset;
7604 if (ap->resSeq < 0) {
7605 /* Bad argument; undo change and returns this index + 1 */
7607 ap->resSeq -= offset;
7608 while ((i = IntGroupIteratorLast(&iter)) >= 0) {
7609 ap = ATOM_AT_INDEX(mp->atoms, i);
7610 ap->resSeq -= offset;
7612 IntGroupIteratorRelease(&iter);
7613 return bad_index + 1;
7615 if (ap->resSeq > maxSeq)
7616 maxSeq = ap->resSeq;
7619 if (maxSeq >= nresidues)
7620 nresidues = maxSeq + 1;
7621 if (offset < 0 && nmodatoms == mp->natoms) {
7622 /* Shift the residue names downward */
7623 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
7625 __MoleculeUnlock(mp);
7626 MoleculeChangeNumberOfResidues(mp, nresidues);
7627 if (offset > 0 && nmodatoms == mp->natoms) {
7628 /* Shift the residue names upward */
7630 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
7631 __MoleculeUnlock(mp);
7633 IntGroupIteratorRelease(&iter);
7635 MoleculeIncrementModifyCount(mp);
7640 /* Change residue names for the specified residue numbers. Names is an array of
7641 chars containing argc*4 characters, and every 4 characters represent a
7642 residue name; characters '\x01'-'\x1f' are converted to '\0', which allow
7643 names to be handled as a C string. */
7645 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
7650 for (i = 0; i < argc; i++) {
7651 if (maxSeq < resSeqs[i])
7652 maxSeq = resSeqs[i];
7654 if (maxSeq >= mp->nresidues)
7655 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
7657 for (i = 0; i < argc; i++) {
7658 char *p = mp->residues[resSeqs[i]];
7660 strncpy(p, names + i * 4, 4);
7661 for (j = 0; j < 4; j++) {
7662 if (p[j] >= 0 && p[j] < 0x20)
7666 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7667 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
7669 __MoleculeUnlock(mp);
7671 MoleculeIncrementModifyCount(mp);
7676 /* Returns the maximum residue number actually used */
7678 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
7683 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7684 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
7686 if (ap->resSeq > maxSeq)
7687 maxSeq = ap->resSeq;
7692 /* Returns the minimum residue number actually used */
7694 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
7698 minSeq = ATOMS_MAX_NUMBER;
7699 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7700 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
7702 if (ap->resSeq < minSeq)
7703 minSeq = ap->resSeq;
7705 return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
7708 #pragma mark ====== Sort by Residues ======
7711 sAtomSortComparator(const void *a, const void *b)
7713 const Atom *ap, *bp;
7714 ap = *((const Atom **)a);
7715 bp = *((const Atom **)b);
7716 if (ap->resSeq == bp->resSeq) {
7717 /* Retain the original order (i.e. atom with larger pointer address is larger) */
7724 /* Compare the residue sequence. However, residue sequence 0 is always larger. */
7725 if (ap->resSeq == 0)
7727 else if (bp->resSeq == 0)
7729 else if (ap->resSeq < bp->resSeq)
7731 else if (ap->resSeq > bp->resSeq)
7738 sMoleculeReorder(Molecule *mp)
7740 int i, res, prevRes;
7744 if (mp == NULL || mp->natoms <= 1)
7747 /* Sort the atoms, bonds, etc. */
7748 apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
7749 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
7750 newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
7751 if (apArray == NULL || old2new == NULL || newAtoms == NULL)
7752 Panic("Low memory during reordering atoms");
7753 for (i = 0; i < mp->natoms; i++)
7754 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
7756 /* Sort the atoms. Note: apArray is an array of "Pointer to Atom" */
7757 qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
7759 /* Make a table of 'which atom becomes which' */
7760 for (i = 0; i < mp->natoms; i++) {
7761 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
7765 /* Renumber the bonds, etc. */
7766 for (i = 0; i < mp->nbonds * 2; i++) {
7767 mp->bonds[i] = old2new[mp->bonds[i]];
7769 for (i = 0; i < mp->nangles * 3; i++) {
7770 mp->angles[i] = old2new[mp->angles[i]];
7772 for (i = 0; i < mp->ndihedrals * 4; i++) {
7773 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
7775 for (i = 0; i < mp->nimpropers * 4; i++) {
7776 mp->impropers[i] = old2new[mp->impropers[i]];
7778 for (i = 0; i < mp->natoms; i++) {
7780 for (j = 0, ip = apArray[i]->connects; j < apArray[i]->nconnects; j++, ip++)
7784 /* Renumber the residues so that the residue numbers are contiguous */
7786 for (i = 0; i < mp->natoms; i++) {
7787 if (apArray[i]->resSeq == 0)
7789 if (apArray[i]->resSeq != prevRes) {
7791 prevRes = apArray[i]->resSeq;
7792 if (prevRes != res) {
7793 strncpy(mp->residues[res], mp->residues[prevRes], 4);
7796 apArray[i]->resSeq = res;
7798 mp->nresidues = res + 1;
7800 /* Sort the atoms and copy back to atoms[] */
7801 for (i = 0; i < mp->natoms; i++) {
7802 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
7804 memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
7806 /* Free the locally allocated storage */
7813 /* Renumber atoms */
7815 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
7817 Int *old2new, i, j, retval;
7821 if (mp->noModifyTopology)
7823 if (old2new_out != NULL)
7824 old2new = old2new_out;
7826 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
7827 saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
7828 if (old2new == NULL || saveAtoms == NULL)
7829 Panic("Low memory during reordering atoms");
7830 memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
7832 for (i = 0; i < mp->natoms; i++)
7834 for (i = 0; i < isize && i < mp->natoms; i++) {
7836 if (j < 0 || j >= mp->natoms) {
7837 retval = 1; /* Out of range */
7840 if (old2new[j] != -1) {
7841 retval = 2; /* Duplicate entry */
7846 if (i < mp->natoms) {
7847 for (j = 0; j < mp->natoms; j++) {
7848 if (old2new[j] != -1)
7853 if (i != mp->natoms) {
7854 retval = 3; /* Internal inconsistency */
7858 /* Renumber the bonds, etc. */
7859 for (i = 0; i < mp->nbonds * 2; i++) {
7860 mp->bonds[i] = old2new[mp->bonds[i]];
7862 for (i = 0; i < mp->nangles * 3; i++) {
7863 mp->angles[i] = old2new[mp->angles[i]];
7865 for (i = 0; i < mp->ndihedrals * 4; i++) {
7866 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
7868 for (i = 0; i < mp->nimpropers * 4; i++) {
7869 mp->impropers[i] = old2new[mp->impropers[i]];
7871 for (i = 0; i < mp->natoms; i++) {
7872 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
7874 for (j = 0, ip = ap->connects; j < ap->nconnects; j++, ip++)
7878 /* Renumber the atoms */
7879 for (i = 0; i < mp->natoms; i++)
7880 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
7883 MoleculeIncrementModifyCount(mp);
7884 mp->needsMDRebuild = 1;
7887 __MoleculeUnlock(mp);
7889 if (old2new_out == NULL)
7894 #pragma mark ====== Coordinate Transform ======
7897 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
7901 if (mp == NULL || tr == NULL)
7904 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7905 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
7907 TransformVec(&ap->r, tr, &ap->r);
7909 mp->needsMDCopyCoordinates = 1;
7910 __MoleculeUnlock(mp);
7911 sMoleculeNotifyChangeAppearance(mp);
7915 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
7919 if (mp == NULL || tr == NULL)
7922 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7923 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
7925 TransformVec(&ap->r, tr, &ap->r);
7927 mp->needsMDCopyCoordinates = 1;
7928 __MoleculeUnlock(mp);
7929 sMoleculeNotifyChangeAppearance(mp);
7933 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
7937 if (mp == NULL || vp == NULL)
7940 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7941 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
7945 mp->needsMDCopyCoordinates = 1;
7946 __MoleculeUnlock(mp);
7947 sMoleculeNotifyChangeAppearance(mp);
7951 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
7958 if (mp == NULL || axis == NULL)
7960 w = VecLength(*axis);
7964 /* Construct a rotation transform: p' = c + A * (p - c) */
7966 cv.x = cv.y = cv.z = 0.0;
7969 TransformForRotation(tr, axis, angle, &cv);
7971 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7972 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
7974 TransformVec(&ap->r, tr, &ap->r);
7976 mp->needsMDCopyCoordinates = 1;
7977 __MoleculeUnlock(mp);
7978 sMoleculeNotifyChangeAppearance(mp);
7982 MoleculeReaxis(Molecule *mp, const Vector *xaxis, const Vector *yaxis, const Vector *zaxis, IntGroup *group)
7987 if (mp == NULL || xaxis == NULL || yaxis == NULL || zaxis == NULL)
7990 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7991 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
7993 v.x = VecDot(ap->r, *xaxis);
7994 v.y = VecDot(ap->r, *yaxis);
7995 v.z = VecDot(ap->r, *zaxis);
7998 mp->needsMDCopyCoordinates = 1;
7999 __MoleculeUnlock(mp);
8000 sMoleculeNotifyChangeAppearance(mp);
8004 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
8009 if (mp == NULL || center == NULL)
8011 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
8012 return 2; /* Empty molecule */
8014 center->x = center->y = center->z = 0.0;
8015 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8016 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8018 VecScaleInc(*center, ap->r, ap->weight);
8022 return 3; /* Atomic weights are not defined? */
8024 VecScaleSelf(*center, w);
8029 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
8036 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
8037 return 2; /* Empty molecule */
8038 vmin.x = vmin.y = vmin.z = 1e50;
8039 vmax.x = vmax.y = vmax.z = -1e50;
8040 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8041 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8043 if (vmin.x > ap->r.x)
8045 if (vmin.y > ap->r.y)
8047 if (vmin.z > ap->r.z)
8049 if (vmax.x < ap->r.x)
8051 if (vmax.y < ap->r.y)
8053 if (vmax.z < ap->r.z)
8063 #pragma mark ====== Measurements ======
8066 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
8069 /* if (mp->is_xtal_coord) {
8070 TransformVec(&r1, mp->cell->tr, vp1);
8071 TransformVec(&r2, mp->cell->tr, vp2);
8077 return VecLength(r1);
8081 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
8085 /* if (mp->is_xtal_coord) {
8086 TransformVec(&r1, mp->cell->tr, vp1);
8087 TransformVec(&r2, mp->cell->tr, vp2);
8088 TransformVec(&r3, mp->cell->tr, vp3);
8096 w = VecLength(r1) * VecLength(r3);
8099 return acos(VecDot(r1, r3) / w) * kRad2Deg;
8103 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
8105 Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
8107 /* if (mp->is_xtal_coord) {
8108 TransformVec(&r1, mp->cell->tr, vp1);
8109 TransformVec(&r2, mp->cell->tr, vp2);
8110 TransformVec(&r3, mp->cell->tr, vp3);
8111 TransformVec(&r4, mp->cell->tr, vp4);
8118 VecSub(r21, r1, r2);
8119 VecSub(r32, r2, r3);
8120 VecSub(r43, r3, r4);
8121 VecCross(v1, r21, r32);
8122 VecCross(v2, r32, r43);
8123 VecCross(v3, r32, v1);
8127 if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
8133 VecScaleSelf(v1, w1);
8134 VecScaleSelf(v2, w2);
8135 VecScaleSelf(v3, w3);
8136 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
8140 #pragma mark ====== XtalCell Parameters ======
8143 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
8145 if (mp->cell != NULL) {
8146 TransformVec(dst, mp->cell->tr, src);
8151 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
8153 if (mp->cell != NULL) {
8154 TransformVec(dst, mp->cell->rtr, src);
8159 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
8161 static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
8163 Vector *vp1, *vp2, *vp3;
8168 for (n1 = 0; n1 < 3; n1++) {
8169 if (cp->flags[n1] != 0)
8173 /* All directions are non-periodic */
8174 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
8175 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
8179 vp1 = &(cp->axes[n1]);
8180 vp2 = &(cp->axes[n2]);
8181 vp3 = &(cp->axes[n3]);
8182 cp->tr[n1] = vp1->x;
8183 cp->tr[n1 + 3] = vp1->y;
8184 cp->tr[n1 + 6] = vp1->z;
8185 cp->tr[9] = cp->origin.x;
8186 cp->tr[10] = cp->origin.y;
8187 cp->tr[11] = cp->origin.z;
8188 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
8189 /* 1-dimensional or 2-dimensional system */
8190 /* Create "dummy" axes, so that transforms between internal and cartesian coordinates are
8191 possible with a single matrix */
8192 if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
8194 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
8195 VecCross(v1, *vp1, xvec);
8196 VecCross(v2, *vp1, yvec);
8197 if (VecLength2(v1) < VecLength2(v2))
8199 VecCross(v2, *vp1, v1);
8200 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
8201 return -1; /* Non-regular transform */
8202 } else if (cp->flags[n2] == 0) {
8204 VecCross(v1, v2, *vp1);
8205 if (NormalizeVec(&v1, &v1))
8206 return -1; /* Non-regular transform */
8209 VecCross(v2, *vp1, v1);
8210 if (NormalizeVec(&v2, &v2))
8211 return -1; /* Non-regular transform */
8214 cp->tr[n2 + 3] = v1.y;
8215 cp->tr[n2 + 6] = v1.z;
8217 cp->tr[n3 + 3] = v2.y;
8218 cp->tr[n3 + 6] = v2.z;
8220 VecCross(v1, *vp1, *vp2);
8221 if (fabs(VecDot(v1, *vp3)) < 1e-7)
8222 return -1; /* Non-regular transform */
8223 cp->tr[n2] = vp2->x;
8224 cp->tr[n2 + 3] = vp2->y;
8225 cp->tr[n2 + 6] = vp2->z;
8226 cp->tr[n3] = vp3->x;
8227 cp->tr[n3 + 3] = vp3->y;
8228 cp->tr[n3 + 6] = vp3->z;
8231 if (TransformInvert(cp->rtr, cp->tr))
8232 return -1; /* Non-regular transform */
8234 /* Calculate the reciprocal cell parameters */
8235 cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[3] * cp->rtr[3] + cp->rtr[6] * cp->rtr[6]);
8236 cp->rcell[1] = sqrt(cp->rtr[1] * cp->rtr[1] + cp->rtr[4] * cp->rtr[4] + cp->rtr[7] * cp->rtr[7]);
8237 cp->rcell[2] = sqrt(cp->rtr[2] * cp->rtr[2] + cp->rtr[5] * cp->rtr[5] + cp->rtr[8] * cp->rtr[8]);
8238 cp->rcell[3] = acos((cp->rtr[1] * cp->rtr[2] + cp->rtr[4] * cp->rtr[5] + cp->rtr[7] * cp->rtr[8]) / (cp->rcell[1] * cp->rcell[2])) * kRad2Deg;
8239 cp->rcell[4] = acos((cp->rtr[2] * cp->rtr[0] + cp->rtr[5] * cp->rtr[3] + cp->rtr[8] * cp->rtr[6]) / (cp->rcell[2] * cp->rcell[0])) * kRad2Deg;
8240 cp->rcell[5] = acos((cp->rtr[0] * cp->rtr[1] + cp->rtr[3] * cp->rtr[4] + cp->rtr[6] * cp->rtr[7]) / (cp->rcell[0] * cp->rcell[1])) * kRad2Deg;
8243 /* Calculate a, b, c, alpha, beta, gamma */
8244 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[3] * cp->tr[3] + cp->tr[6] * cp->tr[6]);
8245 cp->cell[1] = sqrt(cp->tr[1] * cp->tr[1] + cp->tr[4] * cp->tr[4] + cp->tr[7] * cp->tr[7]);
8246 cp->cell[2] = sqrt(cp->tr[2] * cp->tr[2] + cp->tr[5] * cp->tr[5] + cp->tr[8] * cp->tr[8]);
8247 cp->cell[3] = acos((cp->tr[1] * cp->tr[2] + cp->tr[4] * cp->tr[5] + cp->tr[7] * cp->tr[8]) / (cp->cell[1] * cp->cell[2])) * kRad2Deg;
8248 cp->cell[4] = acos((cp->tr[2] * cp->tr[0] + cp->tr[5] * cp->tr[3] + cp->tr[8] * cp->tr[6]) / (cp->cell[2] * cp->cell[0])) * kRad2Deg;
8249 cp->cell[5] = acos((cp->tr[0] * cp->tr[1] + cp->tr[3] * cp->tr[4] + cp->tr[6] * cp->tr[7]) / (cp->cell[0] * cp->cell[1])) * kRad2Deg;
8256 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
8265 memset(&cmat, 0, sizeof(Transform));
8267 if (mp->cell != NULL) {
8268 memmove(&cmat, &(mp->cell->tr), sizeof(Transform));
8271 cmat[0] = cmat[4] = cmat[8] = 1.0;
8274 /* mp->is_xtal_coord = 0; */
8278 cp = (XtalCell *)malloc(sizeof(XtalCell));
8280 Panic("Low memory during setting cell parameters");
8282 cmat[0] = cmat[4] = cmat[8] = 1.0;
8284 /* if (mp->is_xtal_coord)
8285 memmove(&cmat, &(cp->tr), sizeof(Transform)); */
8287 /* mp->is_xtal_coord = 1; */
8288 /* alpha, beta, gamma are in degree */
8292 cp->cell[3] = alpha;
8294 cp->cell[5] = gamma;
8295 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
8296 /* c unique (hexagonal etc.) */
8297 Double cosa, cosb, sinb, cosg;
8298 cosa = cos(alpha * kDeg2Rad);
8299 cosb = cos(beta * kDeg2Rad);
8300 sinb = sin(beta * kDeg2Rad);
8301 cosg = cos(gamma * kDeg2Rad);
8302 cp->axes[0].x = a * sinb;
8304 cp->axes[0].z = a * cosb;
8305 cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
8306 cp->axes[1].z = b * cosa;
8307 cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
8313 Double cosg, sing, cosa, cosb;
8314 cosa = cos(alpha * kDeg2Rad);
8315 cosb = cos(beta * kDeg2Rad);
8316 cosg = cos(gamma * kDeg2Rad);
8317 sing = sin(gamma * kDeg2Rad);
8318 cp->axes[0].x = a * sing;
8319 cp->axes[0].y = a * cosg;
8324 cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
8325 cp->axes[2].y = c * cosa;
8326 cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
8328 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
8329 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
8330 MoleculeCalculateCellFromAxes(cp, 0);
8331 TransformMul(cmat, cp->rtr, cmat);
8334 /* Update the coordinates (if requested) */
8335 if (convertCoordinates) {
8336 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8337 TransformVec(&(ap->r), cmat, &(ap->r));
8341 /* Update the anisotropic parameters */
8342 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8343 Aniso *anp = ap->aniso;
8345 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5]);
8348 __MoleculeUnlock(mp);
8349 sMoleculeNotifyChangeAppearance(mp);
8353 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x23, Double x31)
8357 const Double log2 = 0.693147180559945;
8358 const Double pi22 = 19.7392088021787; /* 2*pi**2 */
8364 if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
8366 anp = mp->atoms[n1].aniso;
8369 anp = (Aniso *)malloc(sizeof(Aniso));
8371 __MoleculeUnlock(mp);
8372 Panic("Low memory during setting anisotropic atom parameters");
8374 mp->atoms[n1].aniso = anp;
8377 case 1: d = 1; dx = 0.5; break;
8378 case 2: d = log2; dx = log2; break;
8379 case 3: d = log2; dx = log2 * 0.5; break;
8380 case 4: u = 1; d = 0.25; dx = 0.25; break;
8381 case 5: u = 1; d = 0.25; dx = 0.125; break;
8382 case 8: u = 1; d = pi22; dx = pi22; break;
8383 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
8384 case 10: d = pi22; dx = pi22; break;
8385 default: d = dx = 1; break;
8387 anp->bij[0] = x11 * d;
8388 anp->bij[1] = x22 * d;
8389 anp->bij[2] = x33 * d;
8390 anp->bij[3] = x12 * dx;
8391 anp->bij[4] = x23 * dx;
8392 anp->bij[5] = x31 * dx;
8394 if (cp != NULL && u == 1) {
8395 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
8396 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
8397 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
8398 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
8399 anp->bij[4] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[3] * kDeg2Rad); */
8400 anp->bij[5] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[4] * kDeg2Rad); */
8403 /* Calculate the principal axes (in Cartesian coordinates) */
8404 /* The principal axes are the eigenvectors of matrix At(B^-1)A, where
8405 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
8406 in which x and z are the crystal-space and cartesian coordinates. */
8407 m1[0] = anp->bij[0] / pi22;
8408 m1[4] = anp->bij[1] / pi22;
8409 m1[8] = anp->bij[2] / pi22;
8410 m1[1] = m1[3] = anp->bij[3] / pi22;
8411 m1[5] = m1[7] = anp->bij[4] / pi22;
8412 m1[2] = m1[6] = anp->bij[5] / pi22;
8413 MatrixInvert(m1, m1);
8415 memmove(m2, cp->rtr, sizeof(Mat33));
8416 MatrixMul(m1, m1, m2);
8417 MatrixTranspose(m2, m2);
8418 MatrixMul(m1, m2, m1);
8420 MatrixSymDiagonalize(m1, val, axis);
8421 for (u = 0; u < 3; u++) {
8423 fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
8426 val[u] = 1 / sqrt(val[u]);
8428 anp->pmat[u] = axis[u].x * val[u];
8429 anp->pmat[u+3] = axis[u].y * val[u];
8430 anp->pmat[u+6] = axis[u].z * val[u];
8432 __MoleculeUnlock(mp);
8436 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic)
8438 static Vector zeroVec = {0, 0, 0};
8439 /* static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}; */
8445 if (mp->cell != NULL)
8448 /* mp->is_xtal_coord = 0; */
8451 b.axes[0] = (ax != NULL ? *ax : zeroVec);
8452 b.axes[1] = (ay != NULL ? *ay : zeroVec);
8453 b.axes[2] = (az != NULL ? *az : zeroVec);
8455 memmove(b.flags, periodic, 3);
8456 if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
8459 if (mp->cell != NULL)
8461 mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
8462 if (mp->cell != NULL) {
8463 memmove(mp->cell, &b, sizeof(XtalCell));
8465 } else n = -2; /* Out of memory */
8466 __MoleculeUnlock(mp);
8470 #pragma mark ====== Fragment manipulation ======
8473 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
8477 if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
8479 IntGroupAdd(result, idx, 1);
8480 ap = ATOM_AT_INDEX(mp->atoms, idx);
8481 for (i = 0; i < ap->nconnects; i++) {
8482 int idx2 = ap->connects[i];
8483 if (IntGroupLookup(result, idx2, NULL))
8485 sMoleculeFragmentSub(mp, idx2, result, exatoms);
8489 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
8490 not containing the atoms in exatoms */
8492 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
8495 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
8497 result = IntGroupNew();
8498 sMoleculeFragmentSub(mp, n1, result, exatoms);
8502 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
8503 not containing the atoms n2, n3, ... (terminated by -1) */
8505 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
8508 IntGroup *exatoms, *result;
8509 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
8511 exatoms = IntGroupNew();
8512 for (i = 0; i < argc; i++)
8513 IntGroupAdd(exatoms, argv[i], 1);
8514 result = IntGroupNew();
8515 sMoleculeFragmentSub(mp, n1, result, exatoms);
8516 IntGroupRelease(exatoms);
8520 /* The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
8521 not containing the atoms in exatoms */
8523 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
8525 IntGroupIterator iter;
8528 if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
8530 IntGroupIteratorInit(inatoms, &iter);
8531 result = IntGroupNew();
8532 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8533 sMoleculeFragmentSub(mp, i, result, exatoms);
8535 IntGroupIteratorRelease(&iter);
8539 /* Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
8540 group is bound to the rest of the molecule via only one bond.
8541 If the result is true, then the atoms belonging to the (only) bond are returned
8542 in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
8543 and n2 can be NULL, if those informations are not needed. */
8545 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
8547 int i, i1, i2, j, k, bond_count, nval1, nval2;
8549 if (mp == NULL || mp->natoms == 0 || group == NULL)
8550 return 0; /* Invalid arguments */
8552 for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
8553 i2 = IntGroupGetEndPoint(group, i);
8554 for (j = i1; j < i2; j++) {
8555 if (j < 0 || j >= mp->natoms)
8556 return 0; /* Invalid atom group */
8557 ap = ATOM_AT_INDEX(mp->atoms, j);
8558 for (k = 0; k < ap->nconnects; k++) {
8559 if (IntGroupLookup(group, ap->connects[k], NULL) == 0) {
8562 nval2 = ap->connects[k];
8564 return 0; /* Too many bonds */
8569 if (bond_count == 1) {
8580 /* Returns non-zero if the given group is 'rotatable' in the molecule. The group
8581 is said to be 'rotatable' when either of the following conditions are met; (1)
8582 the group is detachable, or (2) the group consists of two bonded atoms that define
8583 a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
8584 (either a new IntGroup or 'group' with incremented reference count; thus the caller
8585 is responsible for releasing the returned value). */
8587 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
8590 if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
8591 if (rotGroup != NULL) {
8592 IntGroupRetain(group);
8597 if (group != NULL && IntGroupGetCount(group) == 2) {
8598 i1 = IntGroupGetNthPoint(group, 0);
8599 i2 = IntGroupGetNthPoint(group, 1);
8600 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
8601 IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
8604 i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
8606 IntGroupRelease(frag);
8607 if (rotGroup != NULL)
8611 if (rotGroup != NULL)
8613 else if (frag != NULL)
8614 IntGroupRelease(frag);
8621 #pragma mark ====== Multiple frame ======
8624 MoleculeGetNumberOfFrames(Molecule *mp)
8628 if (mp->nframes <= 0) {
8632 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8633 if (ap->nframes > n)
8644 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
8646 int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
8649 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
8652 n_old = MoleculeGetNumberOfFrames(mp);
8653 n_new = n_old + count;
8654 last_inserted = IntGroupGetNthPoint(group, count - 1);
8655 if (n_new <= last_inserted) {
8656 exframes = last_inserted - n_new + 1; /* number of extra frames that will be silently inserted */
8658 } else exframes = 0;
8660 tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4); /* "*4" for handling cells */
8666 /* Copy back the current coordinates */
8667 /* No change in the current coordinates, but the frame buffer is updated */
8668 MoleculeSelectFrame(mp, mp->cframe, 1);
8670 /* Expand ap->frames for all atoms */
8671 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8672 if (ap->frames == NULL)
8673 vp = (Vector *)calloc(sizeof(Vector), n_new);
8675 vp = (Vector *)realloc(ap->frames, sizeof(Vector) * n_new);
8677 __MoleculeUnlock(mp);
8680 for (j = ap->nframes; j < n_new; j++)
8684 if (mp->cell != NULL && (mp->frame_cells != NULL || inFrameCell != NULL)) {
8685 if (mp->frame_cells == NULL) {
8686 vp = (Vector *)calloc(sizeof(Vector), n_new * 4);
8687 vp[0] = mp->cell->axes[0];
8688 vp[1] = mp->cell->axes[1];
8689 vp[2] = mp->cell->axes[2];
8690 vp[3] = mp->cell->origin;
8692 vp = (Vector *)realloc(mp->frame_cells, sizeof(Vector) * 4 * n_new);
8694 __MoleculeUnlock(mp);
8697 mp->frame_cells = vp;
8700 /* group = [n0..n1-1, n2..n3-1, ...] */
8702 /* tempv[0..n0-1] <- ap[0..n0-1], s += n0,
8703 tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
8704 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
8705 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
8707 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
8708 At last, s will become n_old and t will become count. */
8709 for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8710 int s, t, ns, ne, mult;
8713 if (i == mp->natoms) {
8714 if (mp->cell == NULL || mp->frame_cells == NULL)
8716 vp = mp->frame_cells;
8723 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
8725 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
8728 ne = IntGroupGetEndPoint(group, j);
8730 if (i == mp->natoms) {
8731 if (inFrameCell != NULL) {
8732 tempv[ns * 4] = inFrameCell[t * 4];
8733 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
8734 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
8735 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
8737 tempv[ns * 4] = mp->cell->axes[0];
8738 tempv[ns * 4 + 1] = mp->cell->axes[1];
8739 tempv[ns * 4 + 2] = mp->cell->axes[2];
8740 tempv[ns * 4 + 3] = mp->cell->origin;
8743 if (inFrame != NULL)
8744 tempv[ns] = inFrame[natoms * t + i];
8753 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
8757 ap->nframes = n_new;
8758 memmove(vp, tempv, sizeof(Vector) * mult * n_new);
8761 mp->nframes = n_new;
8762 MoleculeSelectFrame(mp, last_inserted, 0);
8763 MoleculeIncrementModifyCount(mp);
8764 __MoleculeUnlock(mp);
8769 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
8771 int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
8774 IntGroup *group, *group2;
8776 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
8779 /* outFrame[] should have enough size for Vector * natoms * group.count */
8780 memset(outFrame, 0, sizeof(Vector) * natoms * count);
8781 if (mp->cell != NULL && mp->frame_cells != NULL)
8782 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
8784 n_old = MoleculeGetNumberOfFrames(mp);
8786 return -2; /* Cannot delete last frame */
8788 group = IntGroupNew();
8789 group2 = IntGroupNewWithPoints(0, n_old, -1);
8790 IntGroupIntersect(inGroup, group2, group);
8791 IntGroupRelease(group2);
8792 count = IntGroupGetCount(group);
8793 n_new = n_old - count;
8795 IntGroupRelease(group);
8796 return -2; /* Trying to delete too many frames */
8798 tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4); /* "*4" for handling cells */
8799 if (tempv == NULL) {
8800 IntGroupRelease(group);
8806 /* Copy back the current coordinates */
8807 /* No change in the current coordinates, but the frame buffer is updated */
8808 MoleculeSelectFrame(mp, mp->cframe, 1);
8810 /* Determine which frame should be selected after removal is completed */
8813 if (IntGroupLookup(group, mp->cframe, &i)) {
8814 /* cframe will be removed */
8815 n1 = IntGroupGetStartPoint(group, i) - 1;
8817 n1 = IntGroupGetEndPoint(group, i);
8818 } else n1 = mp->cframe;
8819 /* Change to that frame */
8820 MoleculeSelectFrame(mp, n1, 0);
8821 group2 = IntGroupNewFromIntGroup(group);
8822 IntGroupReverse(group2, 0, n_old);
8823 new_cframe = IntGroupLookupPoint(group2, n1);
8825 return -3; /* This cannot happen */
8826 IntGroupRelease(group2);
8829 /* group = [n0..n1-1, n2..n3-1, ...] */
8831 /* tempv[0..n0-1] -> ap[0..n0-1], s += n0,
8832 tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
8833 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
8834 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
8836 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
8837 At last, s will become n_new and t will become count. */
8839 for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8840 int s, t, j, ns, ne;
8842 /* if i == mp->natoms, mp->frame_cells is handled */
8843 if (i == mp->natoms) {
8844 if (mp->cell == NULL || mp->frame_cells == NULL)
8847 vp = mp->frame_cells;
8853 ap->frames = vp = (Vector *)calloc(sizeof(Vector), n_old);
8855 __MoleculeUnlock(mp);
8859 old_count = ap->nframes;
8862 /* Copy vp to tempv */
8863 memset(tempv, 0, sizeof(Vector) * mult * n_old);
8864 memmove(tempv, vp, sizeof(Vector) * mult * (old_count > n_old ? n_old : old_count));
8865 ne = ns = s = t = 0;
8866 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
8870 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (ns - ne));
8873 ne = IntGroupGetEndPoint(group, j);
8878 outFrame[natoms * t + i] = tempv[ns];
8879 else if (outFrameCell != NULL) {
8880 outFrameCell[i * 4] = tempv[ns * 4];
8881 outFrameCell[i * 4 + 1] = tempv[ns * 4 + 1];
8882 outFrameCell[i * 4 + 2] = tempv[ns * 4 + 2];
8883 outFrameCell[i * 4 + 3] = tempv[ns * 4 + 3];
8890 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (n_old - ne));
8898 if (i < mp->natoms) {
8903 free(mp->frame_cells);
8904 mp->frame_cells = NULL;
8908 ap->frames = (Vector *)realloc(ap->frames, sizeof(Vector) * s);
8910 mp->frame_cells = (Vector *)realloc(mp->frame_cells, sizeof(Vector) * 4 * s);
8914 mp->nframes = nframes;
8916 /* Select the "last" frame; do not "copy back" the coordinates to the frame table */
8917 /* i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
8918 MoleculeSelectFrame(mp, new_cframe, 0);
8920 IntGroupRelease(group);
8922 MoleculeIncrementModifyCount(mp);
8923 __MoleculeUnlock(mp);
8928 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
8932 if (mp == NULL || mp->natoms == 0)
8934 cframe = mp->cframe;
8937 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8938 if (copyback && cframe >= 0 && cframe < ap->nframes) {
8939 /* Write the current coordinate back to the frame array */
8940 ap->frames[cframe] = ap->r;
8942 if (frame >= 0 && frame < ap->nframes) {
8943 /* Read the coordinate from the frame array */
8944 ap->r = ap->frames[frame];
8949 if (mp->cell != NULL && mp->frame_cells != NULL) {
8950 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);
8952 mp->needsMDCopyCoordinates = 1;
8953 __MoleculeUnlock(mp);
8956 sMoleculeNotifyChangeAppearance(mp);
8961 #pragma mark ====== MO calculation ======
8963 /* Calculate an MO value for a single point. */
8964 /* Index is the MO number (1-based) */
8965 /* tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom. */
8967 sCalcMOPoint(const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
8971 Double val, tval, *cnp, *tmpp, *mobasep, *mop;
8973 /* Cache dr and |dr|^2 */
8974 for (i = 0; i < bset->natoms; i++) {
8975 Vector r = bset->pos[i];
8976 tmp[i * 4] = r.x = vp->x - r.x;
8977 tmp[i * 4 + 1] = r.y = vp->y - r.y;
8978 tmp[i * 4 + 2] = r.z = vp->z - r.z;
8979 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
8981 /* Iterate over all shells */
8983 mobasep = bset->mo + (index - 1) * bset->ncomps;
8984 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
8985 pp = bset->priminfos + sp->p_idx;
8986 cnp = bset->cns + sp->cn_idx;
8987 tmpp = tmp + sp->a_idx * 4;
8988 mop = mobasep + sp->m_idx;
8992 for (j = 0; j < sp->nprim; j++) {
8993 tval += *cnp++ * exp(-pp->A * tmpp[3]);
8996 val += mop[0] * tval;
9002 for (j = 0; j < sp->nprim; j++) {
9003 tval = exp(-pp->A * tmpp[3]);
9009 x *= mop[0] * tmpp[0];
9010 y *= mop[1] * tmpp[1];
9011 z *= mop[2] * tmpp[2];
9018 for (j = 0; j < sp->nprim; j++) {
9019 tval = exp(-pp->A * tmpp[3]);
9027 x *= mop[1] * tmpp[0];
9028 y *= mop[2] * tmpp[1];
9029 z *= mop[3] * tmpp[2];
9030 val += t + x + y + z;
9034 Double xx, yy, zz, xy, xz, yz;
9035 xx = yy = zz = xy = xz = yz = 0;
9036 for (j = 0; j < sp->nprim; j++) {
9037 tval = exp(-pp->A * tmpp[3]);
9038 xx += *cnp++ * tval;
9039 yy += *cnp++ * tval;
9040 zz += *cnp++ * tval;
9041 xy += *cnp++ * tval;
9042 xz += *cnp++ * tval;
9043 yz += *cnp++ * tval;
9046 xx *= mop[0] * tmpp[0] * tmpp[0];
9047 yy *= mop[1] * tmpp[1] * tmpp[1];
9048 zz *= mop[2] * tmpp[2] * tmpp[2];
9049 xy *= mop[3] * tmpp[0] * tmpp[1];
9050 xz *= mop[4] * tmpp[0] * tmpp[2];
9051 yz *= mop[5] * tmpp[1] * tmpp[2];
9052 val += xx + yy + zz + xy + xz + yz;
9056 Double d0, d1p, d1n, d2p, d2n;
9057 d0 = d1p = d1n = d2p = d2n = 0;
9058 for (j = 0; j < sp->nprim; j++) {
9059 tval = exp(-pp->A * tmpp[3]);
9060 d0 += *cnp++ * tval;
9061 d1p += *cnp++ * tval;
9062 d1n += *cnp++ * tval;
9063 d2p += *cnp++ * tval;
9064 d2n += *cnp++ * tval;
9067 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
9068 d1p *= mop[1] * tmpp[0] * tmpp[2];
9069 d1n *= mop[2] * tmpp[1] * tmpp[2];
9070 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
9071 d2n *= mop[4] * tmpp[0] * tmpp[1];
9072 val += d0 + d1p + d1n + d2p + d2n;
9080 /* Calculate one MO. The input vectors should be in bohr unit (angstrom * 1.889725989 = kAngstrom2Bohr). */
9081 /* mono is the MO number (1-based) */
9083 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)
9085 int ix, iy, iz, n, nn;
9088 if (mp == NULL || mp->bset == NULL)
9090 if (mp->bset->cns == NULL) {
9091 if (sSetupGaussianCoefficients(mp->bset) != 0)
9094 cp = (Cube *)calloc(sizeof(Cube), 1);
9098 cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
9099 if (cp->dp == NULL) {
9112 /* TODO: use multithread */
9113 tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms * 4);
9120 for (ix = 0; ix < nx; ix++) {
9122 for (iy = 0; iy < ny; iy++) {
9123 for (iz = 0; iz < nz; iz++) {
9124 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
9125 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
9126 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
9127 cp->dp[n++] = sCalcMOPoint(mp->bset, mono, &p, tmp);
9129 if (callback != NULL && n - nn > 100) {
9131 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
9135 return -2; /* User interrupt */
9142 AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
9143 return mp->bset->ncubes - 1;
9147 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
9150 Vector rmin, rmax, *vp;
9151 Double dr, dx, dy, dz;
9152 if (mp == NULL || mp->bset == NULL || mp->bset->natoms == 0)
9156 rmin.x = rmin.y = rmin.z = 1e10;
9157 rmax.x = rmax.y = rmax.z = -1e10;
9158 for (i = 0, vp = mp->bset->pos; i < mp->bset->natoms; i++, vp++) {
9159 dr = RadiusForAtomicNumber(ATOM_AT_INDEX(mp->atoms, i)->atomicNumber);
9162 dr = dr * kAngstrom2Bohr * 3.0 + 2.0;
9163 if (rmin.x > vp->x - dr)
9164 rmin.x = vp->x - dr;
9165 if (rmin.y > vp->y - dr)
9166 rmin.y = vp->y - dr;
9167 if (rmin.z > vp->z - dr)
9168 rmin.z = vp->z - dr;
9169 if (rmax.x < vp->x + dr)
9170 rmax.x = vp->x + dr;
9171 if (rmax.y < vp->y + dr)
9172 rmax.y = vp->y + dr;
9173 if (rmax.z < vp->z + dr)
9174 rmax.z = vp->z + dr;
9176 dx = rmax.x - rmin.x;
9177 dy = rmax.y - rmin.y;
9178 dz = rmax.z - rmin.z;
9179 dr = pow(dx * dy * dz / npoints, 1.0/3.0);
9180 *nx = floor(dx / dr + 0.5);
9181 *ny = floor(dy / dr + 0.5);
9182 *nz = floor(dz / dr + 0.5);
9190 xp->x = yp->y = zp->z = dr;
9191 xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
9196 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
9198 if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
9200 return mp->bset->cubes[index];
9204 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
9207 if (mp == NULL || mp->bset == NULL)
9209 for (i = 0; i < mp->bset->ncubes; i++) {
9210 if (mp->bset->cubes[i]->idn == mono)
9217 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
9220 if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
9222 CubeRelease(mp->bset->cubes[index]);
9224 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
9225 if (--(mp->bset->ncubes) == 0) {
9226 free(mp->bset->cubes);
9227 mp->bset->cubes = NULL;
9229 return mp->bset->ncubes;
9233 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
9238 if (mp == NULL || mp->bset == NULL)
9239 return -1; /* Molecule or the basis set information is empty */
9240 cp = MoleculeGetCubeAtIndex(mp, index);
9242 return -2; /* MO not yet calculated */
9243 fp = fopen(fname, "wb");
9245 return -3; /* Cannot create file */
9248 fprintf(fp, "%s MO=%d\n", comment, cp->idn);
9249 fprintf(fp, " MO coefficients\n");
9251 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms), cp->origin.x, cp->origin.y, cp->origin.z);
9252 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx, cp->dx.x, cp->dx.y, cp->dx.z);
9253 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny, cp->dy.x, cp->dy.y, cp->dy.z);
9254 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz, cp->dz.x, cp->dz.y, cp->dz.z);
9256 /* Atomic information */
9257 for (i = 0; i < mp->bset->natoms; i++) {
9258 Vector *vp = mp->bset->pos + i;
9259 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
9260 /* The second number should actually be the effective charge */
9261 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber, vp->x, vp->y, vp->z);
9263 fprintf(fp, "%5d%5d\n", 1, 1);
9266 for (i = n = 0; i < cp->nx; i++) {
9267 for (j = 0; j < cp->ny; j++) {
9268 for (k = 0; k < cp->nz; k++) {
9269 fprintf(fp, " %12.5e", cp->dp[n++]);
9270 if (k == cp->nz - 1 || k % 6 == 5)