4 * Created by Toshi Nagata on 06/03/11.
5 * Copyright 2006 Toshi Nagata. All rights reserved.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation version 2 of the License.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
25 #include "MD/MDCore.h"
26 #include "MD/MDPressure.h"
28 static Molecule *sMoleculeRoot = NULL;
29 static int sMoleculeUntitledCount = 0;
31 Int gSizeOfAtomRecord = sizeof(Atom);
33 /* These are the pasteboard data type. Since the internal representation of the
34 pasteboard data includes binary data that may be dependent on the software version,
35 the revision number is appended to these strings on startup (See MyApp::OnInit()) */
36 char *gMoleculePasteboardType = "Molecule";
37 char *gParameterPasteboardType = "Parameter";
39 #pragma mark ====== Utility function ======
42 strlen_limit(const char *s, int limit)
45 for (len = 0; *s != 0 && (limit < 0 || len < limit); s++, len++);
49 #pragma mark ====== Atom handling ======
52 s_AtomDuplicate(Atom *dst, const Atom *src, Int copy_frame)
55 dst = (Atom *)malloc(gSizeOfAtomRecord);
59 memmove(dst, src, gSizeOfAtomRecord);
60 if (src->aniso != NULL) {
61 dst->aniso = (Aniso *)malloc(sizeof(Aniso));
62 if (dst->aniso != NULL)
63 memmove(dst->aniso, src->aniso, sizeof(Aniso));
65 if (src->frames != NULL && copy_frame) {
66 dst->frames = (Vector *)malloc(sizeof(Vector) * src->nframes);
67 if (dst->frames != NULL) {
68 memmove(dst->frames, src->frames, sizeof(Vector) * src->nframes);
69 dst->nframes = src->nframes;
74 if (src->connect.count > ATOM_CONNECT_LIMIT) {
75 dst->connect.u.ptr = NULL;
76 dst->connect.count = 0;
77 NewArray(&(dst->connect.u.ptr), &(dst->connect.count), sizeof(Int), src->connect.count);
78 memmove(dst->connect.u.ptr, src->connect.u.ptr, sizeof(Int) * src->connect.count);
80 if (src->anchor != NULL) {
81 dst->anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
82 if (dst->anchor != NULL)
83 memmove(dst->anchor, src->anchor, sizeof(PiAnchor));
84 if (dst->anchor->connect.count > ATOM_CONNECT_LIMIT) {
85 dst->anchor->connect.u.ptr = NULL;
86 dst->anchor->connect.count = 0;
87 NewArray(&(dst->anchor->connect.u.ptr), &(dst->anchor->connect.count), sizeof(Int), src->anchor->connect.count);
88 memmove(dst->anchor->connect.u.ptr, src->anchor->connect.u.ptr, sizeof(Int) * src->anchor->connect.count);
90 if (dst->anchor->ncoeffs > 0) {
91 NewArray(&(dst->anchor->coeffs), &(dst->anchor->ncoeffs), sizeof(Double), src->anchor->ncoeffs);
92 memmove(dst->anchor->coeffs, src->anchor->coeffs, sizeof(Double) * src->anchor->ncoeffs);
99 AtomDuplicate(Atom *dst, const Atom *src)
101 return s_AtomDuplicate(dst, src, 1);
105 AtomDuplicateNoFrame(Atom *dst, const Atom *src)
107 return s_AtomDuplicate(dst, src, 0);
113 if (ap->aniso != NULL) {
117 if (ap->frames != NULL) {
122 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
123 ap->connect.count = 0;
124 free(ap->connect.u.ptr);
125 ap->connect.u.ptr = NULL;
130 CubeRelease(Cube *cp)
140 BasisSetRelease(BasisSet *bset)
145 if (bset->shells != NULL)
147 if (bset->priminfos != NULL)
148 free(bset->priminfos);
149 if (bset->mo != NULL)
151 if (bset->cns != NULL)
153 if (bset->moenergies != NULL)
154 free(bset->moenergies);
155 if (bset->scfdensities != NULL)
156 free(bset->scfdensities);
157 if (bset->pos != NULL)
159 if (bset->nuccharges != NULL)
160 free(bset->nuccharges);
161 if (bset->cubes != NULL) {
162 for (i = 0; i < bset->ncubes; i++) {
163 CubeRelease(bset->cubes[i]);
171 AtomConnectData(AtomConnect *ac)
175 return ATOM_CONNECT_PTR(ac);
179 AtomConnectResize(AtomConnect *ac, Int nconnects)
184 if (nconnects <= ATOM_CONNECT_LIMIT) {
185 if (ac->count > ATOM_CONNECT_LIMIT) {
187 memmove(ac->u.data, p, sizeof(Int) * nconnects);
191 if (ac->count <= ATOM_CONNECT_LIMIT) {
194 NewArray(&p, &(ac->count), sizeof(Int), nconnects);
195 memmove(p, ac->u.data, sizeof(Int) * ac->count);
197 } else if (ac->count < nconnects) {
199 AssignArray(&(ac->u.ptr), &(ac->count), sizeof(Int), nconnects - 1, NULL);
202 ac->count = nconnects;
206 AtomConnectInsertEntry(AtomConnect *ac, Int idx, Int connect)
214 /* Insert after the last component that is smaller than connect
215 (i.e. keep them sorted) */
216 p = ATOM_CONNECT_PTR(ac);
217 for (idx = 0; idx < ac->count; idx++) {
218 if (p[idx] >= connect)
222 AtomConnectResize(ac, ac->count + 1);
223 n = ac->count - idx - 1; /* Number of entries to be moved towards the end */
224 p = ATOM_CONNECT_PTR(ac);
226 memmove(p + idx + 1, p + idx, sizeof(Int) * n);
232 AtomConnectDeleteEntry(AtomConnect *ac, Int idx)
237 if (idx < 0 || idx >= ac->count)
239 n = ac->count - idx - 1; /* Number of entries to be moved towards the top */
240 p = ATOM_CONNECT_PTR(ac);
242 memmove(p + idx, p + idx + 1, sizeof(Int) * n);
244 AtomConnectResize(ac, ac->count - 1);
248 AtomConnectHasEntry(AtomConnect *ac, Int ent)
253 p = ATOM_CONNECT_PTR(ac);
254 for (n = 0; n < ac->count; n++) {
261 #pragma mark ====== Accessor types ======
264 MolEnumerableNew(Molecule *mol, int kind)
266 MolEnumerable *mseq = (MolEnumerable *)calloc(sizeof(MolEnumerable), 1);
268 mseq->mol = MoleculeRetain(mol);
275 MolEnumerableRelease(MolEnumerable *mseq)
278 MoleculeRelease(mseq->mol);
284 AtomRefNew(Molecule *mol, int idx)
286 AtomRef *aref = (AtomRef *)calloc(sizeof(AtomRef), 1);
288 aref->mol = MoleculeRetain(mol);
295 AtomRefRelease(AtomRef *aref)
298 MoleculeRelease(aref->mol);
303 #pragma mark ====== Creation of molecules ======
309 Molecule *mp = (Molecule *)calloc(sizeof(Molecule), 1);
311 Panic("Cannot allocate new molecule record");
312 snprintf(name, sizeof name, "Untitled %d", sMoleculeUntitledCount++);
313 ObjectInit((Object *)mp, (Object **)&sMoleculeRoot, name);
314 mp->mview = MainView_new();
320 MoleculeNewWithName(const char *name)
322 Molecule *mp = MoleculeNew();
323 MoleculeSetName(mp, name);
328 MoleculeInitWithAtoms(Molecule *mp, const Atom *atoms, int natoms)
335 if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
336 Panic("Cannot allocate memory for atoms");
337 for (i = 0; i < natoms; i++)
338 AtomDuplicate(mp->atoms + i, atoms + i);
339 mp->nframes = -1; /* Should be recalculated later */
344 MoleculeInitWithMolecule(Molecule *mp2, Molecule *mp)
346 MoleculeFlushFrames(mp);
347 MoleculeInitWithAtoms(mp2, mp->atoms, mp->natoms);
348 if (mp->nbonds > 0) {
349 if (NewArray(&mp2->bonds, &mp2->nbonds, sizeof(Int)*2, mp->nbonds) == NULL)
351 memmove(mp2->bonds, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
353 if (mp->nangles > 0) {
354 if (NewArray(&mp2->angles, &mp2->nangles, sizeof(Int)*3, mp->nangles) == NULL)
356 memmove(mp2->angles, mp->angles, sizeof(Int) * 3 * mp->nangles);
358 if (mp->ndihedrals > 0) {
359 if (NewArray(&mp2->dihedrals, &mp2->ndihedrals, sizeof(Int)*4, mp->ndihedrals) == NULL)
361 memmove(mp2->dihedrals, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
363 if (mp->nimpropers > 0) {
364 if (NewArray(&mp2->impropers, &mp2->nimpropers, sizeof(Int)*4, mp->nimpropers) == NULL)
366 memmove(mp2->impropers, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
368 if (mp->nresidues > 0) {
369 if (NewArray(&mp2->residues, &mp2->nresidues, sizeof(mp->residues[0]), mp->nresidues) == NULL)
371 memmove(mp2->residues, mp->residues, sizeof(mp->residues[0]) * mp->nresidues);
373 if (mp->cell != NULL) {
374 mp2->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
375 memmove(mp2->cell, mp->cell, sizeof(XtalCell));
378 NewArray(&(mp2->syms), &(mp2->nsyms), sizeof(Transform), mp->nsyms);
379 memmove(mp2->syms, mp->syms, sizeof(Transform) * mp2->nsyms);
382 /* mp2->useFlexibleCell = mp->useFlexibleCell; */
383 if (mp->nframe_cells > 0) {
384 if (NewArray(&mp2->frame_cells, &mp2->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells) == NULL)
386 memmove(mp2->frame_cells, mp->frame_cells, sizeof(Vector) * 4 * mp->nframe_cells);
389 /* FIXME: should bset (basis set info) and elpot be duplicated or not? */
392 mp2->par = ParameterDuplicate(mp->par);
393 if (mp->arena != NULL) {
395 md_arena_init_from_arena(mp2->arena, mp->arena);
400 Panic("Cannot allocate memory for duplicate molecule");
401 return NULL; /* Not reached */
404 /* Assign a unique name to this parameter record */
406 MoleculeSetName(Molecule *mp, const char *name)
408 ObjectSetName((Object *)mp, name, (Object *)sMoleculeRoot);
412 MoleculeGetName(Molecule *mp)
414 return ObjectGetName((Object *)mp);
418 MoleculeWithName(const char *name)
420 return (Molecule *)ObjectWithName(name, (Object *)sMoleculeRoot);
424 MoleculeSetPath(Molecule *mol, const char *fname)
427 if (mol == NULL || fname == NULL)
429 if (fname[0] == '/' || (isalpha(fname[0]) && fname[1] == ':')) {
433 cwd = getcwd(NULL, 0);
434 asprintf(&buf, "%s/%s", cwd, fname);
437 if (mol->path != NULL) {
438 if (strcmp(mol->path, buf) == 0) {
443 free((void *)(mol->path));
446 if (mol->arena != NULL) {
447 md_close_output_files(mol->arena);
452 MoleculeGetPath(Molecule *mol)
460 MoleculeRetain(Molecule *mp)
462 ObjectIncrRefCount((Object *)mp);
463 MoleculeRetainExternalObj(mp);
468 MoleculeClear(Molecule *mp)
472 if (mp->arena != NULL) {
473 md_arena_set_molecule(mp->arena, NULL);
476 if (mp->par != NULL) {
477 ParameterRelease(mp->par);
480 if (mp->bset != NULL) {
481 BasisSetRelease(mp->bset);
484 if (mp->atoms != NULL) {
486 for (i = 0; i < mp->natoms; i++)
487 AtomClean(mp->atoms + i);
492 if (mp->bonds != NULL) {
497 if (mp->angles != NULL) {
502 if (mp->dihedrals != NULL) {
504 mp->dihedrals = NULL;
507 if (mp->impropers != NULL) {
509 mp->impropers = NULL;
512 if (mp->residues != NULL) {
517 if (mp->cell != NULL) {
521 if (mp->syms != NULL) {
526 if (mp->selection != NULL) {
527 IntGroupRelease(mp->selection);
528 mp->selection = NULL;
530 if (mp->frame_cells != NULL) {
531 free(mp->frame_cells);
532 mp->frame_cells = NULL;
533 mp->nframe_cells = 0;
535 if (mp->bset != NULL) {
536 BasisSetRelease(mp->bset);
539 if (mp->par != NULL) {
540 ParameterRelease(mp->par);
543 if (mp->elpots != NULL) {
548 if (mp->path != NULL) {
549 free((void *)mp->path);
555 MoleculeRelease(Molecule *mp)
559 MoleculeReleaseExternalObj(mp);
560 if (ObjectDecrRefCount((Object *)mp) == 0) {
562 mp->mview->mol = NULL;
563 MainView_release(mp->mview);
564 ObjectDealloc((Object *)mp, (Object **)&sMoleculeRoot);
569 MoleculeExchange(Molecule *mp1, Molecule *mp2)
572 struct MainView *mview1, *mview2;
573 struct MDArena *arena1, *arena2;
574 /* mview and arena must be kept as they are */
579 /* 'natoms' is the first member to be copied */
580 int ofs = offsetof(Molecule, natoms);
581 memmove((char *)(&mp_temp) + ofs, (char *)mp1 + ofs, sizeof(Molecule) - ofs);
582 memmove((char *)mp1 + ofs, (char *)mp2 + ofs, sizeof(Molecule) - ofs);
583 memmove((char *)mp2 + ofs, (char *)(&mp_temp) + ofs, sizeof(Molecule) - ofs);
588 /* if (mp1->arena != NULL && mp1->arena->mol == mp2)
589 mp1->arena->mol = mp1;
590 if (mp1->arena != NULL && mp1->arena->xmol == mp2)
591 mp1->arena->xmol = mp1;
592 if (mp2->arena != NULL && mp2->arena->mol == mp1)
593 mp2->arena->mol = mp2;
594 if (mp2->arena != NULL && mp2->arena->xmol == mp1)
595 mp2->arena->xmol = mp2; */
598 #pragma mark ====== Mutex ======
601 MoleculeLock(Molecule *mol)
603 if (mol == NULL || mol->mutex == NULL)
605 MoleculeCallback_lockMutex(mol->mutex);
609 MoleculeUnlock(Molecule *mol)
611 if (mol == NULL || mol->mutex == NULL)
613 MoleculeCallback_unlockMutex(mol->mutex);
616 #pragma mark ====== Modify count ======
619 MoleculeIncrementModifyCount(Molecule *mp)
622 if (++(mp->modifyCount) == 1)
623 MoleculeCallback_notifyModification(mp, 0);
624 /* fprintf(stderr, "MoleculeIncrementModifyCount: %d\n", mp->modifyCount); */
629 MoleculeClearModifyCount(Molecule *mp)
633 /* fprintf(stderr, "MoleculeClearModifyCount: %d\n", mp->modifyCount); */
637 #pragma mark ====== File handling functions ======
640 guessMoleculeType(const char *fname)
644 const char *retval = NULL;
645 fp = fopen(fname, "rb");
647 memset(buf, 0, sizeof buf);
648 if (fread(buf, 1, sizeof buf - 1, fp) > 0) {
649 if (strncmp(buf, "PSF", 3) == 0)
651 else if (((p = strstr(buf, "ATOM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r'))
652 || ((p = strstr(buf, "HETATM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r')))
655 retval = "???"; /* unknown */
663 guessElement(Atom *ap)
665 int atomicNumber = -1;
666 if (ap->atomicNumber > 0)
667 atomicNumber = ap->atomicNumber;
669 atomicNumber = GuessAtomicNumber(ap->element, ap->weight);
670 if (atomicNumber <= 0 && ap->aname[0] != 0)
671 atomicNumber = GuessAtomicNumber(ap->aname, ap->weight);
673 if (atomicNumber >= 0) {
674 ap->atomicNumber = atomicNumber;
676 ap->weight = WeightForAtomicNumber(atomicNumber);
677 if (ap->element[0] == 0)
678 ElementToString(atomicNumber, ap->element);
684 sReadLineWithInterrupt(char *buf, int size, FILE *stream, int *lineNumber)
686 static int lastLineNumber = 0;
687 if (lineNumber != NULL) {
688 if (*lineNumber == 0)
690 else if (*lineNumber >= lastLineNumber + 1000) {
691 if (MyAppCallback_checkInterrupt() != 0)
692 return -1; /* User interrupt */
693 lastLineNumber = *lineNumber;
696 return ReadLine(buf, size, stream, lineNumber);
700 s_append_asprintf(char **buf, const char *fmt, ...)
706 vasprintf(&s, fmt, va);
707 len = (*buf == NULL ? 0 : strlen(*buf));
712 *buf = malloc(len + 1);
715 *buf = realloc(*buf, len + 1);
723 MoleculeLoadFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
726 if (ftype == NULL || *ftype == 0) {
728 cp = strrchr(fname, '.');
732 cp = guessMoleculeType(fname);
733 if (strcmp(cp, "???") != 0)
737 if (strcasecmp(ftype, "psf") == 0) {
738 retval = MoleculeLoadPsfFile(mp, fname, errbuf);
739 } else if (strcasecmp(ftype, "pdb") == 0) {
740 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
741 } else if (strcasecmp(ftype, "tep") == 0) {
742 retval = MoleculeLoadTepFile(mp, fname, errbuf);
743 } else if (strcasecmp(ftype, "res") == 0 || strcasecmp(ftype, "ins") == 0) {
744 retval = MoleculeLoadShelxFile(mp, fname, errbuf);
745 } else if (strcasecmp(ftype, "fchk") == 0 || strcasecmp(ftype, "fch") == 0) {
746 retval = MoleculeLoadGaussianFchkFile(mp, fname, errbuf);
748 s_append_asprintf(errbuf, "Unknown format %s", ftype);
751 /* if (retval != 0) {
752 retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
755 MoleculeSetPath(mp, fname);
760 MoleculeLoadMbsfFile(Molecule *mp, const char *fname, char **errbuf)
764 int i, j, k, err, fn, nframes, nwarnings;
770 double mview_dbuf[10];
773 char *bufp, *valp, *comp;
778 const int kUndefined = -10000000;
782 if (mp->natoms != 0 || mp->par != NULL || mp->arena != NULL) {
783 s_append_asprintf(errbuf, "The molecule must be empty");
786 fp = fopen(fname, "rb");
788 s_append_asprintf(errbuf, "Cannot open file");
791 for (i = 0; i < 10; i++)
792 mview_dbuf[i] = kUndefined;
793 for (i = 0; i < 18; i++)
794 mview_ibuf[i] = kUndefined;
799 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
800 if (strncmp(buf, "!:", 2) != 0)
801 continue; /* Skip until section header is found */
803 strsep(&bufp, " \t\n");
804 if (strcmp(buf, "!:atoms") == 0) {
805 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
810 /* idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge */
811 if (sscanf(buf, "%d %6s %d %6s %6s %6s %lf %lf %6s %d %lf %lf %d", &ibuf[0], cbuf[0], &ibuf[1], cbuf[1], cbuf[2], cbuf[3], &dbuf[0], &dbuf[1], cbuf[4], &ibuf[2], &dbuf[2], &dbuf[3], &ibuf[3]) < 13) {
812 s_append_asprintf(errbuf, "line %d: coordinates cannot be read for atom %d", lineNumber, mp->natoms + 1);
815 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
816 strncpy(ap->segName, cbuf[0], 4);
817 ap->resSeq = ibuf[1];
818 strncpy(ap->resName, cbuf[1], 4);
819 strncpy(ap->aname, cbuf[2], 4);
820 ap->type = AtomTypeEncodeToUInt(cbuf[3]);
821 ap->charge = dbuf[0];
822 ap->weight = dbuf[1];
823 strncpy(ap->element, cbuf[4], 2);
824 ap->atomicNumber = ibuf[2];
825 ap->occupancy = dbuf[2];
826 ap->tempFactor = dbuf[3];
827 ap->intCharge = ibuf[3];
830 } else if (strcmp(buf, "!:atoms_symop") == 0) {
832 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
837 /* idx symop symbase */
838 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
839 s_append_asprintf(errbuf, "line %d: symmetry operations cannot be read for atom %d", lineNumber, i + 1);
842 if (i >= mp->natoms) {
843 s_append_asprintf(errbuf, "line %d: too many atomic symmetry info\n", lineNumber);
846 ap = ATOM_AT_INDEX(mp->atoms, i);
847 ap->symop.sym = ibuf[1] / 1000000;
848 ap->symop.dx = (ibuf[1] % 1000000) / 10000;
849 ap->symop.dy = (ibuf[1] % 10000) / 100;
850 ap->symop.dz = ibuf[1] % 100;
851 ap->symbase = ibuf[2];
855 } else if (strcmp(buf, "!:atoms_fix") == 0) {
857 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
862 /* idx fix_force fix_pos */
863 if (sscanf(buf, "%d %lf %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]) < 5) {
864 s_append_asprintf(errbuf, "line %d: fix atom info cannot be read for atom %d", lineNumber, i + 1);
867 if (i >= mp->natoms) {
868 s_append_asprintf(errbuf, "line %d: too many fix atom info\n", lineNumber);
871 ap = ATOM_AT_INDEX(mp->atoms, i);
872 ap->fix_force = dbuf[0];
873 ap->fix_pos.x = dbuf[1];
874 ap->fix_pos.y = dbuf[2];
875 ap->fix_pos.z = dbuf[3];
879 } else if (strcmp(buf, "!:uff_types") == 0) {
881 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
887 if (sscanf(buf, "%d %6s", &ibuf[0], cbuf[0]) < 2) {
888 s_append_asprintf(errbuf, "line %d: uff type info cannot be read for atom %d", lineNumber, i + 1);
891 if (i >= mp->natoms) {
892 s_append_asprintf(errbuf, "line %d: too many uff type info\n", lineNumber);
895 ap = ATOM_AT_INDEX(mp->atoms, i);
896 strncpy(ap->uff_type, cbuf[0], 5);
900 } else if (strcmp(buf, "!:mm_exclude") == 0) {
902 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
907 /* idx mm_exclude periodic_exclude */
908 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
909 s_append_asprintf(errbuf, "line %d: mm_exclude flags cannot be read for atom %d", lineNumber, i + 1);
912 if (i >= mp->natoms) {
913 s_append_asprintf(errbuf, "line %d: too many mm_exclude flags\n", lineNumber);
916 ap = ATOM_AT_INDEX(mp->atoms, i);
917 ap->mm_exclude = (ibuf[1] != 0);
918 ap->periodic_exclude = (ibuf[2] != 0);
922 } else if (strcmp(buf, "!:pi_anchor") == 0) {
923 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
929 if ((j = sscanf(buf, "%d %d", &ibuf[0], &ibuf[1])) < 2) {
930 s_append_asprintf(errbuf, "line %d: bad format for pi_anchor", lineNumber);
934 ap = ATOM_AT_INDEX(mp->atoms, i);
935 if (ap->anchor != NULL) {
936 s_append_asprintf(errbuf, "line %d: warning: duplicate pi_anchor entry", lineNumber);
937 AtomConnectResize(&ap->anchor->connect, 0);
938 free(ap->anchor->coeffs);
941 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
942 if (ibuf[1] < 2 || ibuf[1] >= mp->natoms) {
943 s_append_asprintf(errbuf, "line %d: bad number of components for pi_anchor", lineNumber);
946 AtomConnectResize(&ap->anchor->connect, ibuf[1]);
947 ip = AtomConnectData(&ap->anchor->connect);
948 NewArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), ibuf[1]);
950 for (i = 0; i < j; i++) {
951 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
952 s_append_asprintf(errbuf, "line %d: unexpected end of file while reading pi_anchors", lineNumber);
955 if (sscanf(buf, "%d %lf", &ibuf[0], &dbuf[0]) < 2) {
956 s_append_asprintf(errbuf, "line %d: bad format for pi_anchor", lineNumber);
959 if (ibuf[0] < 0 || ibuf[0] >= mp->natoms) {
960 s_append_asprintf(errbuf, "line %d: atom index out of range", lineNumber);
963 if (dbuf[0] <= 0.0) {
964 s_append_asprintf(errbuf, "line %d: the pi anchor weights should be positive", lineNumber);
968 ap->anchor->coeffs[i] = dbuf[0];
972 } else if (strcmp(buf, "!:positions") == 0) {
974 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
980 if ((j = sscanf(buf, "%d %lf %lf %lf %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5])) < 4) {
981 s_append_asprintf(errbuf, "line %d: atom position cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
984 if (j > 4 && nframes != 0) {
985 s_append_asprintf(errbuf, "line %d: atom position sigma can only be given for frame 0", lineNumber);
988 if (j > 4 && j != 7) {
989 s_append_asprintf(errbuf, "line %d: atom position sigma cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
992 if (i >= mp->natoms) {
993 s_append_asprintf(errbuf, "line %d: too many atom position records\n", lineNumber);
999 ap = ATOM_AT_INDEX(mp->atoms, i);
1001 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes, &v);
1003 ap->frames[0] = ap->r;
1007 ap->sigma.x = dbuf[3];
1008 ap->sigma.y = dbuf[4];
1009 ap->sigma.z = dbuf[5];
1015 mp->nframes = nframes;
1016 mp->cframe = nframes - 1;
1018 mp->nframes = mp->cframe = 0;
1021 } else if (strcmp(buf, "!:bonds") == 0) {
1022 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1027 /* from1 to1 from2 to2 from3 to3 from4 to4 */
1028 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]);
1029 if (i < 2 || i % 2 != 0) {
1030 s_append_asprintf(errbuf, "line %d: bad bond format", lineNumber);
1033 for (j = 0; j < i; j += 2) {
1035 iibuf[1] = ibuf[j + 1];
1036 if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[0] == iibuf[1]) {
1037 s_append_asprintf(errbuf, "line %d: warning: bad bond specification (%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1]);
1039 } else if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[0])->connect), iibuf[1])) {
1040 s_append_asprintf(errbuf, "line %d: warning: bond %d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1]);
1043 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, iibuf);
1044 AtomConnectInsertEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[0])->connect), -1, iibuf[1]);
1045 AtomConnectInsertEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[1])->connect), -1, iibuf[0]);
1050 } else if (strcmp(buf, "!:bond_orders") == 0) {
1051 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1057 i = sscanf(buf, "%lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]);
1059 s_append_asprintf(errbuf, "line %d: bad bond order format", lineNumber);
1062 for (j = 0; j < i; j++) {
1063 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbondOrders, &dbuf[j]);
1066 if (mp->nbondOrders > mp->nbonds) {
1067 s_append_asprintf(errbuf, "line %d: warning: the number of bond order info (%d) exceeds number of bonds (%d) - ignoring excess info\n", lineNumber, mp->nbondOrders, mp->nbonds);
1069 mp->nbondOrders = mp->nbonds;
1070 } else if (mp->nbondOrders < mp->nbonds) {
1071 s_append_asprintf(errbuf, "line %d: warning: the number of bond order info (%d) is less than number of bonds (%d)\n", lineNumber, mp->nbondOrders, mp->nbonds);
1073 j = mp->nbondOrders;
1074 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbonds - 1, NULL);
1075 for (i = j; i < mp->nbonds; i++)
1076 mp->bondOrders[i] = 0.0;
1080 } else if (strcmp(buf, "!:angles") == 0) {
1081 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1086 /* a1 b1 c1 a2 b2 c2 a3 b3 c3 */
1087 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]);
1088 if (i == 0 || i % 3 != 0) {
1089 s_append_asprintf(errbuf, "line %d: bad angle format", lineNumber);
1092 for (j = 0; j < i; j += 3) {
1094 iibuf[1] = ibuf[j + 1];
1095 iibuf[2] = ibuf[j + 2];
1096 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]) {
1097 s_append_asprintf(errbuf, "line %d: warning: bad angle specification (%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1099 } else if (MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[2]) == 0) {
1100 s_append_asprintf(errbuf, "line %d: warning: angle with non-bonded atoms (%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1102 } else if (MoleculeLookupAngle(mp, iibuf[0], iibuf[1], iibuf[2]) >= 0) {
1103 s_append_asprintf(errbuf, "line %d: warning: angle %d-%d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1106 AssignArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, mp->nangles, iibuf);
1111 } else if (strcmp(buf, "!:dihedrals") == 0) {
1112 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1117 /* a1 b1 c1 d1 a2 b2 c2 d2 */
1118 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]);
1119 if (i == 0 || i % 4 != 0) {
1120 s_append_asprintf(errbuf, "line %d: bad dihedral format", lineNumber);
1123 for (j = 0; j < i; j += 4) {
1125 iibuf[1] = ibuf[j + 1];
1126 iibuf[2] = ibuf[j + 2];
1127 iibuf[3] = ibuf[j + 3];
1128 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]) {
1129 s_append_asprintf(errbuf, "line %d: warning: bad dihedral specification (%d-%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2], iibuf[3]);
1131 } else if (MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[2]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[3]) == 0) {
1132 s_append_asprintf(errbuf, "line %d: warning: dihedral with non-bonded atoms (%d-%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2], iibuf[3]);
1134 } else if (MoleculeLookupDihedral(mp, iibuf[0], iibuf[1], iibuf[2], iibuf[3]) >= 0) {
1135 s_append_asprintf(errbuf, "line %d: warning: dihedral %d-%d-%d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2], iibuf[3]);
1138 AssignArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, mp->ndihedrals, iibuf);
1143 } else if (strcmp(buf, "!:impropers") == 0) {
1144 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1149 /* a1 b1 c1 d1 a2 b2 c2 d2 */
1150 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]);
1151 if (i == 0 || i % 4 != 0) {
1152 s_append_asprintf(errbuf, "line %d: bad improper format", lineNumber);
1155 for (j = 0; j < i; j += 4) {
1157 iibuf[1] = ibuf[j + 1];
1158 iibuf[2] = ibuf[j + 2];
1159 iibuf[3] = ibuf[j + 3];
1160 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]) {
1161 s_append_asprintf(errbuf, "line %d: warning: bad improper specification (%d-%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2], iibuf[3]);
1163 } else if (MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[1]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[3]) == 0) {
1164 s_append_asprintf(errbuf, "line %d: warning: improper with non-bonded atoms (%d-%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2], iibuf[3]);
1166 } else if (MoleculeLookupImproper(mp, iibuf[0], iibuf[1], iibuf[2], iibuf[3]) >= 0) {
1167 s_append_asprintf(errbuf, "line %d: warning: improper %d-%d-%d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2], iibuf[3]);
1170 AssignArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, mp->nimpropers, iibuf);
1175 } else if (strcmp(buf, "!:xtalcell") == 0 && mp->cell == NULL) {
1176 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1181 /* a b c alpha beta gamma [sigmaflag] */
1182 if ((j = sscanf(buf, "%lf %lf %lf %lf %lf %lf %d", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5], &ibuf[0])) < 6) {
1183 s_append_asprintf(errbuf, "line %d: bad xtalcell format", lineNumber);
1186 MoleculeSetCell(mp, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], 0);
1187 if (j == 7 && ibuf[0] != 0) {
1188 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1189 s_append_asprintf(errbuf, "line %d: sigma for xtalcell are missing", lineNumber);
1192 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1193 s_append_asprintf(errbuf,"line %d: bad xtalcell sigma format", lineNumber);
1196 if (mp->cell != NULL) {
1197 mp->cell->has_sigma = 1;
1198 for (i = 0; i < 6; i++) {
1199 mp->cell->cellsigma[i] = dbuf[i];
1202 s_append_asprintf(errbuf, "line %d: cell sigma are given while cell is not given", lineNumber);
1207 } else if (strcmp(buf, "!:symmetry_operations") == 0) {
1209 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1215 /* a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3 */
1216 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1217 s_append_asprintf(errbuf, "line %d: bad symmetry_operation format", lineNumber);
1222 tr[i + 3] = dbuf[1];
1223 tr[i + 6] = dbuf[2];
1231 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1236 } else if (strcmp(buf, "!:anisotropic_thermal_parameters") == 0) {
1238 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1243 /* b11 b22 b33 b12 b13 b23 [has_sigma] */
1244 if ((j = sscanf(buf, "%lf %lf %lf %lf %lf %lf %d", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5], &ibuf[0])) < 6) {
1245 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters cannot be read for atom %d", lineNumber, i + 1);
1248 if (i >= mp->natoms) {
1249 s_append_asprintf(errbuf, "line %d: too many anisotropic thermal parameters\n", lineNumber);
1252 if (dbuf[0] == 0.0 && dbuf[1] == 0.0 && dbuf[2] == 0.0 && dbuf[3] == 0.0 && dbuf[4] == 0.0 && dbuf[5] == 0.0) {
1255 MoleculeSetAniso(mp, i, 0, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], NULL);
1257 if (j == 7 && ibuf[0] != 0) {
1258 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1259 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma missing", lineNumber);
1262 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1263 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma cannot be read for atom %d", lineNumber, i + 1);
1266 ap = ATOM_AT_INDEX(mp->atoms, i);
1267 if (ap->aniso == NULL) {
1268 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma are given while the parameters are not given", lineNumber);
1271 ap->aniso->has_bsig = 1;
1272 for (j = 0; j < 6; j++)
1273 ap->aniso->bsig[j] = dbuf[j];
1278 } else if (strcmp(buf, "!:periodic_box") == 0) {
1282 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1287 /* ax ay az; bx by bz; cx cy cz; ox oy oz; fx fy fz [sigma; sa sb sc s_alpha s_beta s_gamma] */
1289 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1290 s_append_asprintf(errbuf, "line %d: bad periodic_box format", lineNumber);
1299 if ((j = sscanf(buf, "%d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3])) < 3) {
1300 s_append_asprintf(errbuf, "line %d: bad periodic_box format", lineNumber);
1303 if (j == 4 && ibuf[3] != 0)
1305 cbuf[0][0] = ibuf[0];
1306 cbuf[0][1] = ibuf[1];
1307 cbuf[0][2] = ibuf[2];
1308 MoleculeSetPeriodicBox(mp, vs, vs + 1, vs + 2, vs + 3, cbuf[0], 0);
1310 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1311 s_append_asprintf(errbuf, "line %d: sigma for cell parameters are missing", lineNumber);
1314 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1315 s_append_asprintf(errbuf, "line %d: bad periodic_box sigma format", lineNumber);
1318 if (mp->cell != NULL) {
1319 mp->cell->has_sigma = 1;
1320 for (i = 0; i < 6; i++) {
1321 mp->cell->cellsigma[i] = dbuf[i];
1324 s_append_asprintf(errbuf, "line %d: cell sigma are given while cell is not given", lineNumber);
1330 } else if (strcmp(buf, "!:frame_periodic_boxes") == 0) {
1333 /* mp->useFlexibleCell = 1; *//* The presence of this block causes asserting this flag */
1334 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1339 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1340 s_append_asprintf(errbuf, "line %d: bad frame_periodic_box format", lineNumber);
1348 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells, vs);
1352 if (mp->cframe < mp->nframe_cells) {
1353 /* mp->cframe should already have been set when positions are read */
1354 Vector *vp = &mp->frame_cells[mp->cframe * 4];
1355 static char defaultFlags[] = {1, 1, 1};
1356 char *flags = (mp->cell != NULL ? mp->cell->flags : defaultFlags);
1357 MoleculeSetPeriodicBox(mp, vp, vp + 1, vp + 2, vp + 3, flags, 0);
1360 } else if (strcmp(buf, "!:md_parameters") == 0) {
1362 if (mp->arena == NULL)
1363 mp->arena = md_arena_new(NULL);
1365 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1371 comp = strsep(&bufp, " \t");
1373 while (*bufp == ' ' || *bufp == '\t')
1375 valp = strsep(&bufp, "\n");
1377 if (strcmp(comp, "alchem_flags") == 0) {
1378 j = (valp == NULL ? 0 : atoi(valp));
1380 valp = (char *)malloc(j);
1382 while ((k = fgetc(fp)) >= 0) {
1384 if (k < '0' || k > '9') {
1385 s_append_asprintf(errbuf, "line %d: too few flags in alchem_flags block", lineNumber + 1);
1389 ReadLine(buf, sizeof buf, fp, &lineNumber);
1391 while (*bufp != 0) {
1392 if (*bufp >= '0' && *bufp <= '2') {
1394 s_append_asprintf(errbuf, "line %d: too many flags in alchem_flags block", lineNumber);
1398 valp[i++] = *bufp - '0';
1399 } else if (*bufp != ' ' && *bufp != '\t' && *bufp != '\n') {
1400 s_append_asprintf(errbuf, "line %d: strange character (0x%02x) in alchem_flags block", lineNumber, (int)*bufp);
1409 md_set_alchemical_flags(arena, j, valp);
1414 /* In the following, the redundant "!= NULL" is to suppress suprious warning */
1415 if ((strcmp(comp, "log_file") == 0 && (pp = &arena->log_result_name) != NULL)
1416 || (strcmp(comp, "coord_file") == 0 && (pp = &arena->coord_result_name) != NULL)
1417 || (strcmp(comp, "vel_file") == 0 && (pp = &arena->vel_result_name) != NULL)
1418 || (strcmp(comp, "force_file") == 0 && (pp = &arena->force_result_name) != NULL)
1419 || (strcmp(comp, "debug_file") == 0 && (pp = &arena->debug_result_name) != NULL)) {
1420 if (*valp == 0 || strstr(valp, "(null)") == valp)
1423 valp = strdup(valp);
1425 char *valp1 = strchr(valp, '\n');
1431 } else if ((strcmp(comp, "debug_output_level") == 0 && (ip = &arena->debug_output_level) != NULL)
1432 || (strcmp(comp, "coord_output_freq") == 0 && (ip = &arena->coord_output_freq) != NULL)
1433 || (strcmp(comp, "energy_output_freq") == 0 && (ip = &arena->energy_output_freq) != NULL)
1434 || (strcmp(comp, "coord_frame") == 0 && (ip = &arena->coord_result_frame) != NULL)
1435 || (strcmp(comp, "andersen_freq") == 0 && (ip = &arena->andersen_thermo_freq) != NULL)
1436 || (strcmp(comp, "random_seed") == 0 && (ip = &arena->random_seed) != NULL)
1437 || (strcmp(comp, "use_xplor_shift") == 0 && (ip = &arena->use_xplor_shift) != NULL)
1438 || (strcmp(comp, "relocate_center") == 0 && (ip = &arena->relocate_center) != NULL)
1439 || (strcmp(comp, "surface_potential_freq") == 0 && (ip = &arena->surface_potential_freq) != NULL)
1440 || (strcmp(comp, "use_graphite") == 0 && (ip = &arena->use_graphite) != NULL)) {
1441 *ip = (valp == NULL ? 0 : atoi(valp));
1442 } else if ((strcmp(comp, "timestep") == 0 && (dp = &arena->timestep) != NULL)
1443 || (strcmp(comp, "cutoff") == 0 && (dp = &arena->cutoff) != NULL)
1444 || (strcmp(comp, "electro_cutoff") == 0 && (dp = &arena->electro_cutoff) != NULL)
1445 || (strcmp(comp, "pairlist_distance") == 0 && (dp = &arena->pairlist_distance) != NULL)
1446 || (strcmp(comp, "switch_distance") == 0 && (dp = &arena->switch_distance) != NULL)
1447 || (strcmp(comp, "temperature") == 0 && (dp = &arena->temperature) != NULL)
1448 || (strcmp(comp, "andersen_coupling") == 0 && (dp = &arena->andersen_thermo_coupling) != NULL)
1449 || (strcmp(comp, "dielectric") == 0 && (dp = &arena->dielectric) != NULL)
1450 || (strcmp(comp, "gradient_convergence") == 0 && (dp = &arena->gradient_convergence) != NULL)
1451 || (strcmp(comp, "coordinate_convergence") == 0 && (dp = &arena->coordinate_convergence) != NULL)
1452 || (strcmp(comp, "scale14_vdw") == 0 && (dp = &arena->scale14_vdw) != NULL)
1453 || (strcmp(comp, "scale14_elect") == 0 && (dp = &arena->scale14_elect) != NULL)
1454 || (strcmp(comp, "surface_probe_radius") == 0 && (dp = &arena->probe_radius) != NULL)
1455 || (strcmp(comp, "surface_tension") == 0 && (dp = &arena->surface_tension) != NULL)
1456 || (strcmp(comp, "alchemical_lambda") == 0 && (dp = &arena->alchem_lambda) != NULL)
1457 || (strcmp(comp, "alchemical_delta_lambda") == 0 && (dp = &arena->alchem_dlambda) != NULL)) {
1458 *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1462 } else if (strcmp(buf, "!:pressure_control_parameters") == 0) {
1463 MDPressureArena *pressure;
1464 if (mp->arena == NULL)
1465 mp->arena = md_arena_new(mp);
1466 if (mp->arena->pressure == NULL)
1467 mp->arena->pressure = pressure_new();
1468 pressure = mp->arena->pressure;
1469 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1475 comp = strsep(&bufp, " \t");
1477 while (*bufp == ' ' || *bufp == '\t')
1479 valp = strsep(&bufp, "\n");
1481 if (strcmp(comp, "pressure") == 0) {
1482 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) {
1483 s_append_asprintf(errbuf, "line %d: bad format", lineNumber);
1486 for (i = 0; i < 9; i++)
1487 pressure->apply[i] = dbuf[i];
1488 } else if (strcmp(comp, "pressure_cell_flexibility") == 0) {
1489 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) {
1490 s_append_asprintf(errbuf, "line %d: bad format", lineNumber);
1493 for (i = 0; i < 8; i++)
1494 pressure->cell_flexibility[i] = dbuf[i];
1495 } else if ((strcmp(comp, "pressure_freq") == 0 && (ip = &pressure->freq) != NULL)) {
1496 *ip = (valp == NULL ? 0 : atoi(valp));
1497 } else if ((strcmp(comp, "pressure_coupling") == 0 && (dp = &pressure->coupling) != NULL)
1498 || (strcmp(comp, "pressure_fluctuate_cell_origin") == 0 && (dp = &pressure->fluctuate_cell_origin) != NULL)
1499 || (strcmp(comp, "pressure_fluctuate_cell_orientation") == 0 && (dp = &pressure->fluctuate_cell_orientation) != NULL)) {
1500 *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1504 } else if (strcmp(buf, "!:velocity") == 0) {
1506 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1512 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1513 s_append_asprintf(errbuf, "line %d: atom velocity cannot be read for atom %d", lineNumber, i + 1);
1516 if (i >= mp->natoms) {
1517 s_append_asprintf(errbuf, "line %d: too many atom velocity records\n", lineNumber);
1520 ap = ATOM_AT_INDEX(mp->atoms, i);
1527 } else if (strcmp(buf, "!:force") == 0) {
1529 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1535 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1536 s_append_asprintf(errbuf, "line %d: atom force cannot be read for atom %d", lineNumber, i + 1);
1539 if (i >= mp->natoms) {
1540 s_append_asprintf(errbuf, "line %d: too many atom force records\n", lineNumber);
1543 ap = ATOM_AT_INDEX(mp->atoms, i);
1550 } else if (strcmp(buf, "!:parameter") == 0 || strcmp(buf, "!:parameters") == 0) {
1551 Parameter *par = mp->par;
1553 mp->par = ParameterNew();
1558 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1563 j = ParameterReadFromString(par, buf, &bufp, fname, lineNumber, 0);
1565 s_append_asprintf(errbuf, "%s", bufp);
1572 s_append_asprintf(errbuf, "%s", bufp);
1576 } else if (strcmp(buf, "!:trackball") == 0) {
1578 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1583 /* scale; trx try trz; theta_deg x y z */
1584 if ((i == 0 && sscanf(buf, "%lf", &mview_dbuf[0]) < 1)
1585 || (i == 1 && sscanf(buf, "%lf %lf %lf",
1586 &mview_dbuf[1], &mview_dbuf[2], &mview_dbuf[3]) < 3)
1587 || (i == 2 && sscanf(buf, "%lf %lf %lf %lf",
1588 &mview_dbuf[4], &mview_dbuf[5], &mview_dbuf[6], &mview_dbuf[7]) < 4)) {
1589 s_append_asprintf(errbuf, "line %d: bad trackball format", lineNumber);
1595 } else if (strcmp(buf, "!:view") == 0) {
1596 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1602 comp = strsep(&bufp, " \t");
1604 while (*bufp == ' ' || *bufp == '\t')
1606 valp = strsep(&bufp, "\n");
1608 /* In the following, the redundant "!= NULL" is to suppress suprious warning */
1609 if ((strcmp(comp, "show_unit_cell") == 0 && (i = 1))
1610 || (strcmp(comp, "show_periodic_box") == 0 && (i = 2))
1611 || (strcmp(comp, "show_expanded_atoms") == 0 && (i = 3))
1612 || (strcmp(comp, "show_ellipsoids") == 0 && (i = 4))
1613 || (strcmp(comp, "show_hydrogens") == 0 && (i = 5))
1614 || (strcmp(comp, "show_dummy_atoms") == 0 && (i = 6))
1615 || (strcmp(comp, "show_rotation_center") == 0 && (i = 7))
1616 || (strcmp(comp, "show_graphite_flag") == 0 && (i = 8))
1617 || (strcmp(comp, "show_periodic_image_flag") == 0 && (i = 9))
1618 || (strcmp(comp, "show_graphite") == 0 && (i = 10))
1619 || (strcmp(comp, "atom_resolution") == 0 && (i = 11))
1620 || (strcmp(comp, "bond_resolution") == 0 && (i = 12))) {
1621 mview_ibuf[i - 1] = atoi(valp);
1622 } else if ((strcmp(comp, "atom_radius") == 0 && (i = 8))
1623 || (strcmp(comp, "bond_radius") == 0 && (i = 9))) {
1624 mview_dbuf[i] = strtod(valp, NULL);
1625 } else if (strcmp(comp, "show_periodic_image") == 0) {
1626 sscanf(valp, "%d %d %d %d %d %d",
1627 &mview_ibuf[12], &mview_ibuf[13], &mview_ibuf[14],
1628 &mview_ibuf[15], &mview_ibuf[16], &mview_ibuf[17]);
1633 /* Unknown sections are silently ignored */
1636 MoleculeCleanUpResidueTable(mp);
1637 if (mp->arena != NULL)
1638 md_arena_set_molecule(mp->arena, mp);
1641 if (mp->mview != NULL) {
1642 if (mview_ibuf[0] != kUndefined)
1643 mp->mview->showUnitCell = mview_ibuf[0];
1644 if (mview_ibuf[1] != kUndefined)
1645 mp->mview->showPeriodicBox = mview_ibuf[1];
1646 if (mview_ibuf[2] != kUndefined)
1647 mp->mview->showExpandedAtoms = mview_ibuf[2];
1648 if (mview_ibuf[3] != kUndefined)
1649 mp->mview->showEllipsoids = mview_ibuf[3];
1650 if (mview_ibuf[4] != kUndefined)
1651 mp->mview->showHydrogens = mview_ibuf[4];
1652 if (mview_ibuf[5] != kUndefined)
1653 mp->mview->showDummyAtoms = mview_ibuf[5];
1654 if (mview_ibuf[6] != kUndefined)
1655 mp->mview->showRotationCenter = mview_ibuf[6];
1656 if (mview_ibuf[7] != kUndefined)
1657 mp->mview->showGraphiteFlag = mview_ibuf[7];
1658 if (mview_ibuf[8] != kUndefined)
1659 mp->mview->showPeriodicImageFlag = mview_ibuf[8];
1660 if (mview_ibuf[9] != kUndefined)
1661 mp->mview->showGraphite = mview_ibuf[9];
1662 if (mview_ibuf[10] != kUndefined && mview_ibuf[10] >= 6)
1663 mp->mview->atomResolution = mview_ibuf[10];
1664 if (mview_ibuf[11] != kUndefined && mview_ibuf[11] >= 4)
1665 mp->mview->bondResolution = mview_ibuf[11];
1666 for (i = 0; i < 6; i++) {
1667 if (mview_ibuf[12 + i] != kUndefined)
1668 mp->mview->showPeriodicImage[i] = mview_ibuf[12 + i];
1670 if (mview_dbuf[8] != kUndefined)
1671 mp->mview->atomRadius = mview_dbuf[8];
1672 if (mview_dbuf[9] != kUndefined)
1673 mp->mview->bondRadius = mview_dbuf[9];
1674 if (mp->mview->track != NULL) {
1675 if (mview_dbuf[0] != kUndefined)
1676 TrackballSetScale(mp->mview->track, mview_dbuf[0]);
1677 if (mview_dbuf[1] != kUndefined)
1678 TrackballSetTranslate(mp->mview->track, mview_dbuf + 1);
1679 if (mview_dbuf[4] != kUndefined)
1680 TrackballSetRotate(mp->mview->track, mview_dbuf + 4);
1688 /* The content of mp may be broken, so make it empty */
1694 MoleculeLoadPsfFile(Molecule *mp, const char *fname, char **errbuf)
1703 Vector *frames = NULL;
1709 else MoleculeClear(mp);
1710 fp = fopen(fname, "rb");
1712 s_append_asprintf(errbuf, "Cannot open file");
1715 /* flockfile(fp); */
1718 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1719 if (strncmp(buf, "PSF", 3) == 0) {
1723 for (p = buf; *p != 0 && isspace(*p); p++) {}
1729 if (strstr(buf, "!COORD") != NULL) {
1730 /* Extended psf file with coordinates */
1732 /* Allocate a temporary storage for frames */
1733 size_t size = sizeof(Vector) * mp->natoms * fn;
1735 frames = (Vector *)malloc(size);
1737 frames = (Vector *)realloc(frames, size);
1741 /* Read coordinates */
1742 for (i = 0; i < mp->natoms; i++) {
1745 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1747 s_append_asprintf(errbuf, "line %d: premature end of file while reading coordinates (frame %d)", lineNumber, fn);
1750 if (sscanf(buf, "%lg %lg %lg", dval, dval + 1, dval + 2) != 3) {
1752 s_append_asprintf(errbuf, "line %d: coordinates cannot be read for atom %d", lineNumber, i + 1);
1759 ATOM_AT_INDEX(mp->atoms, i)->r = r;
1761 frames[mp->natoms * (fn - 1) + i] = r;
1770 ReadFormat(buf, "I8", &natoms);
1773 if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
1776 for (i = 0; i < natoms; i++) {
1778 char segName[5], resName[4], atomName[5], atomType[3], element[3];
1781 memset(&w, 0, sizeof(w));
1782 ap = ATOM_AT_INDEX(mp->atoms, i);
1783 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1785 s_append_asprintf(errbuf, "line %d: premature end of file while reading atoms", lineNumber);
1788 ReadFormat(buf, "I8 x1 S4 I5 x1 S3 x2 S4 x1 S4 F16 F10",
1789 &w.serial, w.segName, &ap->resSeq, w.resName, w.atomName,
1790 w.atomType, &ap->charge, &ap->weight);
1791 strncpy(ap->segName, w.segName, 4);
1792 strncpy(ap->resName, w.resName, 3);
1793 strncpy(ap->aname, w.atomName, 4);
1794 ap->type = AtomTypeEncodeToUInt(w.atomType);
1795 /* $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl */
1796 ap->atomicNumber = GuessAtomicNumber(w.atomName, ap->weight);
1797 ElementToString(ap->atomicNumber, w.element);
1798 strncpy(ap->element, w.element, 2);
1799 /* w.element[0] = 0;
1800 for (p = w.atomName; *p != 0; p++) {
1801 if (isalpha(*p) && *p != '_') {
1802 w.element[0] = toupper(*p);
1803 if (isalpha(p[1]) && p[1] != '_') {
1804 w.element[1] = toupper(p[1]);
1812 strncpy(ap->element, w.element, 2);
1813 ap->atomicNumber = ElementToInt(w.element); */
1814 if (w.resName[0] == 0)
1815 strncpy(ap->resName, "XXX", 3);
1816 if (ap->resSeq > mp->nresidues)
1817 mp->nresidues = ap->resSeq;
1819 if (mp->residues != NULL)
1821 if (NewArray(&mp->residues, &mp->nresidues, sizeof(char (*)[4]), mp->nresidues + 1) == 0)
1823 for (i = 0; i < mp->natoms; i++) {
1824 j = mp->atoms[i].resSeq;
1825 if (mp->residues[j][0] == 0)
1826 strncpy(mp->residues[j], mp->atoms[i].resName, 4);
1829 } else if (section == 3) {
1833 ReadFormat(buf, "I8", &nbonds);
1836 if (NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, nbonds) == NULL)
1839 for (i = 0; i < nbonds; i += 4) {
1840 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1841 s_append_asprintf(errbuf, "line %d: premature end of file while reading bonds", lineNumber);
1845 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1846 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1847 for (j = 0; j < 4 && i + j < nbonds; j++) {
1850 b1 = ibuf[j * 2] - 1; /* Internal atom number is 0-based */
1851 b2 = ibuf[j * 2 + 1] - 1;
1852 if (b1 < 0 || b1 >= mp->natoms || b2 < 0 || b2 >= mp->natoms) {
1853 s_append_asprintf(errbuf, "line %d: The bond %d-%d includes non-existent atom", lineNumber, b1+1, b2+1);
1859 ap = ATOM_AT_INDEX(mp->atoms, b1);
1860 AtomConnectInsertEntry(&ap->connect, -1, b2);
1861 ap = ATOM_AT_INDEX(mp->atoms, b2);
1862 AtomConnectInsertEntry(&ap->connect, -1, b1);
1866 } else if (section == 4) {
1870 ReadFormat(buf, "I8", &nangles);
1873 if (NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, nangles) == NULL)
1876 for (i = 0; i < nangles; i += 3) {
1877 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1878 s_append_asprintf(errbuf, "line %d: premature end of file while reading angles", lineNumber);
1882 ReadFormat(buf, "I8I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1883 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7, ibuf + 8);
1884 for (j = 0; j < 3 && i + j < nangles; j++) {
1886 a1 = ibuf[j * 3] - 1; /* Internal atom number is 0-based */
1887 a2 = ibuf[j * 3 + 1] - 1;
1888 a3 = ibuf[j * 3 + 2] - 1;
1889 if (a1 < 0 || a1 >= mp->natoms || a2 < 0 || a2 >= mp->natoms || a3 < 0 || a3 >= mp->natoms) {
1890 s_append_asprintf(errbuf, "line %d: The angle %d-%d-%d includes non-existent atom", lineNumber, a1+1, a2+1, a3+1);
1900 } else if (section == 5 || section == 6) {
1901 /* Dihedrals and Impropers */
1904 ReadFormat(buf, "I8", &ndihedrals);
1905 if (ndihedrals == 0)
1908 if (NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, ndihedrals) == NULL)
1912 if (NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, ndihedrals) == NULL)
1916 for (i = 0; i < ndihedrals; i += 2) {
1917 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1919 s_append_asprintf(errbuf, "line %d: premature end of file while reading %s", lineNumber, (section == 5 ? "dihedral" : "improper"));
1923 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3, ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1924 for (j = 0; j < 2 && i + j < ndihedrals; j++) {
1926 d1 = ibuf[j * 4] - 1; /* Internal atom number is 0-based */
1927 d2 = ibuf[j * 4 + 1] - 1;
1928 d3 = ibuf[j * 4 + 2] - 1;
1929 d4 = ibuf[j * 4 + 3] - 1;
1930 if (d1 < 0 || d1 >= mp->natoms || d2 < 0 || d2 >= mp->natoms || d3 < 0 || d3 >= mp->natoms || d4 < 0 || d4 >= mp->natoms) {
1931 s_append_asprintf(errbuf, "line %d: The %s %d-%d-%d-%d angle includes non-existent atom", lineNumber, (section == 5 ? "dihedral" : "improper"), d1+1, d2+1, d3+1, d4+1);
1945 /* Create frames for each atom if necessary */
1947 for (i = 0; i < mp->natoms; i++) {
1948 ap = ATOM_AT_INDEX(mp->atoms, i);
1949 ap->frames = (Vector *)malloc(sizeof(Vector) * fn);
1950 if (ap->frames == NULL)
1953 for (j = 0; j < fn; j++)
1954 ap->frames[j] = frames[mp->natoms * j + i];
1961 /* funlockfile(fp); */
1963 mp->nframes = -1; /* Should be recalculated later */
1966 else if (section == -1)
1970 Panic("low memory while reading structure file %s", fname);
1971 return 1; /* not reached */
1974 /* ("-x", "y", "-z+0.5") -> (-1,0,0,0,0,1,0,0,0,0,-1,0.5) */
1976 sMoleculeSymopStringsToTransform(char **symops, Transform tr)
1980 memset(tr, 0, sizeof(Transform));
1981 for (i = 0; i < 3; i++) {
1985 while (*symop != 0) {
1987 while (isspace(*symop))
1989 if (*symop == 0 || *symop == '\r' || *symop == 'n')
1991 if (*symop == '-') {
1994 } else if (*symop == '+') {
1998 while (isspace(*symop))
2000 if (*symop == '.' || isdigit(*symop)) {
2001 /* Numerical offset */
2002 double d = strtod(symop, &symop);
2003 if (*symop == '/') {
2004 double dd = strtod(symop + 1, &symop);
2008 return 1; /* Bad format */
2011 } else if (*symop == 'x' || *symop == 'X') {
2014 } else if (*symop == 'y' || *symop == 'Y') {
2017 } else if (*symop == 'z' || *symop == 'Z') {
2020 } else return 1; /* Bad format */
2021 } /* end while (*symop != 0) */
2027 sMoleculeGenerateSymopWithTransform(Molecule *mp, Transform gtr, int num)
2033 for (i = 0; i < num; i++) {
2034 memmove(tr, mp->syms[i], sizeof(Transform));
2035 TransformMul(tr, gtr, tr);
2036 for (j = 9; j < 12; j++) {
2039 else if (tr[j] <= 0.0)
2042 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
2049 char *p = buf + strlen(buf) - 1;
2050 if (p >= buf && (*p == '\n' || *p == '\r')) {
2052 if (--p >= buf && (*p == '\n' || *p == '\r')) {
2060 MoleculeLoadTepFile(Molecule *mp, const char *fname, char **errbuf)
2073 fp = fopen(fname, "rb");
2075 s_append_asprintf(errbuf, "Cannot open file");
2079 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2080 if (section == -1) {
2087 ReadFormat(buf, "I1F8F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5);
2089 MoleculeSetCell(mp, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], 0);
2096 if (cellType == 0) {
2097 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);
2111 char *symops[3], *brks;
2113 memset(tr, 0, sizeof(Transform));
2114 ReadFormat(buf, "I1", ibuf);
2115 symops[0] = strtok_r(buf + 1, ", ", &brks);
2116 symops[1] = strtok_r(NULL, ", ", &brks);
2117 symops[2] = strtok_r(NULL, ", ", &brks);
2118 if (sMoleculeSymopStringsToTransform(symops, tr)) {
2119 s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2123 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2129 if (section == 2) { /* Atoms */
2133 int atomIndex = mp->natoms;
2134 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2135 memset(ap, 0, gSizeOfAtomRecord);
2136 ReadFormat(buf, "S6x3x9x9F9F9F9F9", name, fbuf, fbuf+1, fbuf+2, fbuf+3);
2137 strncpy(ap->aname, name, 4);
2141 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2142 /* ap->atomicNumber = AtomNameToElement(ap->name);
2143 ElementToString(ap->atomicNumber, ap->element); */
2144 /* sAtomSetElement(ap, -1, ap->name); */
2147 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2148 s_append_asprintf(errbuf, "unexpected end of file");
2151 ReadFormat(buf, "I1F8F9F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2153 if ((atomType >= 0 && atomType <= 5) || (atomType >= 8 && atomType <= 10)) {
2154 /* Anisotropic thermal parameters */
2155 MoleculeSetAniso(mp, atomIndex, atomType, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[5], fbuf[4], NULL);
2163 MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
2165 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2168 mp->nframes = -1; /* Should be recalculated later */
2171 Panic("low memory while reading structure file %s", fname);
2172 return -1; /* not reached */
2176 MoleculeLoadShelxFile(Molecule *mp, const char *fname, char **errbuf)
2184 int currentResSeq = 0;
2185 char currentResName[6];
2192 char (*sfacs)[4] = NULL;
2197 currentResName[0] = 0;
2198 fp = fopen(fname, "rb");
2200 s_append_asprintf(errbuf, "Cannot open file");
2204 tr[0] = tr[4] = tr[8] = 1;
2205 tr[1] = tr[2] = tr[3] = tr[5] = tr[6] = tr[7] = tr[9] = tr[10] = tr[11] = 0;
2206 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), 0, tr) == 0)
2208 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2209 if (strncmp(buf, "CELL", 4) == 0) {
2211 sscanf(buf + 4, " %f %f %f %f %f %f %f", fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2212 MoleculeSetCell(mp, fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], fbuf[6], 0);
2214 } else if (strncmp(buf, "SFAC", 4) == 0) {
2216 for (p1 = strtok_r(buf + 4, " ", &p2); p1 != NULL; p1 = strtok_r(NULL, " ", &p2)) {
2217 char *pp = (char *)AssignArray(&sfacs, &nsfacs, 4, nsfacs, NULL);
2224 } else if (strncmp(buf, "LATT", 4) == 0) {
2225 sscanf(buf + 4, " %d", &latticeType);
2227 } else if (strncmp(buf, "SYMM", 4) == 0) {
2228 char *symops[3], *brks;
2229 memset(tr, 0, sizeof(Transform));
2230 // ReadFormat(buf + 4, "I1", ibuf);
2232 symops[0] = strtok_r(buf + 4, ",", &brks);
2233 symops[1] = strtok_r(NULL, ",", &brks);
2234 symops[2] = strtok_r(NULL, ",", &brks);
2235 if (sMoleculeSymopStringsToTransform(symops, tr)) {
2236 s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2239 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2242 } else if (strncmp(buf, "RESI", 4) == 0) {
2243 for (p1 = buf + 4; isspace(*p1); p1++);
2245 for (p2 = p1 + 1; isalnum(*p2); p2++);
2247 strncpy(currentResName, p1, 4);
2248 currentResName[4] = 0;
2250 } else currentResName[0] = 0;
2251 sscanf(buf + 4, " %d", ¤tResSeq);
2254 /* Atom name: [A-Za-z]{1,2}[0-9]* */
2255 for (p1 = buf; p1 < buf + 2 && (isalpha(*p1) && *p1 != '_'); p1++);
2257 while (isdigit(*p1))
2260 if (p1 > buf && p1 <= buf + 4 && isspace(*p1)) {
2264 int atomIndex = mp->natoms;
2265 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2266 memset(ap, 0, gSizeOfAtomRecord);
2267 strncpy(ap->aname, buf, 4);
2268 n = sscanf(p1, " %d %f %f %f %f %f %f %2s", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, cont);
2269 if (n == 8 && strcmp(cont, "=") == 0) {
2270 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2271 s_append_asprintf(errbuf, "line %d: unexpected end of file within the atom cards", lineNumber);
2274 sscanf(buf, " %f %f %f %f", fbuf+6, fbuf+7, fbuf+8, fbuf+9);
2276 } else n = 5; /* Iso */
2280 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2281 ap->occupancy = fbuf[3];
2282 if (ap->aname[0] != 'Q' && ibuf[0] >= 1 && ibuf[0] <= nsfacs) {
2283 strncpy(ap->element, sfacs[ibuf[0] - 1], 2);
2285 /* sAtomSetElement(ap, -1, sfacs[ibuf[0] - 1]); */
2286 /* strncpy(ap->element, sfacs[ibuf[0] - 1], 4);
2287 ap->atomicNumber = ElementToInt(ap->element); */
2289 sAtomSetElement(ap, -1, ap->name); */
2290 /* ap->atomicNumber = AtomNameToElement(ap->name);
2291 ElementToString(ap->atomicNumber, ap->element); */
2294 if (n == 10 || fbuf[4] >= 0.0) {
2296 /* Read in the standard deviations */
2297 ReadLine(buf, sizeof buf, fp, &lineNumber);
2298 for (i = 0; i < 9; i++) {
2302 dbuf[i] = strtod(buf + j, NULL);
2305 ap->sigma.x = dbuf[0];
2306 ap->sigma.y = dbuf[1];
2307 ap->sigma.z = dbuf[2];
2310 ap->tempFactor = fbuf[4] * 78.9568352087147; /* 8*pi*pi */
2312 MoleculeSetAniso(mp, atomIndex, 8, fbuf[4], fbuf[5], fbuf[6], fbuf[9], fbuf[7], fbuf[8], dbuf);
2313 ap->resSeq = currentResSeq;
2314 strncpy(ap->resName, currentResName, 4);
2321 /* Add symmetry operations according to the lattice type */
2322 switch (latticeType < 0 ? -latticeType : latticeType) {
2323 static Transform tr_i = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0.5};
2324 static Transform tr_c = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0};
2325 static Transform tr_a = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.5};
2326 static Transform tr_b = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5};
2327 static Transform tr_r1 = {0, 1, 0, -1, -1, 0, 0, 0, 1, 0, 0, 0};
2328 static Transform tr_r2 = {-1, -1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0};
2332 sMoleculeGenerateSymopWithTransform(mp, tr_i, 0);
2334 case 3: /* Rhombohedral obverse on hexagonal axes */
2336 sMoleculeGenerateSymopWithTransform(mp, tr_r1, n);
2337 sMoleculeGenerateSymopWithTransform(mp, tr_r2, n);
2341 sMoleculeGenerateSymopWithTransform(mp, tr_a, n);
2342 sMoleculeGenerateSymopWithTransform(mp, tr_b, n);
2343 sMoleculeGenerateSymopWithTransform(mp, tr_c, n);
2346 sMoleculeGenerateSymopWithTransform(mp, tr_a, 0);
2349 sMoleculeGenerateSymopWithTransform(mp, tr_b, 0);
2352 sMoleculeGenerateSymopWithTransform(mp, tr_c, 0);
2356 if (latticeType > 0) {
2357 static Transform tr_inv = {-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0};
2358 sMoleculeGenerateSymopWithTransform(mp, tr_inv, 0);
2361 MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
2363 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2366 mp->nframes = -1; /* Should be recalculated later */
2369 Panic("low memory while reading structure file %s", fname);
2370 return -1; /* not reached */
2373 /* Add one gaussian orbital shell information (not undoable) */
2375 MoleculeAddGaussianOrbitalShell(Molecule *mol, Int sym, Int nprims, Int a_idx)
2380 return -1; /* Molecule is empty */
2383 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2385 return -2; /* Low memory */
2387 shellp = AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), bset->nshells, NULL);
2389 return -2; /* Low memory */
2391 case 0: shellp->sym = kGTOType_S; shellp->ncomp = 1; break;
2392 case 1: shellp->sym = kGTOType_P; shellp->ncomp = 3; break;
2393 case -1: shellp->sym = kGTOType_SP; shellp->ncomp = 4; break;
2394 case 2: shellp->sym = kGTOType_D; shellp->ncomp = 6; break;
2395 case -2: shellp->sym = kGTOType_D5; shellp->ncomp = 5; break;
2396 case 3: shellp->sym = kGTOType_F; shellp->ncomp = 10; break;
2397 case -3: shellp->sym = kGTOType_F7; shellp->ncomp = 7; break;
2398 case 4: shellp->sym = kGTOType_G; shellp->ncomp = 15; break;
2399 case -4: shellp->sym = kGTOType_G9; shellp->ncomp = 9; break;
2401 return -3; /* Unsupported shell type */
2403 shellp->nprim = nprims;
2404 shellp->a_idx = a_idx;
2405 if (bset->shells < shellp) {
2406 shellp->m_idx = shellp[-1].m_idx + shellp[-1].ncomp;
2407 shellp->p_idx = shellp[-1].p_idx + shellp[-1].nprim;
2415 /* Add a set of gaussian primitive coefficients (not undoable) */
2417 MoleculeAddGaussianPrimitiveCoefficients(Molecule *mol, Double exponent, Double contraction, Double contraction_sp)
2422 return -1; /* Molecule is empty */
2425 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2427 return -2; /* Low memory */
2429 primp = AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), bset->npriminfos, NULL);
2431 return -2; /* Low memory */
2432 primp->A = exponent;
2433 primp->C = contraction;
2434 primp->Csp = contraction_sp;
2438 /* Set MO coefficients for idx-th MO */
2440 MoleculeSetMOCoefficients(Molecule *mol, Int idx, Double energy, Int ncomps, Double *coeffs)
2445 return -1; /* Molecule is empty */
2448 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2450 return -2; /* Low memory */
2452 if (bset->nmos == 0) {
2453 if (bset->nshells > 0) {
2454 /* Shell info is already set: calculate the number of MOs from there */
2455 for (i = n = 0; i < bset->nshells; i++)
2456 n += bset->shells[i].ncomp;
2458 } else if (ncomps > 0) {
2459 bset->ncomps = ncomps;
2461 if (bset->rflag == 0)
2462 bset->nmos = bset->ncomps * 2;
2464 bset->nmos = bset->ncomps;
2465 if (bset->nmos <= 0)
2466 return -3; /* Bad or inconsistent number of MOs */
2467 bset->mo = (Double *)calloc(sizeof(Double), bset->nmos * bset->ncomps);
2468 bset->moenergies = (Double *)calloc(sizeof(Double), bset->nmos);
2469 if (bset->mo == NULL || bset->moenergies == NULL) {
2470 if (bset->mo != NULL)
2472 if (bset->moenergies != NULL)
2473 free(bset->moenergies);
2475 bset->moenergies = NULL;
2477 return -2; /* Low memory */
2480 if (idx < 0 || idx >= bset->nmos)
2481 return -4; /* Bad MO index */
2482 if (energy != -1000000)
2483 bset->moenergies[idx] = energy;
2484 if (ncomps < bset->ncomps)
2485 return -5; /* Insufficient number of data provided */
2486 memmove(bset->mo + (idx * bset->ncomps), coeffs, sizeof(Double) * bset->ncomps);
2487 if (bset->cns != NULL) {
2488 /* Clear the cached values */
2496 /* Allocate BasisSet record. rflag: UHF, 0; RHF, 1; ROHF, 2
2497 ne_alpha: number of alpha electrons, ne_beta: number of beta electrons
2498 The natoms and pos are copied from mol. */
2500 MoleculeAllocateBasisSetRecord(Molecule *mol, Int rflag, Int ne_alpha, Int ne_beta)
2505 if (mol == NULL || mol->natoms == 0)
2506 return -1; /* Molecule is empty */
2509 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2511 return -2; /* Low memory */
2513 if (bset->pos != NULL) {
2517 bset->natoms = mol->natoms;
2518 bset->pos = (Vector *)calloc(sizeof(Vector), bset->natoms);
2519 if (bset->pos == NULL)
2520 return -2; /* Low memory */
2521 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
2522 bset->pos[i].x = ap->r.x * kAngstrom2Bohr;
2523 bset->pos[i].y = ap->r.y * kAngstrom2Bohr;
2524 bset->pos[i].z = ap->r.z * kAngstrom2Bohr;
2526 bset->ne_alpha = ne_alpha;
2527 bset->ne_beta = ne_beta;
2528 bset->rflag = rflag;
2533 sSeparateTokens(char *inString, char **outPtr, int size)
2537 for (i = 0; i < size; i++) {
2538 p = strtok((i == 0 ? inString : NULL), " \r\n");
2549 sReadNumberArray(void *basep, Int *countp, Int size, Int num, FILE *fp, int *lnp)
2553 *((void **)basep) = NULL;
2555 if (AssignArray(basep, countp, size, num - 1, NULL) == NULL)
2556 return 4; /* Out of memory */
2558 while (ReadLine(buf, sizeof buf, fp, lnp) > 0) {
2559 char *tokens[16], *p;
2560 sSeparateTokens(buf, tokens, 16);
2561 for (i = 0; i < 16; i++) {
2562 if (tokens[i] == NULL)
2564 if (size == sizeof(Int)) {
2565 (*((Int **)basep))[n] = strtol(tokens[i], &p, 0);
2566 } else if (size == sizeof(Double)) {
2567 (*((Double **)basep))[n] = strtod(tokens[i], &p);
2568 } else return -1; /* Internal error */
2569 if (tokens[i] == p || *p != 0)
2570 return 1; /* Non-digit character */
2572 if (i < 15 && tokens[i + 1] != NULL)
2573 return 2; /* Too many data */
2574 return 0; /* All data are successfully read */
2578 return 3; /* Unexpected EOF */
2582 sSetupGaussianCoefficients(BasisSet *bset)
2589 /* Cache the contraction coefficients for efficient calculation */
2590 /* Sum up the number of components for all primitives */
2591 for (i = k = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2593 k += sp->nprim * sp->ncomp;
2595 /* Allocate memory for the cached values */
2596 if (AssignArray(&bset->cns, &bset->ncns, sizeof(Double), k - 1, NULL) == NULL)
2598 /* Iterate over all primitives */
2600 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2601 for (j = 0, pp = bset->priminfos + sp->p_idx; j < sp->nprim; j++, pp++) {
2604 // (8 alpha^3/pi^3)^0.25 exp(-alpha r^2)
2605 *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2608 // (128 alpha^5/pi^3)^0.25 [x|y|z]exp(-alpha r^2)
2609 d = pp->C * pow(pp->A, 1.25) * 1.425410941;
2615 *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2616 d = pp->Csp * pow(pp->A, 1.25) * 1.425410941;
2622 // xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2)
2623 // xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2624 d = pp->C * pow(pp->A, 1.75);
2625 dp[0] = dp[1] = dp[2] = d * 1.645922781;
2626 dp[3] = dp[4] = dp[5] = d * 2.850821881;
2630 // 3zz-rr: (128 alpha^7/9pi^3)^0.25 (3zz-rr)exp(-alpha r^2)
2631 // xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2632 // xx-yy: (128 alpha^7/pi^3)^0.25 (xx-yy)exp(-alpha r^2)
2633 d = pp->C * pow(pp->A, 1.75);
2634 dp[0] = d * 0.822961390;
2635 dp[1] = dp[2] = dp[4] = d * 2.850821881;
2636 dp[3] = d * 1.425410941;
2639 /* TODO: Support F/F7 and G/G9 type orbitals */
2647 MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char **errbuf)
2652 int natoms, nbasis, i, j, k, n, mxbond, retval, ncomps, nprims, nelec;
2666 bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2670 fp = fopen(fname, "rb");
2672 s_append_asprintf(errbuf, "Cannot open file");
2676 natoms = nbasis = -1;
2684 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2687 if (lineNumber == 2) {
2690 bset->rflag = 0; /* UHF */
2691 else if (buf[11] == 'O')
2692 bset->rflag = 2; /* ROHF */
2693 else bset->rflag = 1; /* RHF */
2696 while (p > buf && *p == ' ')
2699 sSeparateTokens(buf + 42, tokens, 16);
2700 if (strcmp(buf, "Number of atoms") == 0) {
2701 if (tokens[1] == NULL || (natoms = atoi(tokens[1])) <= 0) {
2702 s_append_asprintf(errbuf, "Line %d: strange number of atoms: %s", lineNumber, tokens[1]);
2706 /* Allocate atom records (all are empty for now) */
2707 AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms - 1, NULL);
2708 /* Also allocate atom position array for MO calculations */
2709 AssignArray(&bset->pos, &bset->natoms, sizeof(Vector), natoms - 1, NULL);
2710 /* Also allocate nuclear charge array */
2711 bset->nuccharges = (Double *)calloc(sizeof(Double), natoms);
2712 } else if (strcmp(buf, "Number of electrons") == 0) {
2713 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2714 s_append_asprintf(errbuf, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
2719 } else if (strcmp(buf, "Number of alpha electrons") == 0) {
2720 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2721 s_append_asprintf(errbuf, "Line %d: strange number of alpha electrons: %s", lineNumber, tokens[1]);
2726 } else if (strcmp(buf, "Number of beta electrons") == 0) {
2727 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2728 s_append_asprintf(errbuf, "Line %d: strange number of beta electrons: %s", lineNumber, tokens[1]);
2733 if (bset->ne_alpha + bset->ne_beta != nelec) {
2734 s_append_asprintf(errbuf, "Line %d: sum of alpha (%d) and beta (%d) electrons does not match the number of electrons (%d)", lineNumber, (int)bset->ne_alpha, (int)bset->ne_beta, (int)nelec);
2738 } else if (strcmp(buf, "Number of basis functions") == 0) {
2739 if (tokens[1] == NULL || (nbasis = atoi(tokens[1])) <= 0) {
2740 s_append_asprintf(errbuf, "Line %d: strange number of basis functions: %s", lineNumber, tokens[1]);
2744 } else if (strcmp(buf, "Atomic numbers") == 0) {
2745 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2746 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2750 if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms, fp, &lineNumber) != 0) {
2751 s_append_asprintf(errbuf, "Line %d: cannot read atomic numbers", lineNumber);
2755 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2756 ap->atomicNumber = iary[i];
2757 bset->nuccharges[i] = iary[i];
2758 ElementToString(ap->atomicNumber, ap->element);
2759 memmove(ap->aname, ap->element, 4);
2760 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
2765 } else if (strcmp(buf, "Nuclear charges") == 0) {
2766 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2767 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2771 if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms, fp, &lineNumber) != 0) {
2772 s_append_asprintf(errbuf, "Line %d: cannot read nuclear charges", lineNumber);
2776 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2777 bset->nuccharges[i] = dary[i];
2781 } else if (strcmp(buf, "Current cartesian coordinates") == 0) {
2782 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * 3) {
2783 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of cartesian coordinates: %s", lineNumber, tokens[2]);
2787 if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms * 3, fp, &lineNumber) != 0) {
2788 s_append_asprintf(errbuf, "Line %d: cannot read cartesian coordinates", lineNumber);
2792 for (i = 0, ap = mp->atoms, vp = bset->pos; i < natoms; i++, ap = ATOM_NEXT(ap), vp++) {
2793 vp->x = dary[i * 3];
2794 vp->y = dary[i * 3 + 1];
2795 vp->z = dary[i * 3 + 2];
2796 ap->r.x = vp->x * kBohr2Angstrom;
2797 ap->r.y = vp->y * kBohr2Angstrom;
2798 ap->r.z = vp->z * kBohr2Angstrom;
2802 } else if (strcmp(buf, "MxBond") == 0) {
2803 if (tokens[1] == NULL || (mxbond = atoi(tokens[1])) <= 0) {
2804 s_append_asprintf(errbuf, "Line %d: strange number of bonds per atom: %s", lineNumber, tokens[1]);
2808 } else if (strcmp(buf, "IBond") == 0) {
2810 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * mxbond) {
2811 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of bonds: %s", lineNumber, tokens[2]);
2815 if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms * mxbond, fp, &lineNumber) != 0) {
2816 s_append_asprintf(errbuf, "Line %d: cannot read bond information", lineNumber);
2820 bonds = (Int *)malloc(sizeof(Int) * (mxbond * 2 + 1));
2821 for (i = 0; i < natoms; i++) {
2822 for (j = k = 0; j < mxbond; j++) {
2823 n = iary[i * mxbond + j] - 1;
2825 /* Connect atom i and atom n */
2831 bonds[k] = kInvalidIndex;
2832 MoleculeAddBonds(mp, k / 2, bonds, NULL, 1);
2838 } else if (strcmp(buf, "Shell types") == 0) {
2839 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0) {
2840 s_append_asprintf(errbuf, "Line %d: wrong number of shell types: %s", lineNumber, tokens[2]);
2844 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2845 s_append_asprintf(errbuf, "Line %d: cannot read shell types", lineNumber);
2849 /* Allocate ShellInfo table and store shell type information */
2850 AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), nary - 1, NULL);
2851 for (i = n = 0, sp = bset->shells; i < nary; i++, sp++) {
2853 case 0: sp->sym = kGTOType_S; sp->ncomp = 1; break;
2854 case 1: sp->sym = kGTOType_P; sp->ncomp = 3; break;
2855 case -1: sp->sym = kGTOType_SP; sp->ncomp = 4; break;
2856 case 2: sp->sym = kGTOType_D; sp->ncomp = 6; break;
2857 case -2: sp->sym = kGTOType_D5; sp->ncomp = 5; break;
2858 case 3: sp->sym = kGTOType_F; sp->ncomp = 10; break;
2859 case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break;
2860 case 4: sp->sym = kGTOType_G; sp->ncomp = 15; break;
2861 case -4: sp->sym = kGTOType_G9; sp->ncomp = 9; break;
2863 s_append_asprintf(errbuf, "Line %d: unsupported shell type %d", lineNumber, iary[i]);
2870 bset->ncomps = ncomps = n;
2873 } else if (strcmp(buf, "Number of primitives per shell") == 0) {
2874 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2875 s_append_asprintf(errbuf, "Line %d: wrong size of the primitive table: %s", lineNumber, tokens[2]);
2879 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2880 s_append_asprintf(errbuf, "Line %d: cannot read primitive table", lineNumber);
2884 for (i = n = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2885 sp->nprim = iary[i];
2892 } else if (strcmp(buf, "Shell to atom map") == 0) {
2893 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2894 s_append_asprintf(errbuf, "Line %d: wrong size of the shell-to-atom map: %s", lineNumber, tokens[2]);
2898 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2899 s_append_asprintf(errbuf, "Line %d: cannot read shell-to-atom table", lineNumber);
2903 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2904 sp->a_idx = iary[i] - 1;
2908 } else if (strcmp(buf, "Primitive exponents") == 0) {
2909 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nprims) {
2910 s_append_asprintf(errbuf, "Line %d: wrong number of primitive exponents: %s", lineNumber, tokens[2]);
2914 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2915 s_append_asprintf(errbuf, "Line %d: cannot read primitive exponents", lineNumber);
2919 /* Allocate PrimInfo table */
2920 AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), nprims - 1, NULL);
2921 for (i = 0, pp = bset->priminfos; i < nprims; i++, pp++) {
2926 } else if (strcmp(buf, "Contraction coefficients") == 0) {
2927 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2928 s_append_asprintf(errbuf, "Line %d: wrong number of contraction coefficients: %s", lineNumber, tokens[2]);
2932 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2933 s_append_asprintf(errbuf, "Line %d: cannot read contraction coefficients", lineNumber);
2937 for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2942 } else if (strcmp(buf, "P(S=P) Contraction coefficients") == 0) {
2943 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2944 s_append_asprintf(errbuf, "Line %d: wrong number of P(S=P) contraction coefficients: %s", lineNumber, tokens[2]);
2948 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2949 s_append_asprintf(errbuf, "Line %d: cannot read P(S=P) contraction coefficients", lineNumber);
2953 for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2958 } else if (strcmp(buf, "Alpha Orbital Energies") == 0) {
2959 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2960 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha orbitals: %s", lineNumber, tokens[2]);
2964 if (sReadNumberArray(&bset->moenergies, &bset->nmos, sizeof(Double), i, fp, &lineNumber) != 0) {
2965 s_append_asprintf(errbuf, "Line %d: cannot read alpha orbital energies", lineNumber);
2969 } else if (strcmp(buf, "Alpha MO coefficients") == 0) {
2970 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2971 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha MO coefficients: %s", lineNumber, tokens[2]);
2975 if (sReadNumberArray(&bset->mo, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2976 s_append_asprintf(errbuf, "Line %d: cannot read MO coefficients", lineNumber);
2980 } else if (strcmp(buf, "Beta Orbital Energies") == 0) {
2981 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2982 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta orbitals: %s", lineNumber, tokens[2]);
2986 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2987 s_append_asprintf(errbuf, "Line %d: cannot read beta orbital energies", lineNumber);
2991 bset->moenergies = (Double *)realloc(bset->moenergies, sizeof(Double) * 2 * ncomps);
2992 bset->nmos = ncomps * 2;
2993 bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);
2994 memmove(bset->moenergies + ncomps, dary, sizeof(Double) * ncomps);
2995 memset(bset->mo + ncomps * ncomps, 0, sizeof(Double) * ncomps * ncomps);
2998 } else if (strcmp(buf, "Beta MO coefficients") == 0) {
2999 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
3000 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta MO coefficients: %s", lineNumber, tokens[2]);
3004 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3005 s_append_asprintf(errbuf, "Line %d: cannot read alpha MO coefficients", lineNumber);
3009 bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps); /* Should be unnecessary, just in case */
3010 memmove(bset->mo + ncomps * ncomps, dary, sizeof(Double) * ncomps * ncomps);
3013 } else if (strcmp(buf, "Total SCF Density") == 0) {
3014 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * (ncomps + 1) / 2) {
3015 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of SCF densities: %s", lineNumber, tokens[2]);
3019 if (sReadNumberArray(&bset->scfdensities, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3020 s_append_asprintf(errbuf, "Line %d: cannot read SCF densities", lineNumber);
3026 if (mp->natoms == 0) {
3027 s_append_asprintf(errbuf, "Atom information is missing");
3031 if (bset->shells == NULL || bset->priminfos == NULL) {
3032 s_append_asprintf(errbuf, "Gaussian primitive information is missing");
3036 if (bset->mo == NULL) {
3037 s_append_asprintf(errbuf, "MO coefficients were not found");
3041 if (sSetupGaussianCoefficients(bset) != 0) {
3042 s_append_asprintf(errbuf, "Internal error during setup MO calculation");
3055 if (mp->bset != NULL) {
3056 BasisSetRelease(mp->bset);
3062 Panic("low memory while reading fchk file %s", fname);
3063 return -1; /* not reached */
3067 MoleculeLoadGamessDatFile(Molecule *mol, const char *fname, char **errbuf)
3072 int lineNumber, i, j, k, len, natoms = 0;
3079 Vector *vbuf = NULL;
3081 int optimizing = 0, status = 0;
3085 mol = MoleculeNew();
3087 if (mol->natoms == 0)
3090 fp = fopen(fname, "rb");
3092 s_append_asprintf(errbuf, "Cannot open file");
3096 /* ESP is cleared (not undoable!) */
3097 if (mol->elpots != NULL) {
3104 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3107 if (strncmp(buf, " $DATA", 6) == 0) {
3108 /* Initial geometry */
3110 vbuf = (Vector *)calloc(sizeof(Vector), mol->natoms);
3113 ReadLine(buf, sizeof buf, fp, &lineNumber); /* Title */
3114 ReadLine(buf, sizeof buf, fp, &lineNumber); /* Symmetry */
3115 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3116 if (strncmp(buf, " $END", 5) == 0)
3118 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3119 s_append_asprintf(errbuf, "Line %d: bad format in $DATA section", lineNumber);
3125 memset(&a, 0, sizeof(a));
3126 strncpy(a.aname, sval, 4);
3130 a.atomicNumber = (Int)dval[0];
3131 strncpy(a.element, ElementToString(a.atomicNumber, sval), 3);
3132 a.type = AtomTypeEncodeToUInt(a.element);
3133 a.weight = WeightForAtomicNumber(a.atomicNumber);
3134 MoleculeCreateAnAtom(mol, &a, mol->natoms);
3137 if (i >= mol->natoms) {
3138 s_append_asprintf(errbuf, "Line %d: too many atoms", lineNumber);
3142 if ((ap = ATOM_AT_INDEX(mol->atoms, i))->atomicNumber != dval[0]) {
3143 s_append_asprintf(errbuf, "Line %d: atomic number does not match", lineNumber);
3147 vbuf[i].x = dval[1];
3148 vbuf[i].y = dval[2];
3149 vbuf[i].z = dval[3];
3151 /* Skip until a blank line is found */
3152 /* 2013.6.11. Line including "PM3" is also recognized as the end of atom */
3153 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3154 for (j = 0; buf[j] == ' '; j++);
3155 if (buf[j] == '\n' || strncmp(buf + j, "PM3", 3) == 0)
3162 /* Set atom positions */
3164 if (natoms < mol->natoms) {
3165 s_append_asprintf(errbuf, "Line %d: too few atoms", lineNumber);
3169 ig = IntGroupNewWithPoints(0, natoms, -1);
3170 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, natoms, vbuf);
3171 IntGroupRelease(ig);
3174 vbuf = (Vector *)calloc(sizeof(Vector), natoms);
3175 nframes = MoleculeGetNumberOfFrames(mol);
3179 } else if (strstr(buf, "DATA FROM NSERCH") != NULL || (strstr(buf, "RESULTS FROM SUCCESSFUL") != NULL && (n1 = 1))) {
3180 /* Skip until the separator line is read (three or four lines) */
3184 s_append_asprintf(errbuf, "Line %d: the separator line at the top of the coordinates is not found: bad format?", lineNumber);
3188 ReadLine(buf, sizeof buf, fp, &lineNumber);
3189 } while (strstr(buf, "----------------------------") == NULL);
3190 for (i = 0; i < natoms; i++) {
3191 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3192 s_append_asprintf(errbuf, "Unexpected end of file in reading NSERCH data");
3196 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3197 s_append_asprintf(errbuf, "Line %d: bad format in NSERCH coordinate data", lineNumber);
3201 vbuf[i].x = dval[1];
3202 vbuf[i].y = dval[2];
3203 vbuf[i].z = dval[3];
3205 ig = IntGroupNewWithPoints(nframes, 1, -1);
3206 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, natoms, vbuf, 0, NULL);
3207 IntGroupRelease(ig);
3210 optimizing = 1; /* Flag to skip reading the VEC group */
3214 } else if (strstr(buf, "E(UHF)") != NULL || (strstr(buf, "E(RHF)") != NULL && (n1 = 1)) || (strstr(buf, "E(ROHF)") != NULL && (n1 = 2))) {
3215 if (mol->bset == NULL) {
3216 i = MoleculeAllocateBasisSetRecord(mol, n1, 0, 0);
3218 s_append_asprintf(errbuf, "Line %d: cannot allocate basis set internal buffer", lineNumber);
3223 } else if (strncmp(buf, " $VEC", 5) == 0) {
3225 /* Read the vec group */
3226 if (mol->bset == NULL || mol->bset->ncomps == 0)
3227 continue; /* Just ignore */
3229 continue; /* Ignore VEC group during optimization */
3230 coeffs = (Double *)calloc(sizeof(Double), mol->bset->ncomps);
3231 if (coeffs == NULL) {
3232 s_append_asprintf(errbuf, "Line %d: low memory during $VEC", lineNumber);
3237 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3239 if (strncmp(buf, " $END", 5) == 0)
3241 while ((j = 5 + (k % 5) * 15) <= len && buf[j] != 0 && buf[j] != '\n') {
3242 strncpy(sval, buf + j, 15);
3244 coeffs[k] = strtod(sval, NULL);
3249 if (k < mol->bset->ncomps)
3251 j = MoleculeSetMOCoefficients(mol, i, -1000000, k, coeffs);
3253 s_append_asprintf(errbuf, "Line %d: cannot set coefficients for MO %d", lineNumber, i + 1);
3264 } else if ((strstr(buf, "ELECTRIC POTENTIAL") != NULL || strstr(buf, "ELECTROSTATIC POTENTIAL") != NULL) && strstr(buf, "ELPOTT") != NULL) {
3266 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3268 if (strstr(buf, "TOTAL NUMBER OF GRID POINTS") != NULL)
3270 if (sscanf(buf, "%d %lf %lf %lf %lf", &ival[0], &dval[0], &dval[1], &dval[2], &dval[3]) < 5)
3272 ep = AssignArray(&mol->elpots, &mol->nelpots, sizeof(Elpot), i, NULL);
3273 ep->pos.x = dval[0];
3274 ep->pos.y = dval[1];
3275 ep->pos.z = dval[2];
3280 goto redo; /* This section has no end line, so the last line should be processed again */
3281 else break; /* End of file encountered or interrupted */
3282 } /* TODO: read MOLPLT info if present */
3285 s_append_asprintf(errbuf, "User interrupt at line %d", lineNumber);
3291 if (mol->natoms > 0)
3292 retval = 0; /* Return the partially constructed molecule */
3293 if (newmol && mol->nbonds == 0) {
3296 MoleculeGuessBonds(mol, 1.2, &nbonds, &bonds);
3298 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
3306 MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
3309 if (ftype == NULL || *ftype == 0) {
3311 cp = strrchr(fname, '.');
3315 cp = guessMoleculeType(fname);
3316 if (strcmp(cp, "???") != 0)
3320 if (strcasecmp(ftype, "pdb") == 0) {
3321 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3324 /* Try all formats once again */
3325 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3331 MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char **errbuf)
3337 int i, j, new_unit, retval;
3345 fp = fopen(fname, "rb");
3347 s_append_asprintf(errbuf, "Cannot open file");
3350 /* flockfile(fp); */
3351 if (mp->natoms == 0)
3354 /* Allocate buffer for undo-capable modification */
3355 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
3356 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3357 /* Retain current position if the atom info is missing in the input file */
3363 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3364 if (strncmp(buf, "END", 3) == 0)
3366 if (strncmp(buf, "HETATM", 6) == 0 || strncmp(buf, "ATOM", 4) == 0) {
3368 Int serial, intCharge, resSeq;
3371 char segName[5], resName[4], atomName[5], resSeqStr[5], atomType[3], element[3], occStr[6];
3373 memset(&w, 0, sizeof(w));
3374 ReadFormat(buf, "x6 I5 x1 S4 x1 S3 x1 x1 S4 x1 x3 F8 F8 F8 S6 F6 x6 S4 S2 I2",
3375 &w.serial, w.atomName, w.resName, w.resSeqStr, &w.r.x, &w.r.y, &w.r.z,
3376 w.occStr, &w.temp, w.segName, w.element, &w.intCharge);
3377 if (w.atomName[0] == 0) {
3378 continue; /* Atom name is empty */
3380 /* A workaround for residue number >= 10000 (XPLOR style) */
3381 if (w.resSeqStr[0] >= 'A' && w.resSeqStr[0] <= 'Z') {
3382 w.resSeq = (w.resSeqStr[0] - 'A' + 10) * 1000 + atoi(w.resSeqStr + 1);
3384 w.resSeq = atoi(w.resSeqStr);
3386 if (w.element[0] == 0) {
3387 /* $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl */
3388 for (p = w.atomName; *p != 0; p++) {
3389 if (isalpha(*p) && *p != '_') {
3390 w.element[0] = toupper(*p);
3391 if (isalpha(p[1]) && p[1] != '_') {
3392 w.element[1] = toupper(p[1]);
3401 if (w.occStr[0] == 0)
3404 w.occ = atof(w.occStr);
3405 if (w.serial <= 0) {
3406 s_append_asprintf(errbuf, "line %d: non-positive atom number %d", lineNumber, w.serial);
3410 w.serial--; /* The internal atom number is 0-based */
3411 if (w.serial >= mp->natoms) {
3413 /* Create a new atom entry */
3414 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, w.serial, NULL);
3416 s_append_asprintf(errbuf, "line %d: the atom number %d does not exist in the structure file", lineNumber, w.serial+1);
3422 ap = ATOM_AT_INDEX(mp->atoms, w.serial);
3424 ap->occupancy = w.occ;
3425 ap->tempFactor = w.temp;
3426 if (w.segName[0] == 0)
3427 strncpy(w.segName, "MAIN", 4);
3428 strncpy(ap->segName, w.segName, 4);
3429 ap->resSeq = w.resSeq;
3430 strncpy(ap->resName, w.resName, 4);
3431 strncpy(ap->aname, w.atomName, 4);
3432 strncpy(ap->element, w.element, 2);
3434 ap->atomicNumber = ElementToInt(ap->element);
3435 ap->type = AtomTypeEncodeToUInt(ap->element);
3436 ap->weight = WeightForAtomicNumber(ap->atomicNumber);
3437 ap->intCharge = w.intCharge;
3438 if (ap->resSeq > 0) {
3439 if (ap->resSeq < mp->nresidues) {
3440 /* Update the resName according to residues[] */
3441 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
3443 /* Register the resName to residues[] */
3444 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, w.resName);
3448 strcpy(ap->resName, "XXX");
3449 if (mp->nresidues == 0)
3450 AssignArray(&mp->residues, &mp->nresidues, 4, 0, ap->resName);
3452 i = ElementToInt(ap->element);
3454 ap->weight = gElementParameters[i].weight;
3456 /* Not a new unit: only the atom position is updated */
3460 } else if (strncmp(buf, "CONECT", 6) == 0 && new_unit) {
3461 i = ReadFormat(buf, "x6 I5I5I5I5I5I5I5I5I5I5I5I5",
3462 ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
3463 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7,
3464 ibuf + 8, ibuf + 9, ibuf + 10, ibuf + 11);
3468 for (j = 0; j < i; j++) {
3469 if (ibuf[j] < 0 || ibuf[j] > mp->natoms) {
3470 s_append_asprintf(errbuf, "line %d: The CONECT record contains non-existent atom %d", lineNumber, ibuf[j]);
3473 } else if (ibuf[j] == 0)
3479 for (j = 1, bi = 0; j < i; j++) {
3480 if (ibuf[0] < ibuf[j]) {
3481 if (MoleculeLookupBond(mp, ibuf[0], ibuf[j]) >= 0) {
3482 s_append_asprintf(errbuf, "line %d: warning: duplicate bond %d-%d\n", lineNumber, ibuf[0], ibuf[j]);
3484 bbuf[bi * 2] = ibuf[0] - 1;
3485 bbuf[bi * 2 + 1] = ibuf[j] - 1;
3493 retval = MoleculeAddBonds(mp, bi, bbuf, NULL, 1);
3495 s_append_asprintf(errbuf, "line %d: bad bond specification", lineNumber);
3502 /* funlockfile(fp); */
3505 /* Renumber atoms if some atom number is unoccupied */
3506 int *old2new, oldidx, newidx;
3507 old2new = (int *)calloc(sizeof(int), mp->natoms);
3508 if (old2new == NULL) {
3509 s_append_asprintf(errbuf, "Out of memory");
3513 for (oldidx = newidx = 0; oldidx < mp->natoms; oldidx++) {
3514 ap = ATOM_AT_INDEX(mp->atoms, oldidx);
3515 if (ap->aname[0] != 0) {
3516 old2new[oldidx] = newidx;
3517 if (oldidx > newidx)
3518 memmove(ATOM_AT_INDEX(mp->atoms, newidx), ap, gSizeOfAtomRecord);
3522 mp->natoms = newidx;
3523 if (oldidx > newidx) {
3524 /* Renumber the connects and bonds */
3526 for (i = 0; i < mp->natoms; i++) {
3527 ap = ATOM_AT_INDEX(mp->atoms, i);
3528 cp = AtomConnectData(&ap->connect);
3529 for (j = 0; j < ap->connect.count; j++) {
3530 cp[j] = old2new[cp[j]];
3533 for (i = 0; i < mp->nbonds * 2; i++) {
3534 mp->bonds[i] = old2new[mp->bonds[i]];
3537 retval = MoleculeRebuildTablesFromConnects(mp);
3539 /* This error may not happen */
3540 s_append_asprintf(errbuf, "Cannot build angle/dihedral/improper tables");
3544 /* Undo action: delete all atoms */
3547 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3548 act = MolActionNew(gMolActionUnmergeMolecule, ig);
3549 act->frame = mp->cframe;
3550 MolActionCallback_registerUndo(mp, act);
3551 MolActionRelease(act);
3552 IntGroupRelease(ig);
3555 /* Set the new atom positions */
3556 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3557 MolActionCreateAndPerform(mp, gMolActionSetAtomPositions, ig, mp->natoms, vp);
3558 IntGroupRelease(ig);
3562 mp->nframes = -1; /* Should be recalculated later */
3564 return 1; /* No atoms */
3568 /* funlockfile(fp); */
3574 return 1; /* Maybe different format? */
3579 MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char **errbuf)
3582 SFloat32 *xp, *yp, *zp;
3585 int n, errcount = 0;
3587 if (mp == NULL || mp->natoms == 0) {
3588 s_append_asprintf(errbuf, "Molecule is empty");
3591 n = DcdOpen(fname, &dcd);
3594 case -2: s_append_asprintf(errbuf, "Cannot open file"); break;
3595 case 1: s_append_asprintf(errbuf, "Premature EOF encountered"); break;
3596 case 2: s_append_asprintf(errbuf, "Bad block length of the first section"); break;
3597 case 3: s_append_asprintf(errbuf, "\"CORD\" signature is missing"); break;
3598 case 4: s_append_asprintf(errbuf, "Bad termination of the first section"); break;
3599 case 5: s_append_asprintf(errbuf, "The title section is not correct"); break;
3600 case 6: s_append_asprintf(errbuf, "The atom number section is not correct"); break;
3601 default: s_append_asprintf(errbuf, "Read error in dcd file"); break;
3605 if (dcd.natoms == 0) {
3606 s_append_asprintf(errbuf, "No atoms were found in the dcd file");
3608 } else if (dcd.nframes == 0) {
3609 s_append_asprintf(errbuf, "No frames were found in the dcd file");
3619 vp = (Vector *)calloc(sizeof(Vector), mp->natoms * dcd.nframes);
3621 cp = (Vector *)calloc(sizeof(Vector), dcd.nframes * 4);
3623 xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3624 yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3625 zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3626 ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mp), dcd.nframes, -1);
3627 if (vp == NULL || xp == NULL || yp == NULL || zp == NULL || ig == NULL) {
3628 s_append_asprintf(errbuf, "Cannot allocate memory");
3634 if (ig) IntGroupRelease(ig);
3637 for (n = 0; n < dcd.nframes; n++) {
3640 SFloat32 dcdcell[6];
3641 if (DcdReadFrame(&dcd, n, xp, yp, zp, dcdcell)) {
3642 s_append_asprintf(errbuf, "Read error in dcd file");
3645 for (i = 0, vpp = &vp[n * mp->natoms]; i < dcd.natoms && i < mp->natoms; i++, vpp++) {
3653 /* dcdcell = {a, gamma, b, beta, alpha, c} */
3654 /* angles are described either in cosines (Charmm and NAMD > 2.5) or degrees (NAMD 2.5) */
3655 if (dcdcell[1] < -1.0 || dcdcell[1] > 1.0 || dcdcell[3] < -1.0 || dcdcell[3] > 1.0 || dcdcell[4] < -1.0 || dcdcell[4] > 1.0) {
3656 dcdcell[4] = cos(dcdcell[4] * kDeg2Rad); /* cos(alpha) */
3657 dcdcell[3] = cos(dcdcell[3] * kDeg2Rad); /* cos(beta) */
3658 dcdcell[1] = cos(dcdcell[1] * kDeg2Rad); /* cos(gamma) */
3660 /* a axis lies along the cartesian x axis */
3661 sing = sqrt(1 - dcdcell[1] * dcdcell[1]);
3662 vpp[0].x = dcdcell[0];
3665 vpp[1].x = dcdcell[2] * dcdcell[1];
3666 vpp[1].y = dcdcell[2] * sing;
3668 vpp[2].x = dcdcell[5] * dcdcell[3];
3669 vpp[2].y = dcdcell[5] * (dcdcell[4] - dcdcell[3] * dcdcell[1]) / sing;
3670 vpp[2].z = sqrt(dcdcell[5] * dcdcell[5] - vpp[2].x * vpp[2].x - vpp[2].y * vpp[2].y);
3671 vpp[3].x = vpp[3].y = vpp[3].z = 0.0;
3672 if (mp->cell == NULL) {
3673 /* Create periodicity if not present */
3674 MolActionCreateAndPerform(mp, gMolActionSetBox, &vpp[0], &vpp[1], &vpp[2], &vpp[3], 7, 0);
3678 if (MolActionCreateAndPerform(mp, gMolActionInsertFrames, ig, mp->natoms * dcd.nframes, vp, (cp == NULL ? 0 : dcd.nframes * 4), cp) != 0)
3679 s_append_asprintf(errbuf, "Cannot insert frames");
3680 mp->startStep = dcd.nstart;
3681 mp->stepsPerFrame = dcd.ninterval;
3682 mp->psPerStep = dcd.delta;
3691 IntGroupRelease(ig);
3698 MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
3709 fp = fopen(fname, "rb");
3711 s_append_asprintf(errbuf, "Cannot open file");
3717 flags[0] = flags[1] = flags[2] = 0;
3718 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3719 if (strncmp(buf, "Bounding box:", 13) == 0) {
3720 for (i = 0; i < 3; i++) {
3721 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3722 s_append_asprintf(errbuf, "line %d: missing %d component of the bounding box", lineNumber, i + 1);
3726 n = sscanf(buf, "%lf %lf %lf %d", &d[0], &d[1], &d[2], &flag);
3728 vv.x = vv.y = vv.z = 0.0;
3730 case 0: vv.x = d[0]; break;
3731 case 1: vv.y = d[0]; break;
3732 case 2: vv.z = d[0]; break;
3734 if (n == 1 || (n == 2 && d[1] != 0.0))
3741 flags[i] = (flag != 0);
3743 flags[i] = (VecLength2(vv) != 0);
3747 if (mp->cell != NULL)
3748 vv = mp->cell->origin;
3750 vv.x = vv.y = vv.z = 0.0;
3751 MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
3752 } else if (strncmp(buf, "Bounding box origin:", 20) == 0) {
3753 if (mp->cell != NULL) {
3754 v[0] = mp->cell->axes[0];
3755 v[1] = mp->cell->axes[1];
3756 v[2] = mp->cell->axes[2];
3757 memmove(flags, mp->cell->flags, 3);
3759 v[0].x = 1.0; v[0].y = v[0].z = 0.0;
3760 v[1].y = 1.0; v[1].x = v[1].z = 0.0;
3761 v[2].z = 1.0; v[2].x = v[2].y = 0.0;
3762 flags[0] = flags[1] = flags[2] = 1.0;
3764 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0 || (n = sscanf(buf, "%lf %lf %lf", &d[0], &d[1], &d[2]) < 3)) {
3765 s_append_asprintf(errbuf, "line %d: wrong format for the bounding box origin", lineNumber);
3772 MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
3784 MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
3788 if (ftype == NULL || *ftype == 0) {
3790 cp = strrchr(fname, '.');
3794 cp = guessMoleculeType(fname);
3795 if (strcmp(cp, "???") != 0)
3799 if (strcasecmp(ftype, "psf") == 0) {
3800 retval = MoleculeWriteToPsfFile(mp, fname, errbuf);
3801 } else if (strcasecmp(ftype, "pdb") == 0) {
3802 retval = MoleculeWriteToPdbFile(mp, fname, errbuf);
3803 } else if (strcasecmp(ftype, "tep") == 0) {
3804 retval = MoleculeWriteToTepFile(mp, fname, errbuf);
3806 s_append_asprintf(errbuf, "The file format should be specified");
3810 MoleculeSetPath(mp, fname);
3815 MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char **errbuf)
3818 Int i, j, k, n1, n2, n3, n_aniso, nframes, nanchors, n_uff;
3823 fp = fopen(fname, "wb");
3825 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
3830 nframes = MoleculeFlushFrames(mp);
3832 fprintf(fp, "!:atoms\n");
3833 fprintf(fp, "! idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge\n");
3834 n1 = n2 = n3 = n_aniso = nanchors = n_uff = 0;
3835 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3836 strncpy(bufs[0], ap->segName, 4);
3838 strncpy(bufs[1], ap->resName, 4);
3840 strncpy(bufs[2], ap->aname, 4);
3842 AtomTypeDecodeToString(ap->type, bufs[3]);
3844 strncpy(bufs[4], ap->element, 4);
3846 for (j = 0; j < 5; j++) {
3847 if (bufs[j][0] == 0) {
3851 for (k = 0; k < 6; k++) {
3852 if (bufs[j][k] == 0)
3854 if (bufs[j][k] > 0 && bufs[j][k] < ' ')
3858 if (SYMOP_ALIVE(ap->symop))
3860 if (ap->fix_force != 0)
3862 if (ap->mm_exclude || ap->periodic_exclude)
3864 if (ap->aniso != NULL)
3866 if (ap->anchor != NULL)
3868 if (ap->uff_type[0] != 0)
3870 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);
3875 fprintf(fp, "!:uff_type\n");
3876 fprintf(fp, "! idx uff_type\n");
3877 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3878 fprintf(fp, "%d %.5s\n", i, ap->uff_type);
3884 fprintf(fp, "!:atoms_symop\n");
3885 fprintf(fp, "! idx symop symbase\n");
3886 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3888 n = ap->symop.sym * 1000000 + ap->symop.dx * 10000 + ap->symop.dy * 100 + ap->symop.dz;
3889 fprintf(fp, "%d %d %d\n", i, n, ap->symbase);
3895 fprintf(fp, "!:atoms_fix\n");
3896 fprintf(fp, "! idx fix_force fix_pos\n");
3897 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3898 fprintf(fp, "%d %f %f %f %f\n", i, ap->fix_force, ap->fix_pos.x, ap->fix_pos.y, ap->fix_pos.z);
3904 fprintf(fp, "!:mm_exclude\n");
3905 fprintf(fp, "! idx mm_exclude periodic_exclude\n");
3906 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3907 fprintf(fp, "%d %d %d\n", i, ap->mm_exclude, ap->periodic_exclude);
3913 fprintf(fp, "!:pi_anchor\n");
3914 fprintf(fp, "! idx count; n1 weight1; n2 weight2; ...; nN weightN\n");
3915 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3917 if (ap->anchor == NULL)
3919 k = ap->anchor->connect.count;
3920 ip = AtomConnectData(&ap->anchor->connect);
3921 fprintf(fp, "%d %d\n", i, k);
3922 for (j = 0; j < k; j++) {
3923 fprintf(fp, "%d %f\n", ip[j], ap->anchor->coeffs[j]);
3934 for (i = 0; (i == n2 || i < n1); i++) {
3935 fprintf(fp, "!:positions ; frame %d\n", i);
3936 fprintf(fp, "! idx x y z [sx sy sz]\n");
3937 for (j = 0, ap = mp->atoms; j < mp->natoms; j++, ap = ATOM_NEXT(ap)) {
3940 if (i != n2 && i < ap->nframes)
3941 vp = ap->frames + i;
3944 if (ap->sigma.x != 0.0 || ap->sigma.y != 0.0 || ap->sigma.z != 0.0)
3947 fprintf(fp, "%d %.8f %.8f %.8f", j, vp->x, vp->y, vp->z);
3949 fprintf(fp, " %.8f %.8f %.8f", ap->sigma.x, ap->sigma.y, ap->sigma.z);
3956 if (mp->nbonds > 0) {
3957 fprintf(fp, "!:bonds\n");
3958 fprintf(fp, "! from1 to1 from2 to2 from3 to3 from4 to4\n");
3959 for (i = 0; i < mp->nbonds; i++) {
3960 fprintf(fp, "%d %d%c", mp->bonds[i * 2], mp->bonds[i * 2 + 1], (i % 4 == 3 || i == mp->nbonds - 1 ? '\n' : ' '));
3965 if (mp->nbondOrders > 0) {
3966 fprintf(fp, "!:bond_orders\n");
3967 fprintf(fp, "! order1 order2 order3 order4\n");
3968 for (i = 0; i < mp->nbondOrders; i++) {
3969 fprintf(fp, "%.6f%c", mp->bondOrders[i], (i % 4 == 3 || i == mp->nbondOrders - 1 ? '\n' : ' '));
3974 if (mp->nangles > 0) {
3975 fprintf(fp, "!:angles\n");
3976 fprintf(fp, "! a1 b1 c1 a2 b2 c2 a3 b3 c3\n");
3977 for (i = 0; i < mp->nangles; i++) {
3978 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' : ' '));
3983 if (mp->ndihedrals > 0) {
3984 fprintf(fp, "!:dihedrals\n");
3985 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3986 for (i = 0; i < mp->ndihedrals; i++) {
3987 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' : ' '));
3992 if (mp->nimpropers > 0) {
3993 fprintf(fp, "!:impropers\n");
3994 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3995 for (i = 0; i < mp->nimpropers; i++) {
3996 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' : ' '));
4001 if (mp->cell != NULL) {
4002 fprintf(fp, "!:xtalcell\n");
4003 fprintf(fp, "! a b c alpha beta gamma\n");
4004 fprintf(fp, "! This information is redundant and overridden by the following periodic_box info\n");
4005 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]);
4008 fprintf(fp, "!:periodic_box\n");
4009 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz; fa fb fc [sigma; sa sb sc s_alpha s_beta s_gamma]\n");
4010 for (i = 0; i < 3; i++)
4011 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->axes[i].x, mp->cell->axes[i].y, mp->cell->axes[i].z);
4012 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->origin.x, mp->cell->origin.y, mp->cell->origin.z);
4013 fprintf(fp, "%d %d %d%s\n", mp->cell->flags[0], mp->cell->flags[1], mp->cell->flags[2], (mp->cell->has_sigma ? " 1" : ""));
4014 if (mp->cell->has_sigma) {
4015 fprintf(fp, "%f %f %f %f %f %f\n", mp->cell->cellsigma[0], mp->cell->cellsigma[1], mp->cell->cellsigma[2], mp->cell->cellsigma[3], mp->cell->cellsigma[4], mp->cell->cellsigma[5]);
4020 if (mp->nframe_cells > 0) {
4021 fprintf(fp, "!:frame_periodic_boxes\n");
4022 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz\n");
4023 for (i = 0; i < mp->nframe_cells * 4; i++) {
4024 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->frame_cells[i].x, mp->frame_cells[i].y, mp->frame_cells[i].z);
4029 if (mp->nsyms > 0) {
4030 fprintf(fp, "!:symmetry_operations\n");
4031 fprintf(fp, "! a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3\n");
4032 for (i = 0; i < mp->nsyms; i++) {
4033 Transform *tp = mp->syms + i;
4034 const unsigned char s_index_order[12] = {0, 3, 6, 1, 4, 7, 2, 5, 8, 9, 10, 11};
4035 for (j = 0; j < 12; j++)
4036 fprintf(fp, "%11.6f%c", (*tp)[s_index_order[j]], (j % 3 == 2 ? '\n' : ' '));
4042 fprintf(fp, "!:anisotropic_thermal_parameters\n");
4043 fprintf(fp, "! b11 b22 b33 b12 b13 b23 [sigma; sb11 sb22 sb33 sb12 sb13 sb23]\n");
4044 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4045 if (ap->aniso != NULL) {
4046 Double *bp = ap->aniso->bij;
4047 fprintf(fp, "%14.8g %14.8g %14.8g %14.8g %14.8g %14.8g%s\n", bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], (ap->aniso->has_bsig ? " 1" : ""));
4048 if (ap->aniso->has_bsig) {
4049 bp = ap->aniso->bsig;
4050 fprintf(fp, "%14.8g %14.8g %14.8g %14.8g %14.8g %14.8g\n", bp[0], bp[1], bp[2], bp[3], bp[4], bp[5]);
4053 fprintf(fp, "0 0 0 0 0 0\n");
4059 if (mp->arena != NULL) {
4060 MDArena *arena = mp->arena;
4061 fprintf(fp, "!:md_parameters\n");
4062 fprintf(fp, "log_file %s\n", arena->log_result_name);
4063 fprintf(fp, "coord_file %s\n", arena->coord_result_name);
4064 fprintf(fp, "vel_file %s\n", arena->vel_result_name);
4065 fprintf(fp, "force_file %s\n", arena->force_result_name);
4066 fprintf(fp, "debug_file %s\n", arena->debug_result_name);
4067 fprintf(fp, "debug_output_level %d\n", arena->debug_output_level);
4068 fprintf(fp, "step %d\n", arena->step);
4069 fprintf(fp, "coord_output_freq %d\n", arena->coord_output_freq);
4070 fprintf(fp, "energy_output_freq %d\n", arena->energy_output_freq);
4071 fprintf(fp, "coord_frame %d\n", arena->coord_result_frame);
4072 fprintf(fp, "timestep %g\n", arena->timestep);
4073 fprintf(fp, "cutoff %g\n", arena->cutoff);
4074 fprintf(fp, "electro_cutoff %g\n", arena->electro_cutoff);
4075 fprintf(fp, "pairlist_distance %g\n", arena->pairlist_distance);
4076 fprintf(fp, "switch_distance %g\n", arena->switch_distance);
4077 fprintf(fp, "temperature %g\n", arena->temperature);
4078 fprintf(fp, "andersen_freq %d\n", arena->andersen_thermo_freq);
4079 fprintf(fp, "andersen_coupling %g\n", arena->andersen_thermo_coupling);
4080 fprintf(fp, "random_seed %d\n", arena->random_seed);
4081 fprintf(fp, "dielectric %g\n", arena->dielectric);
4082 fprintf(fp, "gradient_convergence %g\n", arena->gradient_convergence);
4083 fprintf(fp, "coordinate_convergence %g\n", arena->coordinate_convergence);
4084 fprintf(fp, "use_xplor_shift %d\n", arena->use_xplor_shift);
4085 fprintf(fp, "scale14_vdw %g\n", arena->scale14_vdw);
4086 fprintf(fp, "scale14_elect %g\n", arena->scale14_elect);
4087 fprintf(fp, "relocate_center %d\n", arena->relocate_center);
4088 fprintf(fp, "surface_probe_radius %g\n", arena->probe_radius);
4089 fprintf(fp, "surface_tension %g\n", arena->surface_tension);
4090 fprintf(fp, "surface_potential_freq %d\n", arena->surface_potential_freq);
4091 fprintf(fp, "use_graphite %d\n", arena->use_graphite);
4092 fprintf(fp, "alchemical_lambda %g\n", arena->alchem_lambda);
4093 fprintf(fp, "alchemical_delta_lambda %g\n", arena->alchem_dlambda);
4094 if (arena->nalchem_flags > 0) {
4095 fprintf(fp, "alchem_flags %d", arena->nalchem_flags);
4096 for (i = 0; i < arena->nalchem_flags; i++) {
4099 else if (i % 10 == 0)
4101 fputc('0' + arena->alchem_flags[i], fp);
4105 if (arena->pressure != NULL) {
4107 fprintf(fp, "pressure_freq %d\n", arena->pressure->freq);
4108 fprintf(fp, "pressure_coupling %g\n", arena->pressure->coupling);
4109 dp = arena->pressure->apply;
4110 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]);
4111 dp = arena->pressure->cell_flexibility;
4112 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]);
4113 fprintf(fp, "pressure_fluctuate_cell_origin %g\n", arena->pressure->fluctuate_cell_origin);
4114 fprintf(fp, "pressure_fluctuate_cell_orientation %g\n", arena->pressure->fluctuate_cell_orientation);
4118 if (mp->par != NULL) {
4119 Parameter *par = mp->par;
4120 fprintf(fp, "!:parameters\n");
4121 ParameterAppendToFile(par, fp);
4125 fprintf(fp, "!:velocity\n");
4126 fprintf(fp, "! idx vx vy vz\n");
4127 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4128 fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->v.x, ap->v.y, ap->v.z);
4132 fprintf(fp, "!:force\n");
4133 fprintf(fp, "! idx fx fy fz\n");
4134 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4135 fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->f.x, ap->f.y, ap->f.z);
4140 if (mp->mview != NULL) {
4142 if (mp->mview->track != NULL) {
4143 fprintf(fp, "!:trackball\n");
4144 fprintf(fp, "! scale; trx try trz; theta_deg x y z\n");
4145 f[0] = TrackballGetScale(mp->mview->track);
4146 fprintf(fp, "%f\n", f[0]);
4147 TrackballGetTranslate(mp->mview->track, f);
4148 fprintf(fp, "%f %f %f\n", f[0], f[1], f[2]);
4149 TrackballGetRotate(mp->mview->track, f);
4150 fprintf(fp, "%f %f %f %f\n", f[0], f[1], f[2], f[3]);
4153 fprintf(fp, "!:view\n");
4154 fprintf(fp, "show_unit_cell %d\n", mp->mview->showUnitCell);
4155 fprintf(fp, "show_periodic_box %d\n", mp->mview->showPeriodicBox);
4156 fprintf(fp, "show_expanded_atoms %d\n", mp->mview->showExpandedAtoms);
4157 fprintf(fp, "show_ellipsoids %d\n", mp->mview->showEllipsoids);
4158 fprintf(fp, "show_hydrogens %d\n", mp->mview->showHydrogens);
4159 fprintf(fp, "show_dummy_atoms %d\n", mp->mview->showDummyAtoms);
4160 fprintf(fp, "show_rotation_center %d\n", mp->mview->showRotationCenter);
4161 fprintf(fp, "show_graphite_flag %d\n", mp->mview->showGraphiteFlag);
4162 fprintf(fp, "show_graphite %d\n", mp->mview->showGraphite);
4163 fprintf(fp, "show_periodic_image_flag %d\n", mp->mview->showPeriodicImageFlag);
4164 fprintf(fp, "show_periodic_image %d %d %d %d %d %d\n",
4165 mp->mview->showPeriodicImage[0], mp->mview->showPeriodicImage[1],
4166 mp->mview->showPeriodicImage[2], mp->mview->showPeriodicImage[3],
4167 mp->mview->showPeriodicImage[4], mp->mview->showPeriodicImage[5]);
4168 if (mp->mview->atomRadius != 0.2)
4169 fprintf(fp, "atom_radius %f\n", mp->mview->atomRadius);
4170 if (mp->mview->bondRadius != 0.1)
4171 fprintf(fp, "bond_radius %f\n", mp->mview->bondRadius);
4172 if (mp->mview->atomResolution != 12)
4173 fprintf(fp, "atom_resolution %d\n", mp->mview->atomResolution);
4174 if (mp->mview->bondResolution != 8)
4175 fprintf(fp, "bond_resolution %d\n", mp->mview->bondResolution);
4184 MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char **errbuf)
4190 fp = fopen(fname, "wb");
4192 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4195 fprintf(fp, "PSF\n\n");
4196 fprintf(fp, " 1 !NTITLE\n");
4197 fprintf(fp, " REMARKS FILENAME=\n");
4201 fprintf(fp, "%8d !NATOM\n", mp->natoms);
4202 for (i = 0; i < mp->natoms; i++) {
4204 ap = ATOM_AT_INDEX(mp->atoms, i);
4205 fprintf(fp, "%8d ", i + 1);
4206 if (ap->resSeq >= 10000) {
4207 fmt = "%-3.3s %-5d ";
4209 fmt = "%-4.4s %-4d ";
4211 fprintf(fp, fmt, ap->segName, ap->resSeq);
4212 fprintf(fp, "%-3.3s %-4.4s %-4.4s %12.6f %8.4f 0\n",
4213 ap->resName, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->charge, ap->weight);
4218 fprintf(fp, "%8d !NBOND: bonds\n", mp->nbonds);
4219 for (i = 0; i < mp->nbonds * 2; i++) {
4220 fprintf(fp, "%8d", mp->bonds[i] + 1);
4229 fprintf(fp, "%8d !NTHETA: angles\n", mp->nangles);
4230 for (i = 0; i < mp->nangles * 3; i++) {
4231 fprintf(fp, "%8d", mp->angles[i] + 1);
4240 fprintf(fp, "%8d !NPHI: dihedrals\n", mp->ndihedrals);
4241 for (i = 0; i < mp->ndihedrals * 4; i++) {
4242 fprintf(fp, "%8d", mp->dihedrals[i] + 1);
4251 fprintf(fp, "%8d !NIMPHI: impropers\n", mp->nimpropers);
4252 for (i = 0; i < mp->nimpropers * 4; i++) {
4253 fprintf(fp, "%8d", mp->impropers[i] + 1);
4261 fprintf(fp, "%8d !NDON: donors\n\n", 0);
4262 fprintf(fp, "%8d !NACC: acceptors\n\n", 0);
4263 fprintf(fp, "%8d !NNB: non-bonding exclusions\n\n", 0);
4264 for (i = 0; i < mp->natoms; i++) {
4265 fprintf(fp, "%8d", 0);
4272 fprintf(fp, "%8d !NGRP: groups\n", 1);
4273 fprintf(fp, " 0 0 0\n");
4277 if (i > 5 && strcmp(fname + i - 5, ".psfx") == 0) {
4278 /* Extended psf (with coordinates and other info) */
4279 fprintf(fp, "%8d !COORD: coordinates\n", mp->natoms);
4280 for (i = 0; i < mp->natoms; i++) {
4282 ap = ATOM_AT_INDEX(mp->atoms, i);
4284 fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->aname);
4294 MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char **errbuf)
4300 fp = fopen(fname, "wb");
4302 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4305 for (i = 0; i < mp->natoms; i++) {
4307 ap = ATOM_AT_INDEX(mp->atoms, i);
4308 if (ap->resSeq >= 10000) {
4309 snprintf(buf, sizeof buf, "%c%03d", 'A' + (ap->resSeq - 10000) / 1000, ap->resSeq % 1000);
4311 snprintf(buf, sizeof buf, "%4d", ap->resSeq);
4313 fprintf(fp, "ATOM %5d %-4.4s%1.1s%-3.3s %1.1s%4.4s%1.1s "
4314 "%8.3f%8.3f%8.3f %5.2f %5.2f "
4315 "%-4.4s%-2.2s%-2d\n",
4316 i + 1, ap->aname, " ", ap->resName, " ", buf, " ",
4317 ap->r.x, ap->r.y, ap->r.z, ap->occupancy, ap->tempFactor,
4318 ap->segName, ap->element, ap->intCharge);
4320 for (i = 0; i < mp->natoms; i++) {
4322 ap = ATOM_AT_INDEX(mp->atoms, i);
4323 cp = AtomConnectData(&ap->connect);
4324 for (j = 0; j < ap->connect.count; j++) {
4328 fprintf(fp, "CONECT%5d", i + 1);
4330 fprintf(fp, "%5d", cp[j] + 1);
4335 fprintf(fp, "END\n");
4341 MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char **errbuf)
4344 SFloat32 *xp, *yp, *zp;
4347 if (mp == NULL || mp->natoms == 0) {
4348 s_append_asprintf(errbuf, "Molecule is empty");
4351 memset(&dcd, 0, sizeof(dcd));
4352 dcd.natoms = mp->natoms;
4353 dcd.nframes = MoleculeGetNumberOfFrames(mp);
4354 if (dcd.nframes == 0) {
4355 s_append_asprintf(errbuf, "no frame is present");
4358 dcd.nstart = mp->startStep;
4359 dcd.ninterval = mp->stepsPerFrame;
4360 if (dcd.ninterval == 0)
4362 dcd.nend = dcd.nstart + (dcd.nframes - 1) * dcd.ninterval;
4363 if (mp->cell != NULL)
4365 dcd.delta = mp->psPerStep;
4366 if (dcd.delta == 0.0)
4369 n = DcdCreate(fname, &dcd);
4372 s_append_asprintf(errbuf, "Cannot create dcd file");
4374 s_append_asprintf(errbuf, "Cannot write dcd header");
4379 xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4380 yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4381 zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4382 if (xp == NULL || yp == NULL || zp == NULL) {
4383 s_append_asprintf(errbuf, "Cannot allocate memory");
4390 for (n = 0; n < dcd.nframes; n++) {
4393 for (i = 0, ap = mp->atoms; i < dcd.natoms && mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4395 if (ap->frames == NULL || n >= ap->nframes)
4403 if (i < dcd.natoms) {
4404 size_t sz = (dcd.natoms - i) * sizeof(SFloat32);
4405 memset(xp + i, 0, sz);
4406 memset(yp + i, 0, sz);
4407 memset(zp + i, 0, sz);
4409 if (n < mp->nframe_cells && mp->frame_cells != NULL) {
4410 Vector *cp = &(mp->frame_cells[n * 4]);
4411 dcd.globalcell[0] = VecLength(cp[0]);
4412 dcd.globalcell[2] = VecLength(cp[1]);
4413 dcd.globalcell[5] = VecLength(cp[2]);
4414 dcd.globalcell[1] = VecDot(cp[0], cp[1]) / (dcd.globalcell[0] * dcd.globalcell[2]);
4415 dcd.globalcell[3] = VecDot(cp[0], cp[2]) / (dcd.globalcell[0] * dcd.globalcell[5]);
4416 dcd.globalcell[4] = VecDot(cp[1], cp[2]) / (dcd.globalcell[2] * dcd.globalcell[5]);
4418 if (DcdWriteFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
4419 s_append_asprintf(errbuf, "Write error in dcd file");
4435 MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
4441 fp = fopen(fname, "wb");
4443 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4446 if (mp->cell != NULL) {
4447 fprintf(fp, "Bounding box:\n");
4448 for (i = 0; i < 3; i++) {
4449 v = mp->cell->axes[i];
4450 fprintf(fp, "%.3f %.3f %.3f %d\n", v.x, v.y, v.z, mp->cell->flags[i]);
4452 fprintf(fp, "Bounding box origin:\n");
4453 v = mp->cell->origin;
4454 fprintf(fp, "%.3f %.3f %.3f\n", v.x, v.y, v.z);
4461 sCompareByElement(const void *ap, const void *bp)
4463 return ((*(Atom **)bp)->atomicNumber - (*(Atom **)ap)->atomicNumber);
4467 sMakeAdc(int n, int base, Symop symop)
4470 if (SYMOP_ALIVE(symop)) {
4472 sym = (symop.dx + 5) * 10000 + (symop.dy + 5) * 1000 + (symop.dz + 5) * 100 + symop.sym + 1;
4477 return (an + 1) * 100000 + sym;
4481 sCompareAdc(const void *ap, const void *bp)
4483 int n = *((Int *)ap) % 100000 - *((Int *)bp) % 100000;
4485 n = *((Int *)ap) / 100000 - *((Int *)bp) / 100000;
4490 sOutputAtomListInstructions(FILE *fp, int natoms, Atom *atoms)
4492 int i, j, k, an, sym;
4495 adc = (Int *)malloc(sizeof(Int) * natoms);
4498 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4499 if (ap->exflags & kAtomHiddenFlag)
4501 adc[i] = sMakeAdc(i, ap->symbase, ap->symop);
4503 mergesort(adc, natoms, sizeof(Int), sCompareAdc);
4505 /* Create the atom list */
4507 for (i = j = k = 0; i < natoms; i++) {
4508 int an1 = adc[i] / 100000;
4509 int sym1 = adc[i] % 100000;
4510 if (sym == sym1 && an1 == an + 1) {
4517 /* Output the last atom with a minus sign */
4518 adc[j++] = -(an * 100000 + sym);
4519 /* Output this atom */
4526 adc[j++] = -(an * 100000 + sym);
4528 /* Create the instruction cards */
4529 for (i = k = 0; i < j; i++) {
4531 fprintf(fp, " 401");
4532 fprintf(fp, "%9d", adc[i]);
4534 if (i == j - 1 || k == 6 || (k == 5 && i < j - 2 && adc[i + 2] < 0)) {
4543 sEllipsoidType(int an)
4545 return (an >= 18 ? 3 : (an >= 2 && an != 6 ? 2 : (an > 0 ? 1 : 0)));
4549 sOutputAtomTypeInstructions(FILE *fp, int natoms, Atom *atoms)
4553 int etype, elast, istart, ilast, n1, n2;
4554 elast = istart = ilast = -1;
4555 for (i = 0, ap = atoms; i <= natoms; i++, ap++) {
4557 if (SYMOP_ALIVE(ap->symop))
4559 if (ap->exflags & kAtomHiddenFlag)
4561 etype = sEllipsoidType(ap->atomicNumber);
4566 } else if (elast == etype && ilast == i - 1) {
4571 /* Output the instruction card for the 'last' block of atoms */
4574 n1 = 4; n2 = 0; break;
4576 n1 = 4; n2 = 5; break;
4578 n1 = 1; n2 = 0; break;
4580 fprintf(fp, " 1 715 %8d 0 %8d 0 0.100 0.000 0.000\n", n1, n2);
4581 fprintf(fp, " %9d%9d\n", istart + 1, ilast + 1);
4588 sCompareBondType(const void *ap, const void *bp)
4590 /* Descending order */
4591 return *((int *)bp) - *((int *)ap);
4595 sOutputBondInstructions(FILE *fp, int natoms, Atom *atoms, int overlap_correction)
4599 int i, j, n[5], an, count, n1, n2, k;
4603 static const float sBondRad[4] = {0.060, 0.060, 0.060, 0.040};
4604 static const int sBondShade[4] = {5, 3, 1, 1};
4606 n[0] = n[1] = n[2] = n[3] = 0; /* Start index of 3rd row atoms (and higher), 2nd row, 1st row, and H */
4608 for (i = natoms - 1, ap = atoms + i; i >= 0; i--, ap--) {
4609 an = ap->atomicNumber;
4621 if (overlap_correction)
4622 strcpy(buf, " 2 1001 0.000\n");
4624 strcpy(buf, " 2 812\n");
4626 for (i = 0; i < 4; i++) {
4627 for (j = i; j < 4; j++) {
4628 /* Examine bonds between "group i" and "group j" */
4631 double min_bond = 10000.0; /* Minimum distance between bound atoms */
4632 double min_nonbond = 10000.0; /* Minimum distance between non-bound atoms */
4633 double max_bond = -10000.0; /* Maximum distance between bound atoms */
4634 int count_exbond = 0; /* Number of explicit bonds in this group */
4635 for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
4636 for (n2 = n[j], ap2 = atoms + n2; n2 < n[j + 1]; n2++, ap2++) {
4639 VecSub(dr, ap->r, ap2->r);
4641 cp = AtomConnectData(&ap->connect);
4642 for (k = ap->connect.count - 1; k >= 0; k--) {
4647 /* n1 and n2 are bound */
4653 /* n1 and n2 are not bound */
4654 if (d < min_nonbond)
4659 if (min_bond == 10000.0)
4660 continue; /* No bonds between these groups */
4662 if (max_bond + 0.002 < min_nonbond)
4665 max_bond = min_nonbond - 0.002;
4666 /* Some bonds may be omitted, so scan all bonds again */
4667 for (n1 = n[i], ap = ATOM_AT_INDEX(atoms, n1); n1 < n[i + 1]; n1++, ap = ATOM_NEXT(ap)) {
4668 cp = AtomConnectData(&ap->connect);
4669 for (k = ap->connect.count - 1; k >= 0; k--) {
4671 if (n2 < n[j] || n2 >= n[j + 1])
4674 VecSub(dr, ap->r, ap2->r);
4677 /* This bond should be explicitly defined */
4679 if (count_exbond == 0) {
4680 adc1 = -(i + 1); /* Bond type */
4681 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4683 adc1 = sMakeAdc(n1, ap->symbase, ap->symop);
4684 adc2 = sMakeAdc(n2, ap2->symbase, ap2->symop);
4685 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4686 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc2);
4692 /* Output the last instruction card */
4694 /* Make a new trailer card */
4695 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]);
4700 /* Output the last trailer card */
4705 if (count == 0 && overlap_correction) {
4706 /* 1001 card is not yet written, so write it */
4710 snprintf(buf, sizeof(buf), " 1 %3d", (overlap_correction ? 821 : 811));
4711 k = -exbonds[0] - 1; /* Bond type for the first block */
4712 i = 1; /* Index for exbonds[] */
4713 j = 0; /* Count in this block */
4714 while (i <= nexbonds) {
4715 if (j >= 29 || i == nexbonds || exbonds[i] < 0) {
4719 /* The trailer card */
4720 fprintf(fp, " %3d %6.3f\n", sBondShade[k], sBondRad[k]);
4724 k = -exbonds[i++] - 1; /* The new bond type */
4726 } else if (j > 0 && j % 3 == 0) {
4732 snprintf(buf + 9 + (j % 3) * 18, sizeof(buf) - 9 - (j % 3) * 18, "%9d%9d\n", n1, n2);
4740 MoleculeWriteToTepFile(Molecule *mp, const char *fname, char **errbuf)
4743 int i, j, natoms, *ip;
4745 Atom *ap, *atoms, **app;
4747 static Double sUnit[] = {1, 1, 1, 90, 90, 90};
4751 /* Create sorted array of atoms */
4752 natoms = mp->natoms;
4753 atoms = (Atom *)calloc(sizeof(Atom), natoms);
4754 app = (Atom **)calloc(sizeof(Atom *), natoms);
4755 ip = (int *)calloc(sizeof(int), natoms);
4756 if (atoms == NULL || app == NULL || ip == NULL) {
4757 s_append_asprintf(errbuf, "Cannot allocate memory");
4760 /* Sort the atom pointer by atomic number */
4761 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
4763 mergesort(app, natoms, sizeof(Atom *), sCompareByElement);
4764 for (i = 0; i < natoms; i++) {
4765 /* ip[old_index] is new_index */
4766 ip[app[i] - mp->atoms] = i;
4768 /* Copy the atom record to atoms[] */
4769 /* The 'v' member contains crystallographic coordinates */
4770 /* The connection table and symbase are renumbered */
4771 /* Hidden flags are modified to reflect the visibility in the MainView */
4772 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4773 AtomDuplicateNoFrame(ap, app[i]);
4774 /* memmove(ap, app[i], gSizeOfAtomRecord); */
4775 MoleculeCartesianToXtal(mp, &(ap->v), &(ap->r));
4776 cp = AtomConnectData(&ap->connect);
4777 for (j = ap->connect.count - 1; j >= 0; j--) {
4780 if (SYMOP_ALIVE(ap->symop))
4781 ap->symbase = ip[ap->symbase];
4782 if (MainView_isAtomHidden(mp->mview, i)) {
4783 ap->exflags |= kAtomHiddenFlag;
4785 ap->exflags &= ~kAtomHiddenFlag;
4791 fp = fopen(fname, "wb");
4793 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4798 fprintf(fp, "Generated by Molby\n");
4801 if (mp->cell != NULL) {
4802 dp = mp->cell->cell;
4806 fprintf(fp, "%9.3f%9.3f%9.3f%9.3f%9.3f%9.3f\n", dp[0], dp[1], dp[2], dp[3], dp[4], dp[5]);
4808 /* Symmetry operations */
4809 if (mp->nsyms > 0) {
4810 for (i = 0; i < mp->nsyms; i++) {
4812 fprintf(fp, "%c%14g%3g%3g%3g%15g%3g%3g%3g%15g%3g%3g%3g\n", (i == mp->nsyms - 1 ? '1' : ' '), dp[9], dp[0], dp[1], dp[2], dp[10], dp[3], dp[4], dp[5], dp[11], dp[6], dp[7], dp[8]);
4815 fprintf(fp, "1 0 1 0 0 0 0 1 0 0 0 0 1\n");
4819 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4820 /* The 'v' field contains crystallographic coordinates */
4821 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);
4822 if (ap->aniso != NULL) {
4823 dp = ap->aniso->bij;
4824 fprintf(fp, " %8.5f%9.6f%9.6f%9.6f%9.6f%9.6f%9d\n", dp[0], dp[1], dp[2], dp[3], dp[4], dp[5], 0);
4826 Double temp = ap->tempFactor;
4829 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", temp, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4832 /* Special points */
4834 Vector camera, lookat, up, xvec, yvec, zvec;
4835 MainView_getCamera(mp->mview, &camera, &lookat, &up);
4836 VecSub(zvec, lookat, camera);
4837 VecCross(xvec, zvec, up);
4838 NormalizeVec(&xvec, &xvec);
4839 NormalizeVec(&yvec, &up);
4840 VecInc(xvec, lookat);
4841 VecInc(yvec, lookat);
4842 MoleculeCartesianToXtal(mp, &lookat, &lookat);
4843 MoleculeCartesianToXtal(mp, &xvec, &xvec);
4844 MoleculeCartesianToXtal(mp, &yvec, &yvec);
4845 fprintf(fp, " ORGN %9g%9g%9g 0\n", 0.0, 0.0, 0.0);
4846 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4847 fprintf(fp, " CNTR %9g%9g%9g 0\n", lookat.x, lookat.y, lookat.z);
4848 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4849 fprintf(fp, " X %9g%9g%9g 0\n", xvec.x, xvec.y, xvec.z);
4850 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4851 fprintf(fp, " Y %9g%9g%9g 0\n", yvec.x, yvec.y, yvec.z);
4852 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);
4856 fprintf(fp, " 201\n");
4857 fprintf(fp, " 205 12\n");
4858 fprintf(fp, " 301 6.6 6.6 0 0.8\n");
4859 sOutputAtomListInstructions(fp, natoms, atoms);
4860 fprintf(fp, " 501%4d55501%4d55501%4d55501%4d55501%4d55501 1\n", natoms + 2, natoms + 2, natoms + 3, natoms + 2, natoms + 4);
4861 fprintf(fp, " 502 1 0.0 2 0.0 3 0.0\n");
4862 fprintf(fp, " 604 1.538\n");
4864 sOutputBondInstructions(fp, natoms, atoms, 1);
4865 sOutputAtomTypeInstructions(fp, natoms, atoms);
4866 sOutputBondInstructions(fp, natoms, atoms, 0);
4868 for (i = 0; i < natoms; i++) {
4869 AtomClean(atoms + i);
4873 fprintf(fp, " 202\n");
4874 fprintf(fp, " 0 -1\n");
4880 MoleculeDump(Molecule *mol)
4885 for (i = 0; i < mol->natoms; i++) {
4887 ap = ATOM_AT_INDEX(mol->atoms, i);
4888 snprintf(buf1, sizeof buf1, "%3.4s.%d", ap->resName, ap->resSeq);
4889 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);
4890 cp = AtomConnectData(&ap->connect);
4891 for (j = 0; j < ap->connect.count; j++) {
4892 fprintf(stderr, "%s%d", (j > 0 ? "," : ""), cp[j]);
4894 fprintf(stderr, "]\n");
4898 #pragma mark ====== MD support (including modification of Molecule) ======
4900 /* Call md_prepare for the MDArena. If MDArena has not been created, a new arena is created.
4901 If something goes wrong, returns 1 (for missing parameters) or -1 (more serious error).
4902 If retmsg is not NULL, a message describing the problem is returned there. This message
4903 must be free'd by the caller. */
4905 MoleculePrepareMDArena(Molecule *mol, int check_only, char **retmsg)
4908 Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
4910 IntGroup *ig1, *ig2, *ig3;
4911 MDArena *arena = mol->arena;
4913 if (arena == NULL) {
4916 } else if (arena->xmol != mol)
4917 md_arena_set_molecule(arena, mol);
4919 arena->is_initialized = 0;
4921 /* Rebuild the tables */
4922 ig1 = ig2 = ig3 = NULL;
4923 nangles = MoleculeFindMissingAngles(mol, &angles);
4924 ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
4925 nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
4927 ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
4928 MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
4930 IntGroupRelease(ig1);
4932 if (ndihedrals > 0) {
4933 ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
4934 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
4936 IntGroupRelease(ig2);
4938 if (nimpropers > 0) {
4939 ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
4940 MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
4942 IntGroupRelease(ig3);
4946 /* Update the path information of the molecule before MD setup */
4947 char *buf = (char *)malloc(4096);
4948 MoleculeCallback_pathName(mol, buf, sizeof buf);
4949 MoleculeSetPath(mol, buf);
4953 /* Prepare parameters and internal information */
4954 msg = md_prepare(arena, check_only);
4956 /* Some parameters are missing? */
4958 if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
4962 asprintf(retmsg, "cannot initialize for MD: %s", msg);
4967 /* The local parameter list is updated */
4970 if (mol->par == NULL)
4971 mol->par = ParameterNew();
4972 for (parType = kFirstParType; parType <= kLastParType; parType++) {
4973 /* Delete global and undefined parameters */
4974 UnionPar *up, *upbuf;
4976 ig1 = IntGroupNew();
4977 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
4978 if (up->bond.src != 0)
4979 IntGroupAdd(ig1, idx, 1);
4981 if (IntGroupGetCount(ig1) > 0)
4982 MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
4983 IntGroupRelease(ig1);
4984 /* Copy global and undefined parameters from arena and insert to mol->par */
4985 nparams = ParameterGetCountForType(arena->par, parType);
4988 upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
4989 ig1 = IntGroupNew();
4990 ig2 = IntGroupNew();
4991 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
4992 if (up->bond.src > 0)
4993 IntGroupAdd(ig1, idx, 1); /* Global parameter */
4994 else if (up->bond.src < 0)
4995 IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
4997 if ((count = IntGroupGetCount(ig1)) > 0) {
4998 /* Insert global parameters (at the top) */
4999 ParameterCopy(arena->par, parType, upbuf, ig1);
5000 ig3 = IntGroupNewWithPoints(0, count, -1);
5001 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5002 IntGroupRelease(ig3);
5004 if ((count = IntGroupGetCount(ig2)) > 0) {
5005 /* Insert undefined parameters (at the bottom) */
5006 ParameterCopy(arena->par, parType, upbuf, ig2);
5007 idx = ParameterGetCountForType(mol->par, parType);
5008 ig3 = IntGroupNewWithPoints(idx, count, -1);
5009 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5010 IntGroupRelease(ig3);
5012 IntGroupRelease(ig2);
5013 IntGroupRelease(ig1);
5016 mol->needsMDRebuild = 0; /* We know the "modified" parameters are consistent with the MDArena */
5021 *retmsg = strdup(msg);
5026 #pragma mark ====== Serialize ======
5029 MoleculeDeserialize(const char *data, Int length, Int *timep)
5039 par = ParameterNew();
5043 while (length >= 12) {
5044 const char *ptr = data + 8 + sizeof(Int);
5045 int len = *((const Int *)(data + 8));
5047 if (strcmp(data, "ATOM") == 0) {
5048 n = len / gSizeOfAtomRecord;
5049 NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, n);
5050 memmove(mp->atoms, ptr, len);
5051 } else if (strcmp(data, "ANISO") == 0) {
5052 n = len / (sizeof(Int) + sizeof(Aniso));
5053 for (i = 0; i < n; i++) {
5054 j = *((const Int *)ptr);
5055 if (j < 0 || j >= mp->natoms)
5057 ap = ATOM_AT_INDEX(mp->atoms, j);
5058 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
5059 if (ap->aniso == NULL)
5061 *(ap->aniso) = *((Aniso *)(ptr + sizeof(Int)));
5062 ptr += sizeof(Int) + sizeof(Aniso);
5064 } else if (strcmp(data, "FRAME") == 0) {
5065 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5066 if (ap->nframes == 0)
5068 ap->frames = (Vector *)malloc(sizeof(Vector) * ap->nframes);
5069 if (ap->frames == NULL)
5071 memmove(ap->frames, ptr, sizeof(Vector) * ap->nframes);
5072 ptr += sizeof(Vector) * ap->nframes;
5074 } else if (strcmp(data, "EXTCON") == 0) {
5075 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5076 if (ap->connect.count <= ATOM_CONNECT_LIMIT)
5078 n = ap->connect.count;
5079 ap->connect.count = 0;
5080 ap->connect.u.ptr = NULL;
5081 NewArray(&(ap->connect.u.ptr), &(ap->connect.count), sizeof(Int), n);
5082 memmove(ap->connect.u.ptr, ptr, sizeof(Int) * n);
5083 ptr += sizeof(Int) * n;
5085 } else if (strcmp(data, "BOND") == 0) {
5086 n = len / (sizeof(Int) * 2);
5087 NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, n);
5088 memmove(mp->bonds, ptr, len);
5089 } else if (strcmp(data, "ANGLE") == 0) {
5090 n = len / (sizeof(Int) * 3);
5091 NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, n);
5092 memmove(mp->angles, ptr, len);
5093 } else if (strcmp(data, "DIHED") == 0) {
5094 n = len / (sizeof(Int) * 4);
5095 NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, n);
5096 memmove(mp->dihedrals, ptr, len);
5097 } else if (strcmp(data, "IMPROP") == 0) {
5098 n = len / (sizeof(Int) * 4);
5099 NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, n);
5100 memmove(mp->impropers, ptr, len);
5101 } else if (strcmp(data, "RESIDUE") == 0) {
5103 NewArray(&mp->residues, &mp->nresidues, 4, n);
5104 memmove(mp->residues, ptr, len);
5105 } else if (strcmp(data, "CELL") == 0) {
5106 mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
5107 if (mp->cell == NULL)
5109 memmove(mp->cell, ptr, sizeof(XtalCell));
5110 } else if (strcmp(data, "SYMOP") == 0) {
5111 n = len / sizeof(Transform);
5112 NewArray(&mp->syms, &mp->nsyms, sizeof(Transform), n);
5113 memmove(mp->syms, ptr, len);
5114 } else if (strcmp(data, "ANCHOR") == 0) {
5115 const char *ptr2 = ptr + len;
5116 while (ptr < ptr2) {
5118 memset(&an, 0, sizeof(an));
5120 if (i >= 0 && i < mp->natoms) {
5121 n = *((Int *)(ptr + sizeof(Int)));
5122 AtomConnectResize(&(an.connect), n);
5123 memmove(AtomConnectData(&(an.connect)), ptr + sizeof(Int) * 2, sizeof(Int) * n);
5124 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), n);
5125 memmove(an.coeffs, ptr + sizeof(Int) * (2 + n), sizeof(Double) * n);
5126 ap = ATOM_AT_INDEX(mp->atoms, i);
5127 ap->anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
5128 memmove(ap->anchor, &an, sizeof(PiAnchor));
5130 ptr += sizeof(Int) * (2 + n) + sizeof(Double) * n;
5132 } else if (strcmp(data, "TIME") == 0) {
5134 *timep = *((Int *)ptr);
5135 } else if (strcmp(data, "BONDPAR") == 0) {
5137 n = len / sizeof(BondPar);
5138 NewArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), n);
5139 memmove(par->bondPars, ptr, len);
5140 } else if (strcmp(data, "ANGPAR") == 0) {
5142 n = len / sizeof(AnglePar);
5143 NewArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), n);
5144 memmove(par->anglePars, ptr, len);
5145 } else if (strcmp(data, "DIHEPAR") == 0) {
5147 n = len / sizeof(TorsionPar);
5148 NewArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), n);
5149 memmove(par->dihedralPars, ptr, len);
5150 } else if (strcmp(data, "IMPRPAR") == 0) {
5152 n = len / sizeof(TorsionPar);
5153 NewArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), n);
5154 memmove(par->improperPars, ptr, len);
5155 } else if (strcmp(data, "VDWPAR") == 0) {
5157 n = len / sizeof(VdwPar);
5158 NewArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), n);
5159 memmove(par->vdwPars, ptr, len);
5160 } else if (strcmp(data, "VDWPPAR") == 0) {
5162 n = len / sizeof(VdwPairPar);
5163 NewArray(&par->vdwpPars, &par->nvdwpPars, sizeof(VdwPairPar), n);
5164 memmove(par->vdwpPars, ptr, len);
5165 } else if (strcmp(data, "VCUTPAR") == 0) {
5167 n = len / sizeof(VdwCutoffPar);
5168 NewArray(&par->vdwCutoffPars, &par->nvdwCutoffPars, sizeof(VdwCutoffPar), n);
5169 memmove(par->vdwCutoffPars, ptr, len);
5171 len += 8 + sizeof(Int);
5175 if (mp->par == NULL)
5176 ParameterRelease(par);
5177 /* result = MoleculeRebuildTablesFromConnects(mp);
5183 Panic("Low memory while deserializing molecule data");
5184 return NULL; /* Not reached */
5187 Panic("internal error: bad format during deserializing molecule data");
5188 return NULL; /* Not reached */
5192 MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep)
5195 int len, len_all, i, naniso, nframes, nconnects, nanchors;
5198 /* Array of atoms */
5199 len = 8 + sizeof(Int) + gSizeOfAtomRecord * mp->natoms;
5200 ptr = (char *)malloc(len);
5203 memmove(ptr, "ATOM\0\0\0\0", 8);
5204 *((Int *)(ptr + 8)) = gSizeOfAtomRecord * mp->natoms;
5205 p = ptr + 8 + sizeof(Int);
5206 memmove(p, mp->atoms, gSizeOfAtomRecord * mp->natoms);
5207 naniso = nframes = nconnects = nanchors = 0;
5208 for (i = 0; i < mp->natoms; i++) {
5209 ap = ATOM_AT_INDEX(p, i);
5210 if (ap->aniso != NULL) {
5214 if (ap->frames != NULL) {
5215 nframes += ap->nframes;
5218 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5219 nconnects += ap->connect.count;
5220 ap->connect.u.ptr = NULL;
5222 if (ap->anchor != NULL) {
5229 /* Array of aniso */
5231 len = 8 + sizeof(Int) + (sizeof(Int) + sizeof(Aniso)) * naniso;
5232 ptr = (char *)realloc(ptr, len_all + len);
5236 memmove(p, "ANISO\0\0\0", 8);
5237 *((Int *)(p + 8)) = (sizeof(Int) + sizeof(Aniso)) * naniso;
5238 p += 8 + sizeof(Int);
5239 for (i = 0; i < mp->natoms; i++) {
5240 ap = ATOM_AT_INDEX(mp->atoms, i);
5241 if (ap->aniso != NULL) {
5243 *((Aniso *)(p + sizeof(Int))) = *(ap->aniso);
5244 p += sizeof(Int) + sizeof(Aniso);
5250 /* Array of frames */
5252 len = 8 + sizeof(Int) + sizeof(Vector) * nframes;
5253 ptr = (char *)realloc(ptr, len_all + len);
5257 memmove(p, "FRAME\0\0\0", 8);
5258 *((Int *)(p + 8)) = sizeof(Vector) * nframes;
5259 p += 8 + sizeof(Int);
5260 for (i = 0; i < mp->natoms; i++) {
5261 ap = ATOM_AT_INDEX(mp->atoms, i);
5262 if (ap->frames != NULL) {
5263 memmove(p, ap->frames, sizeof(Vector) * ap->nframes);
5264 p += sizeof(Vector) * ap->nframes;
5270 /* Array of connects */
5271 if (nconnects > 0) {
5272 len = 8 + sizeof(Int) + sizeof(Int) * nconnects;
5273 ptr = (char *)realloc(ptr, len_all + len);
5277 memmove(p, "EXTCON\0\0", 8);
5278 *((Int *)(p + 8)) = sizeof(Int) * nconnects;
5279 p += 8 + sizeof(Int);
5280 for (i = 0; i < mp->natoms; i++) {
5281 ap = ATOM_AT_INDEX(mp->atoms, i);
5282 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5283 memmove(p, ap->connect.u.ptr, sizeof(Int) * ap->connect.count);
5284 p += sizeof(Int) * ap->connect.count;
5290 /* Bonds, angles, dihedrals, impropers */
5291 if (mp->nbonds > 0) {
5292 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nbonds;
5293 ptr = (char *)realloc(ptr, len_all + len);
5297 memmove(p, "BOND\0\0\0\0", 8);
5298 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nbonds;
5299 p += 8 + sizeof(Int);
5300 memmove(p, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
5303 if (mp->nangles > 0) {
5304 len = 8 + sizeof(Int) + sizeof(Int) * 3 * mp->nangles;
5305 ptr = (char *)realloc(ptr, len_all + len);
5309 memmove(p, "ANGLE\0\0\0", 8);
5310 *((Int *)(p + 8)) = sizeof(Int) * 3 * mp->nangles;
5311 p += 8 + sizeof(Int);
5312 memmove(p, mp->angles, sizeof(Int) * 3 * mp->nangles);
5315 if (mp->ndihedrals > 0) {
5316 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->ndihedrals;
5317 ptr = (char *)realloc(ptr, len_all + len);
5321 memmove(p, "DIHED\0\0\0", 8);
5322 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->ndihedrals;
5323 p += 8 + sizeof(Int);
5324 memmove(p, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
5327 if (mp->nimpropers > 0) {
5328 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->nimpropers;
5329 ptr = (char *)realloc(ptr, len_all + len);
5333 memmove(p, "IMPROP\0\0", 8);
5334 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->nimpropers;
5335 p += 8 + sizeof(Int);
5336 memmove(p, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
5340 /* Array of residues */
5341 if (mp->nresidues > 0) {
5342 len = 8 + sizeof(Int) + 4 * mp->nresidues;
5343 ptr = (char *)realloc(ptr, len_all + len);
5347 memmove(p, "RESIDUE\0", 8);
5348 *((Int *)(p + 8)) = 4 * mp->nresidues;
5349 p += 8 + sizeof(Int);
5350 memmove(p, mp->residues, 4 * mp->nresidues);
5355 if (mp->cell != NULL) {
5356 len = 8 + sizeof(Int) + sizeof(XtalCell);
5357 ptr = (char *)realloc(ptr, len_all + len);
5361 memmove(p, "CELL\0\0\0\0", 8);
5362 *((Int *)(p + 8)) = sizeof(XtalCell);
5363 p += 8 + sizeof(Int);
5364 memmove(p, mp->cell, sizeof(XtalCell));
5368 /* Symmetry operations */
5369 if (mp->nsyms > 0) {
5370 len = 8 + sizeof(Int) + sizeof(Transform) * mp->nsyms;
5371 ptr = (char *)realloc(ptr, len_all + len);
5375 memmove(p, "SYMOP\0\0\0", 8);
5376 *((Int *)(p + 8)) = sizeof(Transform) * mp->nsyms;
5377 p += 8 + sizeof(Int);
5378 memmove(p, mp->syms, sizeof(Transform) * mp->nsyms);
5384 /* Estimate the necessary storage first */
5385 /* One entry consists of { atom_index (Int), number_of_connects (Int), connects (Int's), weights (Double's) } */
5386 len = 8 + sizeof(Int);
5387 for (i = 0; i < mp->natoms; i++) {
5388 ap = ATOM_AT_INDEX(mp->atoms, i);
5389 if (ap->anchor != NULL)
5390 len += sizeof(Int) * 2 + (sizeof(Int) + sizeof(Double)) * ap->anchor->connect.count;
5392 ptr = (char *)realloc(ptr, len_all + len);
5396 memmove(p, "ANCHOR\0\0", 8);
5397 *((Int *)(p + 8)) = len - (8 + sizeof(Int));
5398 p += 8 + sizeof(Int);
5399 for (i = 0; i < mp->natoms; i++) {
5401 ap = ATOM_AT_INDEX(mp->atoms, i);
5402 if (ap->anchor != NULL) {
5403 count = ap->anchor->connect.count;
5405 *((Int *)(p + sizeof(Int))) = count;
5406 p += sizeof(Int) * 2;
5407 ip = AtomConnectData(&(ap->anchor->connect));
5408 memmove(p, ip, sizeof(Int) * count);
5409 p += sizeof(Int) * count;
5410 memmove(p, ap->anchor->coeffs, sizeof(Double) * count);
5411 p += sizeof(Double) * count;
5418 if (mp->par != NULL) {
5420 for (type = kFirstParType; type <= kLastParType; type++) {
5421 const char *parname;
5422 Int parsize, parcount;
5426 parname = "BONDPAR\0";
5427 parsize = sizeof(BondPar);
5428 parcount = mp->par->nbondPars;
5429 parptr = mp->par->bondPars;
5432 parname = "ANGPAR\0\0";
5433 parsize = sizeof(AnglePar);
5434 parcount = mp->par->nanglePars;
5435 parptr = mp->par->anglePars;
5437 case kDihedralParType:
5438 parname = "DIHEPAR\0";
5439 parsize = sizeof(TorsionPar);
5440 parcount = mp->par->ndihedralPars;
5441 parptr = mp->par->dihedralPars;
5443 case kImproperParType:
5444 parname = "IMPRPAR\0";
5445 parsize = sizeof(TorsionPar);
5446 parcount = mp->par->nimproperPars;
5447 parptr = mp->par->improperPars;
5450 parname = "VDWPAR\0\0";
5451 parsize = sizeof(VdwPar);
5452 parcount = mp->par->nvdwPars;
5453 parptr = mp->par->vdwPars;
5455 case kVdwPairParType:
5456 parname = "VDWPPAR\0";
5457 parsize = sizeof(VdwPairPar);
5458 parcount = mp->par->nvdwpPars;
5459 parptr = mp->par->vdwpPars;
5461 case kVdwCutoffParType:
5462 parname = "VCUTPAR\0";
5463 parsize = sizeof(VdwCutoffPar);
5464 parcount = mp->par->nvdwCutoffPars;
5465 parptr = mp->par->vdwCutoffPars;
5471 len = 8 + sizeof(Int) + parsize * parcount;
5472 ptr = (char *)realloc(ptr, len_all + len);
5476 memmove(p, parname, 8);
5477 *((Int *)(p + 8)) = parsize * parcount;
5478 p += 8 + sizeof(Int);
5479 memmove(p, parptr, parsize * parcount);
5487 time_t tm = time(NULL);
5488 len = 8 + sizeof(Int) + sizeof(Int);
5489 ptr = (char *)realloc(ptr, len_all + len);
5493 memmove(p, "TIME\0\0\0\0", 8);
5494 *((Int *)(p + 8)) = sizeof(Int);
5495 p += 8 + sizeof(Int);
5496 *((Int *)p) = (Int)tm;
5502 if (outLength != NULL)
5503 *outLength = len_all;
5507 Panic("Low memory while serializing a molecule data");
5508 return NULL; /* Not reached */
5511 #pragma mark ====== Search for bonds, angles, dihedrals, impropers ======
5514 sMoleculeSearchIncludingAtoms(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5518 IntGroup *gp = NULL;
5519 if (atomgroup == NULL)
5521 for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5522 for (j = 0; j < nsize; j++) {
5523 if (IntGroupLookup(atomgroup, ip[j], NULL) != 0) {
5526 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5527 Panic("Low memory while searching %s", msg);
5536 MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5540 return sMoleculeSearchIncludingAtoms(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5544 MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5548 return sMoleculeSearchIncludingAtoms(mp->nangles, mp->angles, 3, atomgroup, "angles");
5552 MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5556 return sMoleculeSearchIncludingAtoms(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5560 MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5564 return sMoleculeSearchIncludingAtoms(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5568 sMoleculeSearchAcrossAtomGroup(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5572 IntGroup *gp = NULL;
5573 if (atomgroup == NULL)
5575 for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5577 for (j = 0; j < nsize; j++) {
5579 kk = (IntGroupLookup(atomgroup, ip[j], NULL) != 0);
5583 /* This bond etc. crosses the atom group border */
5586 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5587 Panic("Low memory while searching %s", msg);
5596 MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5600 return sMoleculeSearchAcrossAtomGroup(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5604 MoleculeSearchAnglesAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5608 return sMoleculeSearchAcrossAtomGroup(mp->nangles, mp->angles, 3, atomgroup, "angles");
5612 MoleculeSearchDihedralsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5616 return sMoleculeSearchAcrossAtomGroup(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5620 MoleculeSearchImpropersAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5624 return sMoleculeSearchAcrossAtomGroup(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5627 /* Subroutine for MoleculeGuessBonds. It can be also used independently, but make sure that *outNbonds/*outBonds
5628 _correctly_ represents an array of two integers (as in mp->nbonds/mp->bonds). */
5629 /* Find atoms within the given "distance" from the given position. */
5630 /* If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5631 the threshold distance is given by the sum of van der Waals radii times limit, and radius is
5632 the van der Waals radius of the atom at the given position. */
5633 /* Index is the atom index of the given atom; it is only used in returning the "bond" array
5634 to the caller. If index is negative, then (-index) is the real atom index, and
5635 only atoms with lower indices than (-index) are looked for. */
5637 MoleculeFindCloseAtoms(Molecule *mp, const Vector *vp, Double radius, Double limit, Int *outNbonds, Int **outBonds, Int index)
5639 Int n2, j, nlim, newbond[2];
5643 nlim = index = -index;
5647 for (j = 0; j < nlim; j++) {
5648 Atom *bp = ATOM_AT_INDEX(mp->atoms, j);
5651 n2 = bp->atomicNumber;
5652 if (n2 >= 0 && n2 < gCountElementParameters)
5653 a2 = gElementParameters[n2].radius;
5654 else a2 = gElementParameters[6].radius;
5656 VecSub(dr, *vp, r2);
5660 alim = limit * (radius + a2);
5661 if (VecLength2(dr) < alim * alim) {
5664 /* MoleculeAddBonds(mp, 1, newbonds); */
5665 AssignArray(outBonds, outNbonds, sizeof(Int) * 2, *outNbonds, newbond);
5671 /* Guess the bonds from the coordinates */
5672 /* If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5673 the threshold distance is given by the sum of van der Waals radii times limit. */
5675 MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds)
5677 Int nbonds, *bonds, i, newbond[2];
5681 for (i = 1, ap = ATOM_NEXT(mp->atoms); i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5683 Int an = ap->atomicNumber;
5685 if (an >= 0 && an < gCountElementParameters)
5686 rad = gElementParameters[an].radius;
5687 else rad = gElementParameters[6].radius;
5688 MoleculeFindCloseAtoms(mp, &r, rad, limit, &nbonds, &bonds, -i);
5691 newbond[0] = kInvalidIndex;
5693 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5696 if (outNbonds != NULL)
5697 *outNbonds = nbonds;
5698 if (outBonds != NULL)
5703 /* Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information */
5705 MoleculeRebuildTablesFromConnects(Molecule *mp)
5707 int i, j, k, retval;
5714 if (mp->nbonds == 0) {
5715 for (i = 0; i < mp->natoms; i++) {
5716 ap = ATOM_AT_INDEX(mp->atoms, i);
5717 cp = AtomConnectData(&ap->connect);
5718 for (j = 0; j < ap->connect.count; j++) {
5724 /* MoleculeAddBonds() should not be used, because it assumes connects[] and
5725 bonds are already in sync */
5726 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
5727 /* retval = MoleculeAddBonds(mp, 1, ibuf);
5735 if (mp->nangles == 0) {
5736 for (i = 0; i < mp->natoms; i++) {
5737 ap = ATOM_AT_INDEX(mp->atoms, i);
5738 cp = AtomConnectData(&ap->connect);
5739 for (j = 0; j < ap->connect.count; j++) {
5740 for (k = j + 1; k < ap->connect.count; k++) {
5745 retval = MoleculeAddAngles(mp, ibuf, NULL);
5753 /* Find dihedrals */
5754 if (mp->ndihedrals == 0) {
5755 for (i = 0; i < mp->natoms; i++) {
5756 ap = ATOM_AT_INDEX(mp->atoms, i);
5757 cp = AtomConnectData(&ap->connect);
5758 for (j = 0; j < ap->connect.count; j++) {
5765 apjj = ATOM_AT_INDEX(mp->atoms, jj);
5766 cpjj = AtomConnectData(&apjj->connect);
5767 for (k = 0; k < ap->connect.count; k++) {
5771 for (m = 0; m < apjj->connect.count; m++) {
5773 if (mm == i || mm == kk)
5780 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
5789 /* Find impropers */
5790 if (mp->nimpropers == 0) {
5791 for (i = 0; i < mp->natoms; i++) {
5792 int i1, i2, i4, n1, n2, n4;
5793 ap = ATOM_AT_INDEX(mp->atoms, i);
5794 cp = AtomConnectData(&ap->connect);
5795 for (i1 = 0; i1 < ap->connect.count; i1++) {
5797 for (i2 = i1 + 1; i2 < ap->connect.count; i2++) {
5799 for (i4 = i2 + 1; i4 < ap->connect.count; i4++) {
5806 retval = MoleculeAddImpropers(mp, ibuf, NULL);
5815 mp->needsMDRebuild = 1;
5816 __MoleculeUnlock(mp);
5820 __MoleculeUnlock(mp);
5825 MoleculeAreAtomsConnected(Molecule *mol, int idx1, int idx2)
5827 Atom *ap1 = ATOM_AT_INDEX(mol->atoms, idx1);
5828 if (AtomConnectHasEntry(&ap1->connect, idx2))
5830 else if (ap1->anchor != NULL && AtomConnectHasEntry(&(ap1->anchor->connect), idx2))
5835 #pragma mark ====== Atom names ======
5837 /* Look for the n1-th atom in resno-th residue (n1 is 0-based) */
5839 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
5843 if (mp == NULL || mp->natoms == 0)
5846 for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5847 if (ap->resSeq == resno) {
5854 return lasti; /* max */
5859 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
5863 n = strtol(s, &p, 0);
5875 if ((p = strchr(s, ':')) != NULL) {
5876 /* Residue is specified */
5878 if ((pp = strchr(s, '.')) != NULL && pp < p) {
5879 /* Residue number is also specified */
5882 *resSeq = strtol(pp + 1, &ppp, 0);
5884 return -2; /* Bad format */
5885 while (isspace(*ppp))
5888 return -2; /* Bad format */
5891 /* Check whether the "residue name" is an integer */
5892 n = strtol(s, &pp, 0);
5894 while (isspace(*pp))
5896 if (*pp == 0 || *pp == ':') {
5899 return -2; /* Bad format */
5907 if (n >= sizeof(resName))
5908 n = sizeof(resName) - 1;
5909 strncpy(resName, s, n);
5917 strncpy(atomName, p, 4);
5922 /* Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer */
5924 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
5931 n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
5932 if (atomName[0] == 0) {
5933 if (n >= mp->natoms)
5934 n = -1; /* Out of range */
5937 for (n = 0; n < mp->natoms; n++) {
5938 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
5939 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
5940 && (resSeq < 0 || ap->resSeq == resSeq)
5941 && strncmp(atomName, ap->aname, 4) == 0) {
5945 return -1; /* Not found */
5949 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
5953 if (mp == NULL || index < 0 || index >= mp->natoms) {
5957 ap = mp->atoms + index;
5958 if (ap->resSeq != 0) {
5959 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
5963 snprintf(buf, bufsize, "%.4s", ap->aname);
5966 #pragma mark ====== Selection ======
5969 sMoleculeNotifyChangeSelection(Molecule *mp)
5971 /* TODO: Finer control of notification types may be necessary */
5972 MoleculeCallback_notifyModification(mp, 0);
5976 MoleculeSetSelection(Molecule *mp, IntGroup *select)
5981 IntGroupRetain(select);
5982 if (mp->selection != NULL)
5983 IntGroupRelease(mp->selection);
5984 mp->selection = select;
5985 sMoleculeNotifyChangeSelection(mp);
5989 MoleculeGetSelection(Molecule *mp)
5993 else return mp->selection;
5997 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
5999 if (mp->selection == NULL)
6000 mp->selection = IntGroupNew();
6002 IntGroupClear(mp->selection);
6003 IntGroupAdd(mp->selection, n1, 1);
6004 sMoleculeNotifyChangeSelection(mp);
6008 MoleculeUnselectAtom(Molecule *mp, int n1)
6010 if (mp->selection != NULL)
6011 IntGroupRemove(mp->selection, n1, 1);
6012 sMoleculeNotifyChangeSelection(mp);
6016 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
6018 if (mp->selection == NULL)
6019 mp->selection = IntGroupNew();
6020 IntGroupReverse(mp->selection, n1, 1);
6021 sMoleculeNotifyChangeSelection(mp);
6025 MoleculeIsAtomSelected(Molecule *mp, int n1)
6027 if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
6033 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
6035 if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
6041 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
6044 IntGroup *remain, *ig1, *ig2;
6046 remain = IntGroupNewFromIntGroup(remove);
6050 status = IntGroupReverse(remain, 0, mp->natoms);
6052 ig1 = IntGroupNew();
6056 status = IntGroupDifference(selection, remove, ig1);
6059 ig2 = IntGroupNew();
6063 status = IntGroupDeconvolute(ig1, remain, ig2);
6066 IntGroupRelease(remain);
6068 IntGroupRelease(ig1);
6073 IntGroupRelease(ig2);
6078 #pragma mark ====== Atom Equivalence ======
6082 struct sEqList *next;
6083 struct sEqList *link;
6086 static struct sEqList *sListBase = NULL;
6087 static struct sEqList *sListFree = NULL;
6089 static struct sEqList *
6093 if (sListFree != NULL) {
6095 sListFree = lp->next;
6096 lp->i[0] = lp->i[1] = 0;
6100 lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
6101 lp->link = sListBase;
6107 sFreeEqList(struct sEqList *list)
6109 list->next = sListFree;
6114 sDeallocateEqLists(void)
6116 struct sEqList *lp, *lp_link;
6117 for (lp = sListBase; lp != NULL; lp = lp_link) {
6126 sExistInEqList(int i, int idx, struct sEqList *list)
6128 while (list != NULL) {
6129 if (list->i[idx] == i)
6136 static struct sEqList *
6137 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
6140 struct sEqList *list1, *list2;
6141 Int ii, jj, ni, nj, *cpi, *cpj;
6142 api = ATOM_AT_INDEX(mol->atoms, i);
6143 apj = ATOM_AT_INDEX(mol->atoms, j);
6144 if (api->atomicNumber != apj->atomicNumber)
6146 list1 = sAllocEqList();
6152 if (i == j || (db[i] != NULL && db[i] == db[j]))
6154 cpi = AtomConnectData(&api->connect);
6155 cpj = AtomConnectData(&apj->connect);
6156 for (ni = 0; ni < api->connect.count; ni++) {
6158 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
6160 if (sExistInEqList(ii, 0, list1))
6163 for (nj = 0; nj < apj->connect.count; nj++) {
6165 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6167 if (sExistInEqList(jj, 1, list1))
6169 list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
6173 if (list2 == NULL) {
6175 return NULL; /* No equivalent to ii */
6177 list1 = list2; /* ii is OK, try next */
6183 sDBInclude(Int *ip, int i)
6188 for (j = ip[0] - 1; j >= 0; j--) {
6196 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
6198 Int **db; /* List of equivalents for each atom */
6200 Atom *api, *apj, *apk;
6201 Int *cpi, *cpj, *ibuf, nibuf;
6202 int i, j, k, ii, jj, kk;
6203 if (mol == NULL || mol->natoms == 0)
6205 db = (Int **)calloc(sizeof(Int *), mol->natoms);
6209 /* Find the equivalent univalent atoms */
6210 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6211 if (api->connect.count < 2)
6213 cpi = AtomConnectData(&api->connect);
6214 for (j = 0; j < api->connect.count; j++) {
6218 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6220 AssignArray(&ibuf, &nibuf, sizeof(Int), n, &jj);
6222 apj = ATOM_AT_INDEX(mol->atoms, jj);
6223 if (apj->connect.count != 1 || db[jj] != NULL)
6225 cpj = AtomConnectData(&apj->connect);
6226 for (k = j + 1; k < api->connect.count; k++) {
6228 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
6230 apk = ATOM_AT_INDEX(mol->atoms, kk);
6231 if (apk->connect.count != 1 || db[kk] != NULL)
6233 if (apj->atomicNumber == apk->atomicNumber) {
6234 AssignArray(&ibuf, &nibuf, sizeof(Int), n, &kk);
6239 ip = (Int *)calloc(sizeof(Int), n + 1);
6243 memmove(ip + 1, ibuf, sizeof(Int) * n);
6244 for (k = 0; k < n; k++)
6254 /* Try matching (i,j) pair */
6255 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6256 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
6258 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
6259 struct sEqList *list;
6260 if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
6262 if (api->atomicNumber != apj->atomicNumber)
6263 continue; /* Different elements do not match */
6264 if (db[i] != NULL && db[i] == db[j])
6265 continue; /* Already equivalent */
6266 list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
6268 continue; /* (i,j) do not match */
6269 while (list != NULL) {
6272 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
6273 /* Merge db[ii] and db[jj] */
6274 k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
6275 ip = (Int *)calloc(sizeof(Int), k + 1);
6277 return NULL; /* Out of memory */
6278 if (db[ii] == NULL) {
6282 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
6285 if (db[jj] == NULL) {
6288 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
6297 for (k = 0; k < ip[0]; k++)
6301 printf("(%d,%d) matched: ", ii, jj);
6302 for (k = 0; k < ip[0]; k++) {
6303 printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
6313 /* Record the equivalent atoms with the lowest index for each atom */
6314 result = (Int *)calloc(sizeof(Int), mol->natoms);
6315 for (i = 0; i < mol->natoms; i++)
6317 for (i = 0; i < mol->natoms; i++) {
6318 if (result[i] >= 0 || (ip = db[i]) == NULL)
6321 for (j = 0; j < ip[0]; j++) {
6326 for (j = 0; j < ip[0]; j++) {
6327 result[ip[j + 1]] = k;
6328 db[ip[j + 1]] = NULL;
6332 sDeallocateEqLists();
6336 #pragma mark ====== Symmetry expansion ======
6339 MoleculeGetTransformForSymop(Molecule *mp, Symop symop, Transform *tf, int is_cartesian)
6342 if (mp == NULL || mp->cell == NULL)
6344 if (symop.sym >= mp->nsyms && symop.sym != 0)
6346 memmove(*tf, SYMMETRY_AT_INDEX(mp->syms, symop.sym), sizeof(Transform));
6347 (*tf)[9] += symop.dx;
6348 (*tf)[10] += symop.dy;
6349 (*tf)[11] += symop.dz;
6351 TransformMul(t, *tf, mp->cell->rtr);
6352 TransformMul(*tf, mp->cell->tr, t);
6358 MoleculeGetSymopForTransform(Molecule *mp, const Transform tf, Symop *symop, int is_cartesian)
6362 if (mp == NULL || mp->cell == NULL)
6365 TransformMul(t, tf, mp->cell->tr);
6366 TransformMul(t, mp->cell->rtr, t);
6368 memmove(t, tf, sizeof(Transform));
6370 for (i = 0; i < mp->nsyms || i == 0; i++) {
6371 Transform *tp = &(SYMMETRY_AT_INDEX(mp->syms, i));
6372 for (j = 0; j < 9; j++) {
6373 if (fabs((*tp)[j] - t[j]) > 1e-4)
6377 for (j = 9; j < 12; j++) {
6378 double f1 = t[j] - (*tp)[j];
6379 double f2 = floor(f1 + 0.5);
6380 if (fabs(f1 - f2) > 1e-4)
6390 symop->alive = (SYMOP_ALIVE((*symop)) != 0);
6395 return -3; /* Not found */
6399 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
6403 if (symop.sym >= mp->nsyms && symop.sym != 0)
6405 if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
6406 TransformVec(vpout, mp->cell->rtr, vpin);
6407 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpout);
6408 vpout->x += symop.dx;
6409 vpout->y += symop.dy;
6410 vpout->z += symop.dz;
6411 TransformVec(vpout, mp->cell->tr, vpout);
6413 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpin);
6414 vpout->x += symop.dx;
6415 vpout->y += symop.dy;
6416 vpout->z += symop.dz;
6421 /* Add expanded atoms. Returns the number of newly created atoms.
6422 If indices is non-NULL, it should be an array of Int with at least
6423 IntGroupGetCount(group) entries, and on return it contains the
6424 indices of the expanded atoms (may be existing atoms if the expanded
6425 atoms are already present)
6426 If allowOverlap is non-zero, then the new atom is created even when the
6427 coordinates coincide with the some other atom (special position) of the
6428 same element; otherwise, such atom will not be created and the existing
6429 atom is returned in indices[]. */
6431 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group, Int *indices, Int allowOverlap)
6433 int i, n, n0, n1, n2, base, count, *table;
6435 IntGroupIterator iter;
6441 if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
6443 if (symop.sym != 0 && symop.sym >= mp->nsyms)
6446 /* Create atoms, with avoiding duplicates */
6447 n0 = n1 = mp->natoms;
6448 table = (int *)malloc(sizeof(int) * n0);
6451 for (i = 0; i < n0; i++)
6453 IntGroupIteratorInit(group, &iter);
6454 MoleculeGetTransformForSymop(mp, symop, &tr, 0);
6456 for (i = 0; i < count; i++) {
6457 n = IntGroupIteratorNext(&iter);
6458 ap = ATOM_AT_INDEX(mp->atoms, n);
6459 if (SYMOP_ALIVE(ap->symop)) {
6460 /* Calculate the cumulative symop */
6462 MoleculeGetTransformForSymop(mp, ap->symop, &t1, 0);
6463 TransformMul(tr2, tr, t1);
6464 if (MoleculeGetSymopForTransform(mp, tr2, &symop1, 0) != 0) {
6465 if (indices != NULL)
6467 continue; /* Skip this atom */
6475 /* Calculate the expande position */
6476 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
6478 /* Is this expansion already present? */
6479 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
6480 /* Symmetry operation and the base atom are the same */
6481 if (ap2->symbase == base && SYMOP_EQUAL(symop1, ap2->symop))
6483 /* Atomic number and the position are the same */
6484 if (ap2->atomicNumber == ap->atomicNumber && allowOverlap == 0) {
6485 VecSub(dr, ap2->r, nr);
6486 if (VecLength2(dr) < 1e-6)
6491 /* If yes, then skip it */
6492 if (indices != NULL)
6496 /* Create a new atom */
6498 AtomDuplicate(&newAtom, ap);
6499 MoleculeCreateAnAtom(mp, &newAtom, -1);
6500 AtomClean(&newAtom);
6501 ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
6503 ap2->symbase = base;
6504 ap2->symop = symop1;
6505 ap2->symop.alive = (symop1.dx != 0 || symop1.dy != 0 || symop1.dz != 0 || symop1.sym != 0);
6506 table[n] = n1; /* The index of the new atom */
6507 MoleculeSetAnisoBySymop(mp, n1); /* Recalculate anisotropic parameters according to symop */
6508 if (indices != NULL)
6513 IntGroupIteratorRelease(&iter);
6516 for (i = n0; i < n1; i++) {
6518 ap = ATOM_AT_INDEX(mp->atoms, i);
6519 if (SYMOP_ALIVE(ap->symop) && MoleculeGetTransformForSymop(mp, ap->symop, &tr, 1) == 0) {
6520 /* For each connected atom, look for the transformed atom */
6522 ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6523 cp = AtomConnectData(&ap2->connect);
6524 n2 = ap2->connect.count;
6525 for (n = 0; n < n2; n++) {
6526 Atom *apn = ATOM_AT_INDEX(mp->atoms, cp[n]);
6528 TransformVec(&nr, tr, &nr);
6529 /* Look for the bonded atom transformed by ap->symop */
6530 for (j = 0, ap2 = mp->atoms; j < mp->natoms; j++, ap2 = ATOM_NEXT(ap2)) {
6531 if (ap2->symbase == cp[n] && SYMOP_EQUAL(ap->symop, ap2->symop))
6533 VecSub(dr, nr, ap2->r);
6534 if (ap2->atomicNumber == apn->atomicNumber && VecLength2(dr) < 1e-6)
6537 if (j < mp->natoms) {
6538 /* Bond i-j is created */
6541 if (MoleculeLookupBond(mp, b[0], b[1]) < 0)
6542 MoleculeAddBonds(mp, 1, b, NULL, 1);
6547 mp->needsMDRebuild = 1;
6548 __MoleculeUnlock(mp);
6550 return n1 - n0; /* The number of added atoms */
6553 /* Recalculate the coordinates of symmetry expanded atoms.
6554 (Also recalculate the positions of pi-anchor atoms)
6555 Returns the number of affected atoms.
6556 If group is non-NULL, only the expanded atoms whose base atoms are in the
6557 given group are considered.
6558 If groupout and vpout are non-NULL, the indices of the affected atoms
6559 and the original positions are returned (for undo operation).
6560 The pointers returned in *groupout and *vpout must be released and
6561 free()'ed by the caller */
6563 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
6568 IntGroup *ig = NULL;
6571 if (mp == NULL || mp->natoms == 0)
6576 if (mp->nsyms != 0) {
6577 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6578 if (!SYMOP_ALIVE(ap->symop))
6580 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
6582 bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6583 MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
6584 VecSub(dr, nr, ap->r);
6585 if (VecLength2(dr) < 1e-20)
6587 if (groupout != NULL) {
6590 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
6593 IntGroupAdd(ig, i, 1);
6599 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6601 if (ap->anchor == NULL)
6603 if (group != NULL) {
6604 if (IntGroupLookup(group, i, NULL) == 0) {
6605 n = ap->anchor->connect.count;
6606 ip = AtomConnectData(&(ap->anchor->connect));
6607 for (j = 0; j < n; j++) {
6608 if (IntGroupLookup(group, ip[j], NULL) != 0)
6612 continue; /* This pi-anchor should not be modified */
6616 MoleculeCalculatePiAnchorPosition(mp, i);
6617 VecSub(dr, nr, ap->r);
6618 if (VecLength2(dr) < 1e-20) {
6619 ap->r = nr; /* No change */
6622 if (groupout != NULL) {
6625 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
6628 IntGroupAdd(ig, i, 1);
6632 mp->needsMDCopyCoordinates = 1;
6633 __MoleculeUnlock(mp);
6636 if (groupout != NULL && vpout != NULL) {
6638 *vpout = (Vector *)realloc(vp, sizeof(Vector) * count);
6640 IntGroupRelease(ig);
6644 if (groupout != NULL && vpout != NULL) {
6652 #pragma mark ====== Show/hide atoms ======
6655 sMoleculeNotifyChangeAppearance(Molecule *mp)
6657 /* TODO: Finer control of notification types may be necessary */
6658 MoleculeCallback_notifyModification(mp, 0);
6663 sMoleculeUnselectHiddenAtoms(Molecule *mp)
6666 if (mp == NULL || mp->selection == NULL)
6668 for (i = 0; i < mp->natoms; i++) {
6669 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6670 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
6671 IntGroupRemove(mp->selection, i, 1);
6673 sMoleculeNotifyChangeAppearance(mp);
6677 MoleculeShowAllAtoms(Molecule *mp)
6682 for (i = 0; i < mp->natoms; i++) {
6683 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6684 ap->exflags &= ~kAtomHiddenFlag;
6686 sMoleculeNotifyChangeAppearance(mp);
6691 MoleculeShowReverse(Molecule *mp)
6696 for (i = 0; i < mp->natoms; i++) {
6697 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6698 ap->exflags ^= kAtomHiddenFlag;
6700 sMoleculeUnselectHiddenAtoms(mp);
6701 sMoleculeNotifyChangeAppearance(mp);
6706 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
6709 if (mp == NULL || ig == NULL)
6711 for (i = 0; i < mp->natoms; i++) {
6712 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6713 if (ap->exflags & kAtomHiddenFlag)
6714 continue; /* Already hidden */
6715 if (IntGroupLookupPoint(ig, i) >= 0)
6716 ap->exflags |= kAtomHiddenFlag;
6718 sMoleculeUnselectHiddenAtoms(mp);
6719 sMoleculeNotifyChangeAppearance(mp);
6723 #pragma mark ====== Reversible Editing ======
6727 sMoleculeNotifyModification(Molecule *mp)
6729 ** TODO: Finer control of notification types may be necessary **
6730 MoleculeCallback_notifyModification(mp, 0);
6734 /* Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup */
6736 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
6739 if (where == NULL) {
6740 /* Append the new objects at the end */
6741 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
6744 n1 = IntGroupGetCount(where); /* Position to get new object */
6745 n2 = nobjs; /* Position to get old object */
6746 n3 = n1 + n2; /* Position to place new/old object */
6747 for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
6748 int start = IntGroupGetStartPoint(where, i);
6749 int end = IntGroupGetEndPoint(where, i);
6751 /* old[end-(n3-n2)..n2-1] is moved to old[end..n3-1] */
6752 memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
6753 n2 = end - (n3 - n2);
6756 /* new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1] */
6757 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
6764 /* Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
6766 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6768 int n1, n2, n3, start, end, i;
6769 if (objs == NULL || where == NULL)
6770 return 1; /* Bad argument */
6771 n1 = 0; /* Position to move remaining elements to */
6772 n2 = 0; /* Position to move remaining elements from */
6773 n3 = 0; /* Position to move removed elements to */
6774 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6775 end = IntGroupGetEndPoint(where, i);
6777 /* Move (start - n2) elements from objs[n2] to objs[n1] */
6779 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
6783 /* Move (end - start) elements from objs[n2] to clip[n3] */
6785 memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
6786 n3 += (end - start);
6787 n2 += (end - start);
6789 /* Move (nobjs - n2) elements from objs[n2] to objs[n1] */
6791 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
6795 /* Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
6797 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6799 int n1, start, end, i;
6800 if (objs == NULL || where == NULL)
6801 return 1; /* Bad argument */
6802 n1 = 0; /* Position to move removed elements to */
6803 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6804 end = IntGroupGetEndPoint(where, i);
6805 /* Copy (end - start) elements from objs[start] to clip[n1] */
6807 memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
6808 n1 += (end - start);
6813 /* Create a new atom with no bonding information. ap must _not_ be inside the given molecule
6814 (Use AtomDuplicate() first) */
6816 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
6820 if (mp == NULL || ap == NULL || mp->noModifyTopology)
6823 if (pos < 0 || pos >= mp->natoms)
6825 ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
6827 goto error; /* Out of memory */
6828 ap1 = ATOM_AT_INDEX(mp->atoms, pos);
6829 if (pos < mp->natoms - 1) {
6830 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6832 if (AtomDuplicate(ap1, ap) == NULL) {
6833 /* Cannot duplicate: restore the original state */
6834 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6838 ap1->connect.count = 0;
6839 if (ap1->resSeq >= mp->nresidues)
6840 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
6841 if (ap1->resName[0] == 0)
6842 strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
6843 if (ap1->segName[0] == 0)
6844 strncpy(ap1->segName, "MAIN", 4);
6845 if (pos < mp->natoms - 1) {
6846 /* Renumber the connect table, bonds, angles, etc. */
6847 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
6850 cp = AtomConnectData(&api->connect);
6851 for (j = 0; j < api->connect.count; j++) {
6855 if (api->anchor != NULL) {
6856 cp = AtomConnectData(&api->anchor->connect);
6857 for (j = 0; j < api->anchor->connect.count; j++) {
6863 for (i = 0; i < mp->nbonds * 2; i++) {
6864 if (mp->bonds[i] >= pos)
6867 for (i = 0; i < mp->nangles * 3; i++) {
6868 if (mp->angles[i] >= pos)
6871 for (i = 0; i < mp->ndihedrals * 4; i++) {
6872 if (mp->dihedrals[i] >= pos)
6875 for (i = 0; i < mp->nimpropers * 4; i++) {
6876 if (mp->impropers[i] >= pos)
6880 mp->nframes = -1; /* Should be recalculated later */
6881 MoleculeIncrementModifyCount(mp);
6882 mp->needsMDRebuild = 1;
6883 __MoleculeUnlock(mp);
6886 __MoleculeUnlock(mp);
6892 static int s_error_count;
6895 s_fprintf(FILE *fp, const char *fmt, ...)
6900 return vfprintf(fp, fmt, va);
6904 MoleculeCheckSanity(Molecule *mol)
6906 const char *fail = "Sanity check failure";
6907 Int i, j, *ip, c[4];
6910 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
6911 if (ap->resSeq >= mol->nresidues)
6912 s_fprintf(stderr, "%s: atom %d residue %d but nresidues %d\n", fail, i, ap->resSeq, mol->nresidues);
6913 if (ap->type != 0 && ap->type < kAtomTypeMinimum)
6914 s_fprintf(stderr, "%s: atom %d atom type %d less than minimum\n", fail, i, ap->type);
6915 if (ap->atomicNumber < 0 || ap->atomicNumber > 113)
6916 s_fprintf(stderr, "%s: atom %d atomic number %d\n", fail, i, ap->atomicNumber);
6917 ip = AtomConnectData(&ap->connect);
6918 for (j = 0; j < ap->connect.count; j++) {
6919 if (ip[j] < 0 || ip[j] >= mol->natoms)
6920 s_fprintf(stderr, "%s: atom %d connect[%d] = %d out of range\n", fail, i, j, ip[j]);
6921 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[j])->connect), i) == 0)
6922 s_fprintf(stderr, "%s: atom %d has connect %d but atom %d has no connect %d\n", fail, i, ip[j], ip[j], i);
6925 for (i = 0, ip = mol->bonds; i < mol->nbonds; i++, ip += 2) {
6926 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms)
6927 s_fprintf(stderr, "%s: bond %d %d-%d out of range\n", fail, i, ip[0], ip[1]);
6928 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[0])->connect), ip[1]) == 0)
6929 s_fprintf(stderr, "%s: bond %d %d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[0], ip[1]);
6931 for (i = 0, ip = mol->angles; i < mol->nangles; i++, ip += 3) {
6932 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms)
6933 s_fprintf(stderr, "%s: angle %d %d-%d-%d out of range\n", fail, i, ip[0], ip[1], ip[2]);
6934 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
6936 s_fprintf(stderr, "%s: angle %d %d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[1], ip[0]);
6937 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
6939 s_fprintf(stderr, "%s: angle %d %d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[1], ip[2]);
6940 if (c[0] == 2 && c[1] == 2)
6941 s_fprintf(stderr, "%s: angle %d %d-%d-%d but bonds %d-%d and %d-%d are both virtual\n", fail, i, ip[0], ip[1], ip[2], ip[1], ip[0], ip[1], ip[2]);
6943 for (i = 0, ip = mol->dihedrals; i < mol->ndihedrals; i++, ip += 4) {
6944 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms || ip[3] < 0 || ip[3] >= mol->natoms)
6945 s_fprintf(stderr, "%s: dihedral %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
6946 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
6947 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
6948 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
6950 s_fprintf(stderr, "%s: dihedral %d %d-%d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[3], ip[1], ip[0]);
6952 s_fprintf(stderr, "%s: dihedral %d %d-%d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[3], ip[1], ip[2]);
6954 s_fprintf(stderr, "%s: dihedral %d %d-%d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[3], ip[2], ip[3]);
6956 for (i = 0, ip = mol->impropers; i < mol->nimpropers; i++, ip += 4) {
6957 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms || ip[3] < 0 || ip[3] >= mol->natoms)
6958 s_fprintf(stderr, "%s: improper %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
6959 c[0] = MoleculeAreAtomsConnected(mol, ip[2], ip[0]);
6960 c[1] = MoleculeAreAtomsConnected(mol, ip[2], ip[1]);
6961 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
6963 s_fprintf(stderr, "%s: improper %d %d-%d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[3], ip[2], ip[0]);
6965 s_fprintf(stderr, "%s: improper %d %d-%d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[3], ip[2], ip[1]);
6967 s_fprintf(stderr, "%s: improper %d %d-%d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[3], ip[2], ip[3]);
6969 return s_error_count;
6973 /* Merge two molecules. We use this procedure for all add-atom operations. */
6974 /* resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
6975 /* If nactions and actions are non-NULL, then the corresponding undo actions are created and returned. */
6976 /* If forUndo is non-zero, then only the atoms are inserted; other information should be inserted
6977 separately by other undo actions. */
6979 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, Int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
6982 Int i, j, n1, n2, n3, n4, *cp;
6983 Int *new2old, *old2new;
6988 if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
6989 return 0; /* Do nothing */
6991 if (dst->noModifyTopology)
6992 return 1; /* Prohibited operation */
6994 if (where != NULL && IntGroupGetCount(where) != src->natoms)
6995 return 1; /* Bad parameter */
6997 if (nactions != NULL)
6999 if (actions != NULL)
7003 __MoleculeLock(dst);
7007 if (resSeqOffset < 0)
7010 /* Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
7011 and ndst..ndst+nsrc-1 are for atoms in src. */
7012 new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
7013 if (new2old == NULL)
7015 old2new = new2old + ndst + nsrc;
7016 n1 = 0; /* dst index */
7017 n2 = 0; /* src index */
7018 n3 = 0; /* "merged" index */
7020 while (n1 < ndst || n2 < nsrc) {
7021 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
7024 /* n4 elements from dst[n1] will go to merged[n3] */
7025 for (j = 0; j < n4; j++) {
7026 old2new[n1 + j] = n3 + j;
7027 new2old[n3 + j] = n1 + j;
7031 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
7033 /* n4 elements from src[n2] will go to merged[n3] */
7034 for (j = 0; j < n4; j++) {
7035 old2new[ndst + n2 + j] = n3 + j;
7036 new2old[n3 + j] = ndst + n2 + j;
7043 /* Expand the destination array */
7044 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
7047 /* Move the atoms */
7048 if (where == NULL) {
7049 /* Duplicate atoms to the end of the destination array */
7050 for (i = 0; i < nsrc; i++) {
7051 ap = ATOM_AT_INDEX(dst->atoms, ndst + i);
7052 if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7054 if (forUndo) /* For undo action, all bonds come from another undo action, so connection info are cleared */
7055 AtomConnectResize(&ap->connect, 0);
7058 /* Duplicate to a temporary storage and then insert */
7059 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
7060 if (tempatoms == NULL)
7062 for (i = 0; i < nsrc; i++) {
7063 ap = ATOM_AT_INDEX(tempatoms, i);
7064 if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7066 if (forUndo) /* See above */
7067 AtomConnectResize(&ap->connect, 0);
7069 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
7073 dst->natoms = ndst + nsrc;
7075 /* Renumber the atom indices in connect[] and symbase, and modify the residue numbers */
7076 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7077 if (new2old[i] < ndst) {
7078 /* This atom is from dst */
7081 /* This atom is from src */
7082 n1 = ndst; /* Offset to the internal number */
7083 if (ap->resSeq != 0)
7084 ap->resSeq += resSeqOffset; /* Modify residue number */
7086 cp = AtomConnectData(&ap->connect);
7087 for (j = 0; j < ap->connect.count; j++)
7088 cp[j] = old2new[cp[j] + n1];
7089 if (SYMOP_ALIVE(ap->symop))
7090 ap->symbase = old2new[ap->symbase + n1];
7091 if (ap->anchor != NULL) {
7092 cp = AtomConnectData(&ap->anchor->connect);
7093 for (j = 0; j < ap->anchor->connect.count; j++)
7094 cp[j] = old2new[cp[j] + n1];
7098 /* Move the bonds, angles, dihedrals, impropers */
7099 for (i = 0; i < 4; i++) {
7100 Int *nitems, *nitems_src;
7101 Int **items, **items_src;
7102 Int nsize; /* Number of Ints in one element */
7105 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
7107 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
7109 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
7111 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
7113 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
7114 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
7116 /* During undo, no bonds etc. are copied from src; they will be taken care later
7121 /* Keep the old number of entries in dst, because it is updated by AssignArray() */
7123 /* Also keep the old number of entries in src, in case src and dst point the same molecule */
7125 /* Expand the array */
7126 if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
7128 /* Copy the items */
7129 memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
7131 /* Copy the bond order info if present */
7132 Int nn1 = dst->nbondOrders;
7133 if (dst->bondOrders != NULL || src->bondOrders != NULL) {
7134 if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), dst->nbonds - 1, NULL) == NULL)
7136 memset(dst->bondOrders + nn1, 0, sizeof(Double) * (dst->nbonds - nn1));
7137 if (src->bondOrders != NULL)
7138 memmove(dst->bondOrders + n1, src->bondOrders, sizeof(Double) * n2);
7143 for (j = 0; j < n1 * nsize; j++)
7144 (*items)[j] = old2new[(*items)[j]];
7145 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
7146 (*items)[j] = old2new[(*items)[j] + ndst];
7147 if (forUndo == 0 && actions != NULL) {
7148 ig = IntGroupNewWithPoints(n1, n2, -1);
7150 case 0: act = MolActionNew(gMolActionDeleteBonds, ig); break;
7151 case 1: act = MolActionNew(gMolActionDeleteAngles, ig); break;
7152 case 2: act = MolActionNew(gMolActionDeleteDihedrals, ig); break;
7153 case 3: act = MolActionNew(gMolActionDeleteImpropers, ig); break;
7155 IntGroupRelease(ig);
7156 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7161 /* Renumber existing parameters */
7162 if (dst->par != NULL) {
7164 for (type = kFirstParType; type <= kLastParType; type++) {
7166 n1 = ParameterGetCountForType(dst->par, type);
7167 for (i = 0; i < n1; i++) {
7168 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7169 ParameterRenumberAtoms(type, up1, ndst, old2new);
7174 /* Merge parameters from src */
7175 if (src->par != NULL && forUndo == 0) {
7176 UnionPar *up1, *up2;
7178 if (dst->par == NULL)
7179 dst->par = ParameterNew();
7181 /* Renumber existing parameters */
7182 for (type = kFirstParType; type <= kLastParType; type++) {
7183 n1 = ParameterGetCountForType(dst->par, type);
7184 for (i = 0; i < n1; i++) {
7185 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7186 ParameterRenumberAtoms(type, up1, ndst, old2new);
7191 for (type = kFirstParType; type <= kLastParType; type++) {
7192 n1 = ParameterGetCountForType(src->par, type);
7193 n2 = ParameterGetCountForType(dst->par, type);
7196 /* Determine which parameter should be copied from src to dst */
7197 for (i = 0; i < n1; i++) {
7199 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
7200 n3 = ParameterGetAtomTypes(type, up1, types);
7201 for (j = 0; j < n3; j++) {
7202 /* If it includes explicit atom index, then it should be copied */
7203 if (types[j] < kAtomTypeMinimum) {
7204 IntGroupAdd(ig, i, 1);
7209 for (j = 0; j < n2; j++) {
7210 up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
7211 if (ParameterCompare(up1, up2, type))
7215 /* This is an unknown parameter; should be copied */
7216 IntGroupAdd(ig, i, 1);
7219 n1 = IntGroupGetCount(ig);
7222 up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
7225 /* Copy parameters and renumber indices if necessary */
7226 for (i = j = 0; i < n1; i++) {
7227 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
7231 ParameterRenumberAtoms(type, up1 + j, nsrc, old2new + ndst);
7234 /* Merge parameters */
7236 IntGroupAdd(ig, n2, j);
7237 if (ParameterInsert(dst->par, type, up1, ig) < j)
7239 if (actions != NULL) {
7240 act = MolActionNew(gMolActionDeleteParameters, type, ig);
7241 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7247 IntGroupRelease(ig);
7250 /* Copy the residues if necessary */
7251 /* src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
7252 However, 1+resSeqOffset should not overwrite the existing residue in dst;
7253 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1]. */
7255 n1 = dst->nresidues;
7256 if (1 + resSeqOffset < n1) {
7258 } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
7259 if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
7260 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
7262 memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
7263 if (nactions != NULL) {
7264 act = MolActionNew(gMolActionChangeNumberOfResidues, n1);
7265 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7271 MoleculeCleanUpResidueTable(dst);
7274 dst->nframes = -1; /* Should be recalculated later */
7276 MoleculeIncrementModifyCount(dst);
7277 dst->needsMDRebuild = 1;
7278 __MoleculeUnlock(dst);
7282 __MoleculeUnlock(dst);
7283 Panic("Low memory while adding atoms");
7284 return 1; /* Not reached */
7287 /* Unmerge the molecule. If necessary, the undo actions are stored in nactions/actions array.
7288 (The nactions/actions array must be initialized by the caller) */
7290 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag, Int *nactions, MolAction ***actions, Int forUndo)
7292 Int nsrc, ndst, nsrcnew;
7293 Int i, j, n1, n2, n3, n4, *cp;
7294 Int *new2old, *old2new;
7295 IntGroup *move_g, *del_g, *remain_g, *dst_par_g, *remove_par_g;
7301 if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
7308 if (src->noModifyTopology && moveFlag)
7309 return 1; /* Prohibit editing */
7311 if ((ndst = IntGroupGetCount(where)) > src->natoms)
7312 return 1; /* Bad parameter */
7314 __MoleculeLock(src);
7319 nsrcnew = nsrc - ndst;
7320 if (resSeqOffset < 0)
7323 /* Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
7324 and nsrcnew..nsrc-1 are for atoms moved into dst. */
7325 new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
7326 if (new2old == NULL)
7328 old2new = new2old + nsrc;
7329 n1 = 0; /* src index */
7330 n2 = 0; /* dst index */
7331 n3 = 0; /* src index after "unmerge" */
7333 while (n1 < nsrc || n2 < ndst) {
7334 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
7337 /* n4 elements from src[n1] will go to unmerged[n3] */
7338 for (j = 0; j < n4; j++) {
7339 old2new[n1 + j] = n3 + j;
7340 new2old[n3 + j] = n1 + j;
7344 if ((n4 = IntGroupGetInterval(where, i)) < 0)
7346 /* n4 elements from src[n1] will go to dst[n2] */
7347 for (j = 0; j < n4; j++) {
7348 old2new[n1 + j] = nsrcnew + n2 + j;
7349 new2old[nsrcnew + n2 + j] = n1 + j;
7356 /* Atoms to remain in the source group */
7358 remain_g = IntGroupNewWithPoints(0, nsrc, -1);
7359 IntGroupRemoveIntGroup(remain_g, where);
7360 } else remain_g = NULL;
7362 /* Find parameters to be moved to the dst (dst_par_g), and to be removed from the src (remove_par_g) */
7363 if (src->par != NULL) {
7364 dst_par_g = IntGroupNew();
7366 remove_par_g = IntGroupNew();
7367 else remove_par_g = NULL;
7368 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7369 n2 = ParameterGetCountForType(src->par, n1);
7372 for (i = 0; i < n2; i++) {
7373 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7374 if (ParameterIsRelevantToAtomGroup(n1, up, src->atoms, where)) {
7375 /* This parameter is to be copied to dst */
7376 IntGroupAdd(dst_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7378 if (moveFlag && !ParameterIsRelevantToAtomGroup(n1, up, src->atoms, remain_g)) {
7379 /* This parameter is to be removed */
7380 IntGroupAdd(remove_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7384 } else dst_par_g = remove_par_g = NULL;
7386 /* Pi anchors should be modified if the anchor and its component atoms become separated between
7389 Int ibufsize, *ibuf, flag_i, flag_j;
7391 ibuf = (Int *)malloc(sizeof(Int) * ibufsize);
7392 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7393 if (ap->anchor == NULL)
7395 flag_i = (old2new[i] < nsrcnew);
7396 cp = AtomConnectData(&ap->anchor->connect);
7397 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7398 flag_j = (old2new[cp[j]] < nsrcnew);
7399 if (flag_i == flag_j) {
7400 if (n1 >= ibufsize) {
7402 ibuf = (Int *)realloc(ibuf, sizeof(Int) * ibufsize);
7408 /* Need to modify the pi anchor list */
7411 MolActionCreateAndPerform(src, SCRIPT_ACTION("isI"), "set_atom_attr", i, "anchor_list", n1, ibuf);
7416 /* Make a new molecule */
7418 dst = MoleculeNew();
7421 /* Expand the destination array */
7422 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
7424 dst_ap = dst->atoms;
7427 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
7432 /* Move the atoms */
7434 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
7436 src->natoms = nsrcnew;
7438 /* The atom record must be deallocated correctly */
7439 for (i = 0; i < ndst; i++)
7440 AtomClean(ATOM_AT_INDEX(dst_ap, i));
7444 for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
7445 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
7450 /* The dummy destination array is no longer needed */
7455 /* Renumber the atom indices in connect[] (src) */
7457 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7458 cp = AtomConnectData(&ap->connect);
7459 for (j = n1 = 0; j < ap->connect.count; j++) {
7460 n2 = old2new[cp[j]];
7464 AtomConnectResize(&ap->connect, n1);
7465 if (ap->anchor != NULL) {
7466 cp = AtomConnectData(&ap->anchor->connect);
7467 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7468 n2 = old2new[cp[j]];
7472 if (n1 != ap->anchor->connect.count) {
7473 /* This should not happen!! */
7474 AtomConnectResize(&ap->anchor->connect, n1);
7475 fprintf(stderr, "Internal error in sMoleculeUnmergeSub (line %d)\n", __LINE__);
7477 free(ap->anchor->coeffs);
7486 /* Renumber the atom indices in connect[] (dst) */
7488 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7489 if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
7490 ap->resSeq -= resSeqOffset;
7491 else ap->resSeq = 0;
7492 cp = AtomConnectData(&ap->connect);
7493 for (j = n1 = 0; j < ap->connect.count; j++) {
7494 n2 = old2new[cp[j]] - nsrcnew;
7498 AtomConnectResize(&ap->connect, n1);
7499 if (ap->anchor != NULL) {
7500 cp = AtomConnectData(&ap->anchor->connect);
7501 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7502 n2 = old2new[cp[j]] - nsrcnew;
7506 if (n1 != ap->anchor->connect.count) {
7507 /* This can happen, and the anchor info is silently modified */
7509 AtomConnectResize(&ap->anchor->connect, 0);
7510 free(ap->anchor->coeffs);
7515 AtomConnectResize(&ap->anchor->connect, n1);
7517 for (j = 0; j < n1; j++)
7518 d += ap->anchor->coeffs[j];
7519 for (j = 0; j < n1; j++)
7520 ap->anchor->coeffs[j] /= d;
7521 MoleculeCalculatePiAnchorPosition(dst, i);
7528 /* Separate the bonds, angles, dihedrals, impropers */
7529 /* TODO: Improper torsions should also be copied! */
7530 move_g = IntGroupNew();
7533 for (i = 3; i >= 0; i--) {
7534 Int *nitems, *nitems_dst;
7535 Int **items, **items_dst;
7536 Int nsize; /* Number of Ints in one element */
7537 unsigned char *counts;
7538 del_g = IntGroupNew();
7541 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
7543 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
7545 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
7547 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
7549 nitems = NULL; items = NULL; nsize = 0; break; /* Not reached */
7552 nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
7553 items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
7558 counts = (unsigned char *)calloc(1, *nitems);
7559 /* Find the entries that should be moved to dst */
7561 for (j = 0; j < *nitems * nsize; j++) {
7562 n1 = old2new[(*items)[j]];
7564 counts[j / nsize]++; /* Count the atom belonging to dst */
7566 for (j = n2 = n3 = 0; j < *nitems; j++) {
7567 if (counts[j] > 0) {
7568 /* Remove from src */
7570 if (IntGroupAdd(del_g, j, 1) != 0)
7572 if (counts[j] == nsize) {
7575 if (IntGroupAdd(move_g, j, 1) != 0)
7581 /* Expand the destination array */
7582 if (items_dst != NULL && n3 > 0) {
7583 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
7585 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
7587 if (i == 0 && src->bondOrders != NULL) {
7588 if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), n3 - 1, NULL) == NULL)
7590 if (sCopyElementsFromArrayAtPositions(src->bondOrders, src->nbondOrders, dst->bondOrders, sizeof(Double), move_g) != 0)
7594 /* Remove from src */
7595 if (moveFlag && forUndo == 0) {
7596 if (nactions != NULL) {
7599 ip = (Int *)malloc(sizeof(Int) * nsize * n2);
7600 for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
7601 memmove(ip + j * nsize, *items + k * nsize, sizeof(Int) * nsize);
7602 if (i == 0 && src->bondOrders != NULL) {
7603 dp = (Double *)malloc(sizeof(Double) * n2);
7604 for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
7605 dp[j] = src->bondOrders[k];
7609 act = MolActionNew(gMolActionAddBondsForUndo, n2 * nsize, ip, del_g); break;
7611 act = MolActionNew(gMolActionAddAngles, n2 * nsize, ip, del_g); break;
7613 act = MolActionNew(gMolActionAddDihedrals, n2 * nsize, ip, del_g); break;
7615 act = MolActionNew(gMolActionAddImpropers, n2 * nsize, ip, del_g); break;
7618 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7623 act = MolActionNew(gMolActionAssignBondOrders, n2, dp, del_g);
7624 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7629 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
7634 /* Renumber the entries */
7636 for (j = 0; j < *nitems * nsize; j++) {
7637 (*items)[j] = old2new[(*items)[j]];
7640 if (items_dst != NULL) {
7641 for (j = 0; j < *nitems_dst * nsize; j++) {
7642 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
7646 IntGroupClear(move_g);
7647 IntGroupRelease(del_g);
7649 IntGroupRelease(move_g);
7651 /* Copy the residues */
7653 /* src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset) */
7654 n1 = src->nresidues - resSeqOffset; /* This will be dst->nresidues (if >0) */
7655 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
7658 memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
7662 /* Copy the parameters to dst */
7663 if (dst != NULL && dst_par_g != NULL && (n2 = IntGroupGetCount(dst_par_g)) > 0) {
7664 IntGroup *dst_new_g = IntGroupNew();
7665 Int dst_par_count[kLastParType - kFirstParType + 1];
7666 if (dst_new_g == NULL)
7668 for (i = 0; i <= kLastParType - kFirstParType; i++)
7669 dst_par_count[i] = 0;
7670 up = (UnionPar *)calloc(sizeof(UnionPar), n2);
7673 if (ParameterCopy(src->par, kFirstParType, up, dst_par_g) < n2)
7675 /* Renumber the explicit atom indices */
7676 for (i = 0; i < nsrc; i++)
7677 old2new[i] -= nsrcnew; /* new indices for atoms in dst; otherwise negative numbers */
7678 for (i = 0; i < n2; i++) {
7679 /* Renumber the indices, and count the number of parameters for each type */
7680 n1 = kFirstParType + IntGroupGetNthPoint(dst_par_g, i) / kParameterIndexOffset;
7681 dst_par_count[n1 - kFirstParType]++;
7682 ParameterRenumberAtoms(n1, up + i, nsrc, old2new);
7684 for (i = 0; i < nsrc; i++)
7685 old2new[i] += nsrcnew;
7686 if (dst->par == NULL)
7687 dst->par = ParameterNew();
7688 for (i = 0; i <= kLastParType - kFirstParType; i++) {
7689 if (dst_par_count[i] > 0)
7690 IntGroupAdd(dst_new_g, i * kParameterIndexOffset, dst_par_count[i]);
7692 if (ParameterInsert(dst->par, kFirstParType, up, dst_new_g) < n2)
7695 IntGroupRelease(dst_new_g);
7697 IntGroupRelease(dst_par_g);
7699 /* Remove the unused parameter. Note: the parameters that are in remove_par_g and not in
7700 dst_par_g will disappear. To support undo, these parameters should be taken care separately. */
7701 if (forUndo == 0 && remove_par_g != NULL && (n2 = IntGroupGetCount(remove_par_g)) > 0) {
7702 UnionPar *up = (UnionPar *)malloc(sizeof(UnionPar) * n2);
7703 ParameterDelete(src->par, kFirstParType, up, remove_par_g);
7704 if (nactions != NULL) {
7705 act = MolActionNew(gMolActionAddParameters, kFirstParType, remove_par_g, n2, up);
7706 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7711 IntGroupRelease(remove_par_g);
7713 /* Renumber the parameter records remaining in the src */
7715 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7716 n2 = ParameterGetCountForType(src->par, n1);
7717 for (i = 0; i < n2; i++) {
7718 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7719 ParameterRenumberAtoms(n1, up, nsrc, old2new);
7725 IntGroupRelease(remain_g);
7726 MoleculeCleanUpResidueTable(src);
7728 MoleculeCleanUpResidueTable(dst);
7731 src->nframes = -1; /* Should be recalculated later */
7733 dst->nframes = -1; /* Should be recalculated later */
7739 MoleculeIncrementModifyCount(src);
7740 src->needsMDRebuild = 1;
7741 __MoleculeUnlock(src);
7746 __MoleculeUnlock(src);
7747 /* Panic("Low memory while removing atoms"); */
7751 /* Separate molecule into two parts. The atoms specified by 'where' are moved
7752 from src to a new molecule, which is returned as *dstp. Dstp can be NULL,
7753 in which case the moved atoms are discarded. */
7755 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7757 return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1, nactions, actions, forUndo);
7760 /* Extract atoms from a given molecule into two parts. The atoms specified by
7761 'where' are copied from src to a new molecule, which is returned as *dstp.
7762 If dummyFlag is non-zero, then the atoms that are not included in the group
7763 but are connected to any atoms in the group are converted to "dummy" atoms
7764 (i.e. with element "Du" and names beginning with an underscore) and included
7765 in the new molecule object. */
7767 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
7771 /* Extract the fragment */
7772 retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0, NULL, NULL, 0);
7778 /* Search bonds crossing the molecule border */
7779 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
7781 IntGroupIterator iter;
7784 IntGroupIteratorInit(ig, &iter);
7785 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7786 /* The atoms at the border */
7789 n1 = src->bonds[i*2];
7790 n2 = src->bonds[i*2+1];
7791 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
7795 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
7796 continue; /* Actually this is an internal error */
7798 /* n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule */
7799 /* Create a new dummy atom with the same segment/residue info with n1
7800 and the same position as n2 */
7801 ap = ATOM_AT_INDEX(src->atoms, n1);
7802 memset(&a, 0, gSizeOfAtomRecord);
7803 a.segSeq = ap->segSeq;
7804 memmove(a.segName, ap->segName, 4);
7805 a.resSeq = ap->resSeq;
7806 memmove(a.resName, ap->resName, 4);
7807 ElementToString(0, a.element); /* "Du" */
7808 snprintf(a.aname, 4, "_%d", idx++);
7809 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
7810 /* Add the dummy atom to the new molecule; nn[1] is the index
7811 of the new dummy atom in the new molecule */
7812 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
7813 /* Connect nn1 and nn2 */
7814 nn[2] = kInvalidIndex;
7815 MoleculeAddBonds(*dstp, 1, nn, NULL, 1);
7817 IntGroupIteratorRelease(&iter);
7818 IntGroupRelease(ig);
7826 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds, IntGroup *where, Int autoGenerate)
7828 Int nangles, ndihedrals;
7829 Int *angles, *dihedrals;
7830 Int i, j, k, kk, n1, n2, cn1, cn2;
7833 Atom *ap1, *ap2, *ap3;
7835 if (mp == NULL || bonds == NULL || nbonds <= 0)
7837 if (mp->noModifyTopology)
7838 return -4; /* Prohibited operation */
7840 /* Note: Duplicates and validity are not checked (the caller must do that) */
7845 if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, n1 + nbonds - 1, NULL) == NULL
7846 || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nbonds, sizeof(Int) * 2, where) != 0) {
7847 __MoleculeUnlock(mp);
7848 return -4; /* Out of memory */
7850 if (mp->bondOrders != NULL) {
7851 /* Expand the bond order info (all new entries are zero) */
7852 Double *dp = (Double *)calloc(sizeof(Double), nbonds);
7855 if (AssignArray(&(mp->bondOrders), &(mp->nbondOrders), sizeof(Double), n1 + nbonds - 1, NULL) == NULL
7856 || sInsertElementsToArrayAtPositions(mp->bondOrders, n1, dp, nbonds, sizeof(Double), where) != 0) {
7857 __MoleculeUnlock(mp);
7864 angles = dihedrals = NULL;
7865 nangles = ndihedrals = 0;
7867 /* Add connects[], and angles/dihedrals (if autoGenerate is true) */
7868 for (i = 0; i < nbonds; i++) {
7870 /* One entry at time */
7871 /* (Otherwise, duplicate entries of angles and dihedrals result) */
7873 n2 = bonds[i * 2 + 1];
7875 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
7876 AtomConnectInsertEntry(&ap1->connect, -1, n2);
7877 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
7878 AtomConnectInsertEntry(&ap2->connect, -1, n1);
7880 /* Add angles and dihedrals */
7882 AtomConnect *ac1, *ac2;
7883 if (ap1->anchor == NULL || ap2->anchor == NULL) {
7884 /* N1-N2-{XY} or N2-N1-{XY} angles (X: connected atom, Y: constitute atom of pi-anchor) */
7885 for (j = 0; j < 4; j++) {
7887 case 0: temp[0] = n1; temp[1] = n2; ac1 = &ap2->connect; break; /* N1-N2-X */
7888 case 1: if (ap2->anchor == NULL) continue; else ac1 = &ap2->anchor->connect; break; /* N1-N2-Y */
7889 case 2: temp[0] = n2; temp[1] = n1; ac1 = &ap1->connect; break; /* N2-N1-X */
7890 case 3: if (ap1->anchor == NULL) continue; else ac1 = &ap1->anchor->connect; break; /* N2-N1-Y */
7892 cp1 = AtomConnectData(ac1);
7894 for (k = 0; k < cn1; k++) {
7896 if (temp[2] == temp[0])
7898 ap3 = ATOM_AT_INDEX(mp->atoms, temp[2]);
7899 if (ap3->anchor != NULL) {
7900 /* Avoid X-anchor-anchor angle (anchor-X-anchor is allowed) */
7901 if ((j < 2 && ap2->anchor != NULL) || (j >= 2 && ap1->anchor != NULL))
7904 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7906 /* Dihedrals N1-N2-X-{XY} or N2-N1-X-{XY} */
7907 if (j == 1 || j == 3)
7909 cp2 = AtomConnectData(&ap3->connect);
7910 for (kk = 0; kk < ap3->connect.count; kk++) {
7912 if (temp[3] == temp[0] || temp[3] == temp[1])
7914 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7917 if (ap3->anchor != NULL) {
7918 /* N1-N2-X-Y or N2-N1-X-Y */
7919 /* for Y, only the first constitute atom is considered */
7920 cp2 = AtomConnectData(&ap3->anchor->connect);
7922 if (temp[3] == temp[0] || temp[3] == temp[1])
7924 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7930 /* X-N1-N2-X dihedrals */
7931 /* Y-N1-N2-anchor is allowed, but the force may be zero if the angle N1-N2-anchor is */
7932 /* close to 180 deg (e.g. in ferrocene, C-anchor-Fe-anchor dihedral should be k=0) */
7933 if (ap1->anchor == NULL) {
7934 ac1 = &ap1->connect;
7937 ac1 = &ap1->anchor->connect;
7938 cn1 = 1; /* Only the first constitute atom of pi-anchor is considered */
7940 if (ap2->anchor == NULL) {
7941 ac2 = &ap2->connect;
7944 ac2 = &ap2->anchor->connect;
7945 cn2 = 1; /* Only the first constitute atom of pi-anchor is considered */
7949 cp1 = AtomConnectData(ac1);
7950 cp2 = AtomConnectData(ac2);
7951 for (j = 0; j < cn1; j++) {
7953 if (temp[0] == temp[2])
7955 for (k = 0; k < cn2; k++) {
7957 if (temp[3] == temp[0] || temp[3] == temp[1])
7959 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7966 if (angles != NULL) {
7967 temp[0] = kInvalidIndex;
7968 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7970 MoleculeAddAngles(mp, angles, NULL);
7973 if (dihedrals != NULL) {
7974 temp[0] = kInvalidIndex;
7975 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7977 MoleculeAddDihedrals(mp, dihedrals, NULL);
7981 MoleculeIncrementModifyCount(mp);
7982 mp->needsMDRebuild = 1;
7983 __MoleculeUnlock(mp);
7988 __MoleculeUnlock(mp);
7989 Panic("Low memory while adding bonds");
7990 return -1; /* Not reached */
7994 /* The deleted angles and dihedrals are stored in outRemoval. */
7995 /* (*outRemoval) is an array of integers, containing:
7996 [0..na*3-1]: the angle indices
7997 [na*3..na*3+nd*4-1]: the dihedral indices
7998 [na*3+nd*4..na*3+nd*4+ni*4-1]: the improper indices
7999 *outRemovedPos is an intgroup denoting the positions of the removed angles/dihedrals/impropers.
8000 the angle indices are included as they are,
8001 the dihedral indices are offset by ATOMS_MAX_NUMBER,
8002 the improper indices are offset by ATOMS_MAX_NUMBER*2.
8003 Note: the removed bond indices are not returned, because the caller should already know them. */
8005 MoleculeDeleteBonds(Molecule *mp, Int *bonds, IntGroup *where, Int **outRemoved, IntGroup **outRemovedPos)
8007 Int i, j, n1, n2, nw;
8008 Int *ip, *jp, na, nd, ni;
8009 IntGroup *ag, *dg, *ig;
8011 IntGroupIterator iter;
8015 if (mp->noModifyTopology)
8016 return -4; /* Prohibited operation */
8020 /* Update connects[] */
8021 IntGroupIteratorInit(where, &iter);
8022 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8023 n1 = mp->bonds[i * 2];
8024 n2 = mp->bonds[i * 2 + 1];
8025 ap = ATOM_AT_INDEX(mp->atoms, n1);
8026 ip = AtomConnectData(&ap->connect);
8027 for (j = 0; j < ap->connect.count; j++) {
8029 AtomConnectDeleteEntry(&ap->connect, j);
8033 ap = ATOM_AT_INDEX(mp->atoms, n2);
8034 ip = AtomConnectData(&ap->connect);
8035 for (j = 0; j < ap->connect.count; j++) {
8037 AtomConnectDeleteEntry(&ap->connect, j);
8043 /* Remove bonds, angles, dihedrals, impropers */
8048 nw = IntGroupGetCount(where);
8049 jp = (Int *)malloc(sizeof(Int) * nw * 2);
8051 IntGroupIteratorReset(&iter);
8052 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8053 jp[j++] = mp->bonds[i * 2];
8054 jp[j++] = mp->bonds[i * 2 + 1];
8056 IntGroupIteratorRelease(&iter);
8058 for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8059 for (j = 0; j < nw; j++) {
8062 if ((ip[0] == n1 && ip[1] == n2)
8063 || (ip[1] == n1 && ip[0] == n2)
8064 || (ip[1] == n1 && ip[2] == n2)
8065 || (ip[2] == n1 && ip[1] == n2)) {
8066 if (IntGroupAdd(ag, i, 1) != 0)
8073 for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8074 for (j = 0; j < nw; j++) {
8077 if ((ip[0] == n1 && ip[1] == n2)
8078 || (ip[1] == n1 && ip[0] == n2)
8079 || (ip[1] == n1 && ip[2] == n2)
8080 || (ip[2] == n1 && ip[1] == n2)
8081 || (ip[2] == n1 && ip[3] == n2)
8082 || (ip[3] == n1 && ip[2] == n2)) {
8085 if (IntGroupAdd(dg, i, 1) != 0)
8092 for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8093 for (j = 0; j < nw; j++) {
8096 if ((ip[0] == n1 && ip[2] == n2)
8097 || (ip[1] == n1 && ip[2] == n2)
8098 || (ip[3] == n1 && ip[2] == n2)
8099 || (ip[0] == n2 && ip[2] == n1)
8100 || (ip[1] == n2 && ip[2] == n1)
8101 || (ip[3] == n2 && ip[2] == n1)) {
8104 if (IntGroupAdd(ig, i, 1) != 0)
8113 if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, bonds, sizeof(Int) * 2, where) != 0)
8115 mp->nbonds -= IntGroupGetCount(where);
8116 if (mp->nbonds == 0) {
8120 if (mp->bondOrders != NULL) {
8121 if (sRemoveElementsFromArrayAtPositions(mp->bondOrders, mp->nbondOrders, NULL, sizeof(Double), where) != 0)
8123 mp->nbondOrders -= IntGroupGetCount(where);
8124 if (mp->nbondOrders == 0) {
8125 free(mp->bondOrders);
8126 mp->bondOrders = NULL;
8129 if (na == 0 && nd == 0 && ni == 0)
8132 ip = (Int *)malloc(sizeof(Int) * (na * 3 + nd * 4 + ni * 4));
8134 MoleculeDeleteAngles(mp, ip, ag);
8136 MoleculeDeleteDihedrals(mp, ip + na * 3, dg);
8138 MoleculeDeleteImpropers(mp, ip + na * 3 + nd * 4, ig);
8140 IntGroupOffset(dg, ATOMS_MAX_NUMBER);
8141 IntGroupOffset(ig, ATOMS_MAX_NUMBER * 2);
8142 IntGroupAddIntGroup(ag, dg);
8143 IntGroupAddIntGroup(ag, ig);
8144 IntGroupRelease(dg);
8145 IntGroupRelease(ig);
8148 if (IntGroupGetCount(ag) == 0) {
8149 IntGroupRelease(ag);
8154 *outRemovedPos = ag;
8156 MoleculeIncrementModifyCount(mp);
8157 mp->needsMDRebuild = 1;
8158 __MoleculeUnlock(mp);
8160 return na * 3 + nd * 4 + ni * 4;
8163 __MoleculeUnlock(mp);
8164 Panic("Low memory while removing bonds");
8165 return -1; /* Not reached */
8169 MoleculeAssignBondOrders(Molecule *mp, const Double *orders, IntGroup *where)
8172 IntGroupIterator iter;
8173 if (mp == NULL || orders == NULL || mp->nbonds == 0)
8175 if (mp->noModifyTopology)
8176 return -4; /* Prohibited operation */
8177 if (mp->bondOrders == NULL) {
8178 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbonds - 1, NULL);
8179 memset(mp->bondOrders, 0, sizeof(Double) * mp->nbondOrders);
8181 IntGroupIteratorInit(where, &iter);
8183 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8184 if (i >= mp->nbondOrders)
8186 mp->bondOrders[i] = orders[j++];
8188 IntGroupIteratorRelease(&iter);
8193 MoleculeGetBondOrders(Molecule *mp, Double *outOrders, IntGroup *where)
8196 IntGroupIterator iter;
8197 if (mp == NULL || mp->nbonds == 0)
8199 if (mp->bondOrders == NULL) {
8200 /* Returns all zero */
8201 i = IntGroupGetCount(where);
8202 for (j = 0; j < i; j++)
8205 IntGroupIteratorInit(where, &iter);
8207 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8208 if (i < mp->nbondOrders)
8209 outOrders[j] = mp->bondOrders[i];
8210 else outOrders[j] = 0.0;
8218 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
8221 if (mp == NULL || angles == NULL)
8223 if (mp->noModifyTopology)
8224 return -4; /* Prohibited operation */
8228 nc = IntGroupGetCount(where);
8230 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
8236 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
8237 || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
8238 __MoleculeUnlock(mp);
8239 Panic("Low memory while adding angles");
8242 mp->needsMDRebuild = 1;
8243 __MoleculeUnlock(mp);
8248 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
8251 if (mp == NULL || where == NULL)
8253 if (mp->noModifyTopology)
8254 return -4; /* Prohibited operation */
8256 if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
8257 __MoleculeUnlock(mp);
8258 Panic("Low memory while adding angles");
8260 mp->nangles -= (nc = IntGroupGetCount(where));
8261 if (mp->nangles == 0) {
8265 mp->needsMDRebuild = 1;
8266 __MoleculeUnlock(mp);
8271 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
8274 if (mp == NULL || dihedrals == NULL)
8276 if (mp->noModifyTopology)
8277 return -4; /* Prohibited operation */
8279 nc = IntGroupGetCount(where);
8281 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
8287 n1 = mp->ndihedrals;
8289 if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8290 || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
8291 __MoleculeUnlock(mp);
8292 Panic("Low memory while adding dihedrals");
8294 mp->needsMDRebuild = 1;
8295 __MoleculeUnlock(mp);
8300 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
8303 if (mp == NULL || where == NULL)
8305 if (mp->noModifyTopology)
8306 return -4; /* Prohibited operation */
8308 if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
8309 __MoleculeUnlock(mp);
8310 Panic("Low memory while adding dihedrals");
8312 mp->ndihedrals -= (nc = IntGroupGetCount(where));
8313 if (mp->ndihedrals == 0) {
8314 free(mp->dihedrals);
8315 mp->dihedrals = NULL;
8317 mp->needsMDRebuild = 1;
8318 __MoleculeUnlock(mp);
8323 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
8326 if (mp == NULL || impropers == NULL)
8328 if (mp->noModifyTopology)
8329 return -4; /* Prohibited operation */
8331 nc = IntGroupGetCount(where);
8333 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
8339 n1 = mp->nimpropers;
8341 if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8342 || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
8343 __MoleculeUnlock(mp);
8344 Panic("Low memory while adding impropers");
8346 mp->needsMDRebuild = 1;
8347 __MoleculeUnlock(mp);
8352 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
8355 if (mp == NULL || where == NULL)
8357 if (mp->noModifyTopology)
8358 return -4; /* Prohibited operation */
8360 if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
8361 __MoleculeUnlock(mp);
8362 Panic("Low memory while adding impropers");
8364 mp->nimpropers -= (nc = IntGroupGetCount(where));
8365 if (mp->impropers == NULL) {
8366 free(mp->impropers);
8367 mp->impropers = NULL;
8369 __MoleculeUnlock(mp);
8374 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
8377 if (mp == NULL || mp->bonds == NULL)
8379 for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
8380 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
8387 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
8390 if (mp == NULL || mp->angles == NULL)
8392 for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8393 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
8394 (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
8401 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8404 if (mp == NULL || mp->dihedrals == NULL)
8406 for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8407 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
8408 (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
8415 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8418 if (mp == NULL || mp->impropers == NULL)
8420 for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8423 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
8424 (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
8425 (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
8431 /* Remove the bond at bondIndex and create two dummy atoms instead.
8432 The dummy atoms are placed at the end of atoms[], and the residue
8433 numbers are the same as the root atoms (i.e. the atoms to which
8434 the dummy atoms are connected). The indices are returned in
8435 dummyIndices[0,1]. */
8437 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
8439 Int roots[3], newBonds[5];
8445 if (mp == NULL || mp->noModifyTopology)
8447 if (bondIndex < 0 || bondIndex >= mp->nbonds)
8449 roots[0] = mp->bonds[bondIndex * 2];
8450 roots[1] = mp->bonds[bondIndex * 2 + 1];
8451 roots[2] = kInvalidIndex;
8452 rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
8453 rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
8454 VecSub(dr, rootp[0]->r, rootp[1]->r);
8455 for (i = 0; i < 2; i++) {
8458 memmove(nap, rootp[i], sizeof(na));
8459 nap->aname[0] = '*';
8460 strcpy(nap->element, "Du");
8462 nap->charge = nap->weight = 0.0;
8463 nap->atomicNumber = 0;
8464 nap->connect.count = 0;
8465 w = (i == 0 ? 0.4 : -0.4);
8466 VecScaleInc(nap->r, dr, w);
8473 /* Expand atoms array and append the dummy atoms at the end */
8475 natoms = mp->natoms;
8476 if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
8478 memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
8479 dummyIndices[0] = natoms;
8480 dummyIndices[1] = natoms + 1;
8482 /* Remove the old bond and create new bonds */
8483 ig = IntGroupNewWithPoints(bondIndex, 1, -1);
8486 MoleculeDeleteBonds(mp, NULL, ig, NULL, NULL);
8487 IntGroupRelease(ig);
8488 newBonds[0] = roots[0];
8489 newBonds[1] = dummyIndices[0];
8490 newBonds[2] = roots[1];
8491 newBonds[3] = dummyIndices[1];
8492 newBonds[4] = kInvalidIndex;
8494 i = (MoleculeAddBonds(mp, 2, newBonds, NULL, 1) < 0 ? -1 : 0);
8495 mp->needsMDRebuild = 1;
8496 __MoleculeUnlock(mp);
8500 __MoleculeUnlock(mp);
8501 Panic("Low memory during creating dummy atoms");
8505 /* Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
8506 a bond between the two root atoms. The value bondIndex is used as a
8507 hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
8508 the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
8509 is ignored and the new bond is stored at the end of bonds[]. */
8511 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
8518 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
8521 if (mol == NULL || mol->noModifyTopology)
8528 __MoleculeLock(mol);
8529 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
8530 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
8531 mol->needsMDRebuild = 1;
8532 __MoleculeUnlock(mol);
8539 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
8542 if (mol == NULL || mol->noModifyTopology)
8544 n1 = mol->ndihedrals;
8545 np1 = mol->dihedrals;
8546 mol->ndihedrals = 0;
8547 mol->dihedrals = NULL;
8548 if (ndihedrals > 0) {
8549 __MoleculeLock(mol);
8550 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
8551 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
8552 mol->needsMDRebuild = 1;
8553 __MoleculeUnlock(mol);
8555 *outDihedrals = np1;
8560 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
8563 if (mol == NULL || mol->noModifyTopology)
8565 n1 = mol->nimpropers;
8566 np1 = mol->impropers;
8567 mol->nimpropers = 0;
8568 mol->impropers = NULL;
8569 if (nimpropers > 0) {
8570 __MoleculeLock(mol);
8571 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
8572 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
8573 mol->needsMDRebuild = 1;
8574 __MoleculeUnlock(mol);
8576 *outImpropers = np1;
8582 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
8589 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8590 return 0; /* molecule is empty */
8591 if (mol->noModifyTopology)
8595 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
8596 Int *cp = AtomConnectData(&ap->connect);
8597 if (ap->anchor != NULL)
8599 for (j = 0; j < ap->connect.count; j++) {
8601 if (ATOM_AT_INDEX(mol->atoms, j0)->anchor != NULL)
8603 for (k = j + 1; k < ap->connect.count; k++) {
8605 if (ATOM_AT_INDEX(mol->atoms, k0)->anchor != NULL)
8607 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
8608 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
8617 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
8621 if (outAngles != NULL)
8622 *outAngles = angles;
8627 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
8629 Int n1, n2, n3, n4, *ip, *cp2, *cp3;
8634 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8635 return 0; /* molecule is empty */
8638 for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
8639 Int i1, i3, i4, *ip;
8640 if (ap2->anchor != NULL)
8642 cp2 = AtomConnectData(&ap2->connect);
8643 for (i3 = 0; i3 < ap2->connect.count; i3++) {
8647 ap3 = ATOM_AT_INDEX(mol->atoms, n3);
8648 if (ap3->anchor != NULL)
8650 cp3 = AtomConnectData(&ap3->connect);
8651 for (i1 = 0; i1 < ap2->connect.count; i1++) {
8655 if (ATOM_AT_INDEX(mol->atoms, n1)->anchor != NULL)
8657 for (i4 = 0; i4 < ap3->connect.count; i4++) {
8659 if (n2 == n4 || n1 == n4)
8661 if (ATOM_AT_INDEX(mol->atoms, n4)->anchor != NULL)
8663 if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
8664 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
8674 if (ndihedrals > 0) {
8675 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
8679 if (outDihedrals != NULL)
8680 *outDihedrals = dihedrals;
8685 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
8687 Int n1, n2, n3, n4, t1, t2, t3, t4, *ip, *cp;
8688 Parameter *par = mol->par;
8693 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8694 return 0; /* molecule is empty */
8695 if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
8696 return 0; /* No improper parameters are defined */
8700 for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
8701 Int i1, i2, i4, found, *ip;
8703 cp = AtomConnectData(&ap3->connect);
8704 for (i1 = 0; i1 < ap3->connect.count; i1++) {
8706 t1 = ATOM_AT_INDEX(ap, n1)->type;
8707 for (i2 = i1 + 1; i2 < ap3->connect.count; i2++) {
8709 t2 = ATOM_AT_INDEX(ap, n2)->type;
8710 for (i4 = i2 + 1; i4 < ap3->connect.count; i4++) {
8712 t4 = ATOM_AT_INDEX(ap, n4)->type;
8714 if (ParameterLookupImproperPar(par, t1, t2, t3, t4, n1, n2, n3, n4, 0) != NULL)
8716 else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, -1, -1, -1, -1, 0) != NULL)
8718 if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
8719 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8729 if (nimpropers > 0) {
8730 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8734 if (outImpropers != NULL)
8735 *outImpropers = impropers;
8739 #pragma mark ====== Residues ======
8742 MoleculeCleanUpResidueTable(Molecule *mp)
8746 if (mp == NULL || mp->natoms == 0)
8750 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8751 if (ap->resSeq >= maxres)
8752 maxres = ap->resSeq + 1;
8753 if (ap->resSeq < mp->nresidues) {
8754 if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
8755 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8757 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
8760 if (maxres < mp->nresidues)
8761 mp->nresidues = maxres;
8762 __MoleculeUnlock(mp);
8765 /* Change the number of residues. If nresidues is greater than the current value,
8766 then the array mp->residues is expanded with null names. If nresidues is smaller
8767 than the current value, mp->nresidues is set to the smallest possible value
8768 that is no smaller than nresidues and larger than any of the resSeq values. */
8770 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
8775 if (mp->nresidues == nresidues)
8777 else if (mp->nresidues < nresidues) {
8780 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
8781 while (n < nresidues)
8782 mp->residues[n++][0] = 0;
8783 __MoleculeUnlock(mp);
8789 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8790 if (ap->resSeq >= n)
8799 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
8801 IntGroupIterator iter;
8802 int withArray, resSeq, maxSeq;
8806 /* If LSB of resSeqs is 1, then a constant value is used for all specified atoms */
8807 if (((int)resSeqs & 1) == 0) {
8812 resSeq = ((int)resSeqs - 1) / 2;
8815 IntGroupIteratorInit(group, &iter);
8817 /* Change resSeqs */
8821 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8822 ap = ATOM_AT_INDEX(mp->atoms, i);
8824 resSeq = resSeqs[j++];
8825 if (resSeq > maxSeq)
8827 ap->resSeq = resSeq;
8829 __MoleculeUnlock(mp);
8831 /* Expand array if necessary */
8832 if (maxSeq >= mp->nresidues)
8833 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8835 /* Synchronize resName and residues[] */
8837 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8838 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8840 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8842 IntGroupIteratorRelease(&iter);
8843 __MoleculeUnlock(mp);
8845 MoleculeIncrementModifyCount(mp);
8851 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
8853 return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(resSeq * 2 + 1));
8856 /* Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
8857 specifies the mp->nresidues after modifying the residue numbers.
8858 If all atoms are modified, then the table of residue names is also shifted. Otherwise,
8859 the table of residue names is not touched. */
8861 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
8863 int i, maxSeq, nmodatoms;
8865 IntGroupIterator iter;
8866 IntGroupIteratorInit(group, &iter);
8869 nresidues = mp->nresidues;
8872 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8873 ap = ATOM_AT_INDEX(mp->atoms, i);
8874 ap->resSeq += offset;
8875 if (ap->resSeq < 0) {
8876 /* Bad argument; undo change and returns this index + 1 */
8878 ap->resSeq -= offset;
8879 while ((i = IntGroupIteratorLast(&iter)) >= 0) {
8880 ap = ATOM_AT_INDEX(mp->atoms, i);
8881 ap->resSeq -= offset;
8883 IntGroupIteratorRelease(&iter);
8884 return bad_index + 1;
8886 if (ap->resSeq > maxSeq)
8887 maxSeq = ap->resSeq;
8890 if (maxSeq >= nresidues)
8891 nresidues = maxSeq + 1;
8892 if (offset < 0 && nmodatoms == mp->natoms) {
8893 /* Shift the residue names downward */
8894 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
8896 __MoleculeUnlock(mp);
8897 MoleculeChangeNumberOfResidues(mp, nresidues);
8898 if (offset > 0 && nmodatoms == mp->natoms) {
8899 /* Shift the residue names upward */
8901 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
8902 __MoleculeUnlock(mp);
8904 IntGroupIteratorRelease(&iter);
8906 MoleculeIncrementModifyCount(mp);
8911 /* Change residue names for the specified residue numbers. Names is an array of
8912 chars containing argc*4 characters, and every 4 characters represent a
8913 residue name; characters '\x01'-'\x1f' are converted to '\0', which allow
8914 names to be handled as a C string. */
8916 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
8921 for (i = 0; i < argc; i++) {
8922 if (maxSeq < resSeqs[i])
8923 maxSeq = resSeqs[i];
8925 if (maxSeq >= mp->nresidues)
8926 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8928 for (i = 0; i < argc; i++) {
8929 char *p = mp->residues[resSeqs[i]];
8931 strncpy(p, names + i * 4, 4);
8932 for (j = 0; j < 4; j++) {
8933 if (p[j] >= 0 && p[j] < 0x20)
8937 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8938 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8940 __MoleculeUnlock(mp);
8942 MoleculeIncrementModifyCount(mp);
8947 /* Returns the maximum residue number actually used */
8949 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
8954 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8955 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8957 if (ap->resSeq > maxSeq)
8958 maxSeq = ap->resSeq;
8963 /* Returns the minimum residue number actually used */
8965 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
8969 minSeq = ATOMS_MAX_NUMBER;
8970 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8971 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8973 if (ap->resSeq < minSeq)
8974 minSeq = ap->resSeq;
8976 return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
8979 #pragma mark ====== Sort by Residues ======
8982 sAtomSortComparator(const void *a, const void *b)
8984 const Atom *ap, *bp;
8985 ap = *((const Atom **)a);
8986 bp = *((const Atom **)b);
8987 if (ap->resSeq == bp->resSeq) {
8988 /* Retain the original order (i.e. atom with larger pointer address is larger) */
8995 /* Compare the residue sequence. However, residue sequence 0 is always larger. */
8996 if (ap->resSeq == 0)
8998 else if (bp->resSeq == 0)
9000 else if (ap->resSeq < bp->resSeq)
9002 else if (ap->resSeq > bp->resSeq)
9009 sMoleculeReorder(Molecule *mp)
9011 int i, res, prevRes;
9015 if (mp == NULL || mp->natoms <= 1)
9018 /* Sort the atoms, bonds, etc. */
9019 apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
9020 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9021 newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9022 if (apArray == NULL || old2new == NULL || newAtoms == NULL)
9023 Panic("Low memory during reordering atoms");
9024 for (i = 0; i < mp->natoms; i++)
9025 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
9027 /* Sort the atoms. Note: apArray is an array of "Pointer to Atom" */
9028 qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
9030 /* Make a table of 'which atom becomes which' */
9031 for (i = 0; i < mp->natoms; i++) {
9032 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
9036 /* Renumber the bonds, etc. */
9037 for (i = 0; i < mp->nbonds * 2; i++) {
9038 mp->bonds[i] = old2new[mp->bonds[i]];
9040 for (i = 0; i < mp->nangles * 3; i++) {
9041 mp->angles[i] = old2new[mp->angles[i]];
9043 for (i = 0; i < mp->ndihedrals * 4; i++) {
9044 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9046 for (i = 0; i < mp->nimpropers * 4; i++) {
9047 mp->impropers[i] = old2new[mp->impropers[i]];
9049 for (i = 0; i < mp->natoms; i++) {
9051 ip = AtomConnectData(&(apArray[i]->connect));
9052 for (j = 0; j < apArray[i]->connect.count; j++, ip++)
9056 /* Renumber the residues so that the residue numbers are contiguous */
9058 for (i = 0; i < mp->natoms; i++) {
9059 if (apArray[i]->resSeq == 0)
9061 if (apArray[i]->resSeq != prevRes) {
9063 prevRes = apArray[i]->resSeq;
9064 if (prevRes != res) {
9065 strncpy(mp->residues[res], mp->residues[prevRes], 4);
9068 apArray[i]->resSeq = res;
9070 mp->nresidues = res + 1;
9072 /* Sort the atoms and copy back to atoms[] */
9073 for (i = 0; i < mp->natoms; i++) {
9074 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
9076 memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
9078 /* Free the locally allocated storage */
9084 /* Renumber atoms */
9086 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
9088 Int *old2new, i, j, retval;
9092 if (mp->noModifyTopology)
9094 if (old2new_out != NULL)
9095 old2new = old2new_out;
9097 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9098 saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9099 if (old2new == NULL || saveAtoms == NULL)
9100 Panic("Low memory during reordering atoms");
9101 memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
9103 for (i = 0; i < mp->natoms; i++)
9105 for (i = 0; i < isize && i < mp->natoms; i++) {
9107 if (j < 0 || j >= mp->natoms) {
9108 retval = 1; /* Out of range */
9111 if (old2new[j] != -1) {
9112 retval = 2; /* Duplicate entry */
9117 if (i < mp->natoms) {
9118 for (j = 0; j < mp->natoms; j++) {
9119 if (old2new[j] != -1)
9124 if (i != mp->natoms) {
9125 retval = 3; /* Internal inconsistency */
9129 /* Renumber the bonds, etc. */
9130 for (i = 0; i < mp->nbonds * 2; i++) {
9131 mp->bonds[i] = old2new[mp->bonds[i]];
9133 for (i = 0; i < mp->nangles * 3; i++) {
9134 mp->angles[i] = old2new[mp->angles[i]];
9136 for (i = 0; i < mp->ndihedrals * 4; i++) {
9137 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9139 for (i = 0; i < mp->nimpropers * 4; i++) {
9140 mp->impropers[i] = old2new[mp->impropers[i]];
9142 /* Renumber the connection table and pi anchor table */
9143 for (i = 0; i < mp->natoms; i++) {
9144 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
9145 Int *ip = AtomConnectData(&ap->connect);
9146 for (j = 0; j < ap->connect.count; j++, ip++)
9148 if (ap->anchor != NULL) {
9149 ip = AtomConnectData(&ap->anchor->connect);
9150 for (j = 0; j < ap->anchor->connect.count; j++, ip++)
9155 if (mp->par != NULL) {
9156 /* Renumber the parameters */
9158 for (j = kFirstParType; j <= kLastParType; j++) {
9159 n = ParameterGetCountForType(mp->par, j);
9160 for (i = 0; i < n; i++) {
9161 UnionPar *up = ParameterGetUnionParFromTypeAndIndex(mp->par, j, i);
9163 ParameterRenumberAtoms(j, up, mp->natoms, old2new);
9168 /* Renumber the atoms */
9169 for (i = 0; i < mp->natoms; i++)
9170 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
9173 MoleculeIncrementModifyCount(mp);
9174 mp->needsMDRebuild = 1;
9177 __MoleculeUnlock(mp);
9179 if (old2new_out == NULL)
9184 #pragma mark ====== Coordinate Transform ======
9187 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
9192 Transform rtr, symtr;
9193 if (mp == NULL || tr == NULL)
9195 TransformInvert(rtr, tr);
9197 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9198 if (group == NULL || IntGroupLookup(group, i, NULL) != 0) {
9199 TransformVec(&ap->r, tr, &ap->r);
9200 if (!SYMOP_ALIVE(ap->symop))
9202 /* Transform symop */
9203 if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9205 TransformMul(symtr, tr, symtr);
9206 if (group == NULL || IntGroupLookup(group, ap->symbase, NULL) != 0)
9207 TransformMul(symtr, symtr, rtr);
9209 if (!SYMOP_ALIVE(ap->symop))
9211 /* Transform symop if the base atom is transformed */
9212 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
9214 if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9216 TransformMul(symtr, symtr, rtr);
9218 if (MoleculeGetSymopForTransform(mp, symtr, &new_symop, 1) != 0)
9220 ap->symop = new_symop;
9222 mp->needsMDCopyCoordinates = 1;
9223 __MoleculeUnlock(mp);
9224 sMoleculeNotifyChangeAppearance(mp);
9229 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
9233 if (mp == NULL || tr == NULL)
9236 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9237 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9239 TransformVec(&ap->r, tr, &ap->r);
9241 mp->needsMDCopyCoordinates = 1;
9242 __MoleculeUnlock(mp);
9243 sMoleculeNotifyChangeAppearance(mp);
9248 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
9251 if (mp == NULL || vp == NULL)
9253 memset(tr, 0, sizeof(tr));
9254 tr[0] = tr[4] = tr[8] = 1.0;
9258 MoleculeTransform(mp, tr, group);
9262 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
9265 TransformForRotation(tr, axis, angle, center);
9266 MoleculeTransform(mp, tr, group);
9270 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
9275 if (mp == NULL || center == NULL)
9277 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9278 return 2; /* Empty molecule */
9280 center->x = center->y = center->z = 0.0;
9281 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9282 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9284 VecScaleInc(*center, ap->r, ap->weight);
9288 return 3; /* Atomic weights are not defined? */
9290 VecScaleSelf(*center, w);
9295 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
9302 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9303 return 2; /* Empty molecule */
9304 vmin.x = vmin.y = vmin.z = 1e50;
9305 vmax.x = vmax.y = vmax.z = -1e50;
9306 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9307 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9309 if (vmin.x > ap->r.x)
9311 if (vmin.y > ap->r.y)
9313 if (vmin.z > ap->r.z)
9315 if (vmax.x < ap->r.x)
9317 if (vmax.y < ap->r.y)
9319 if (vmax.z < ap->r.z)
9329 #pragma mark ====== Measurements ======
9332 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
9335 /* if (mp->is_xtal_coord) {
9336 TransformVec(&r1, mp->cell->tr, vp1);
9337 TransformVec(&r2, mp->cell->tr, vp2);
9343 return VecLength(r1);
9347 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
9351 /* if (mp->is_xtal_coord) {
9352 TransformVec(&r1, mp->cell->tr, vp1);
9353 TransformVec(&r2, mp->cell->tr, vp2);
9354 TransformVec(&r3, mp->cell->tr, vp3);
9362 w = VecLength(r1) * VecLength(r3);
9365 return acos(VecDot(r1, r3) / w) * kRad2Deg;
9369 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
9371 Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
9373 /* if (mp->is_xtal_coord) {
9374 TransformVec(&r1, mp->cell->tr, vp1);
9375 TransformVec(&r2, mp->cell->tr, vp2);
9376 TransformVec(&r3, mp->cell->tr, vp3);
9377 TransformVec(&r4, mp->cell->tr, vp4);
9384 VecSub(r21, r1, r2);
9385 VecSub(r32, r2, r3);
9386 VecSub(r43, r3, r4);
9387 VecCross(v1, r21, r32);
9388 VecCross(v2, r32, r43);
9389 VecCross(v3, r32, v1);
9393 if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
9399 VecScaleSelf(v1, w1);
9400 VecScaleSelf(v2, w2);
9401 VecScaleSelf(v3, w3);
9402 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
9406 #pragma mark ====== XtalCell Parameters ======
9409 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
9411 if (mp->cell != NULL) {
9412 TransformVec(dst, mp->cell->tr, src);
9417 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
9419 if (mp->cell != NULL) {
9420 TransformVec(dst, mp->cell->rtr, src);
9425 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
9427 static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
9429 Vector *vp1, *vp2, *vp3;
9434 for (n1 = 0; n1 < 3; n1++) {
9435 if (cp->flags[n1] != 0)
9439 /* All directions are non-periodic */
9440 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
9441 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
9445 vp1 = &(cp->axes[n1]);
9446 vp2 = &(cp->axes[n2]);
9447 vp3 = &(cp->axes[n3]);
9448 cp->tr[n1*3] = vp1->x;
9449 cp->tr[n1*3+1] = vp1->y;
9450 cp->tr[n1*3+2] = vp1->z;
9451 cp->tr[9] = cp->origin.x;
9452 cp->tr[10] = cp->origin.y;
9453 cp->tr[11] = cp->origin.z;
9454 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
9455 /* 1-dimensional or 2-dimensional system */
9456 /* Create "dummy" axes, so that transforms between internal and cartesian coordinates are
9457 possible with a single matrix */
9458 if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
9460 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
9461 VecCross(v1, *vp1, xvec);
9462 VecCross(v2, *vp1, yvec);
9463 if (VecLength2(v1) < VecLength2(v2))
9465 VecCross(v2, *vp1, v1);
9466 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
9467 return -1; /* Non-regular transform */
9468 } else if (cp->flags[n2] == 0) {
9470 VecCross(v1, v2, *vp1);
9471 if (NormalizeVec(&v1, &v1))
9472 return -1; /* Non-regular transform */
9475 VecCross(v2, *vp1, v1);
9476 if (NormalizeVec(&v2, &v2))
9477 return -1; /* Non-regular transform */
9479 cp->tr[n2*3] = v1.x;
9480 cp->tr[n2*3+1] = v1.y;
9481 cp->tr[n2*3+2] = v1.z;
9482 cp->tr[n3*3] = v2.x;
9483 cp->tr[n3*3+1] = v2.y;
9484 cp->tr[n3*3+2] = v2.z;
9486 VecCross(v1, *vp1, *vp2);
9487 if (fabs(VecDot(v1, *vp3)) < 1e-7)
9488 return -1; /* Non-regular transform */
9489 cp->tr[n2*3] = vp2->x;
9490 cp->tr[n2*3+1] = vp2->y;
9491 cp->tr[n2*3+2] = vp2->z;
9492 cp->tr[n3*3] = vp3->x;
9493 cp->tr[n3*3+1] = vp3->y;
9494 cp->tr[n3*3+2] = vp3->z;
9497 if (TransformInvert(cp->rtr, cp->tr))
9498 return -1; /* Non-regular transform */
9500 /* Calculate the reciprocal cell parameters */
9501 cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[3] * cp->rtr[3] + cp->rtr[6] * cp->rtr[6]);
9502 cp->rcell[1] = sqrt(cp->rtr[1] * cp->rtr[1] + cp->rtr[4] * cp->rtr[4] + cp->rtr[7] * cp->rtr[7]);
9503 cp->rcell[2] = sqrt(cp->rtr[2] * cp->rtr[2] + cp->rtr[5] * cp->rtr[5] + cp->rtr[8] * cp->rtr[8]);
9504 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;
9505 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;
9506 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;
9509 /* Calculate a, b, c, alpha, beta, gamma */
9510 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[1] * cp->tr[1] + cp->tr[2] * cp->tr[2]);
9511 cp->cell[1] = sqrt(cp->tr[3] * cp->tr[3] + cp->tr[4] * cp->tr[4] + cp->tr[5] * cp->tr[5]);
9512 cp->cell[2] = sqrt(cp->tr[6] * cp->tr[6] + cp->tr[7] * cp->tr[7] + cp->tr[8] * cp->tr[8]);
9513 cp->cell[3] = acos((cp->tr[3] * cp->tr[6] + cp->tr[4] * cp->tr[7] + cp->tr[5] * cp->tr[8]) / (cp->cell[1] * cp->cell[2])) * kRad2Deg;
9514 cp->cell[4] = acos((cp->tr[6] * cp->tr[0] + cp->tr[7] * cp->tr[1] + cp->tr[8] * cp->tr[2]) / (cp->cell[2] * cp->cell[0])) * kRad2Deg;
9515 cp->cell[5] = acos((cp->tr[0] * cp->tr[3] + cp->tr[1] * cp->tr[4] + cp->tr[2] * cp->tr[5]) / (cp->cell[0] * cp->cell[1])) * kRad2Deg;
9521 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
9530 memset(&cmat, 0, sizeof(Transform));
9531 if (mp->cell != NULL)
9532 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
9534 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
9536 if (mp->cell != NULL) {
9538 mp->needsMDRebuild = 1;
9544 cp = (XtalCell *)calloc(sizeof(XtalCell), 1);
9546 Panic("Low memory during setting cell parameters");
9548 mp->needsMDRebuild = 1;
9550 /* alpha, beta, gamma are in degree */
9554 cp->cell[3] = alpha;
9556 cp->cell[5] = gamma;
9557 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
9558 /* c unique (hexagonal etc.) */
9559 Double cosa, cosb, sinb, cosg;
9560 cosa = cos(alpha * kDeg2Rad);
9561 cosb = cos(beta * kDeg2Rad);
9562 sinb = sin(beta * kDeg2Rad);
9563 cosg = cos(gamma * kDeg2Rad);
9564 cp->axes[0].x = a * sinb;
9566 cp->axes[0].z = a * cosb;
9567 cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
9568 cp->axes[1].z = b * cosa;
9569 cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
9575 Double cosg, sing, cosa, cosb;
9576 cosa = cos(alpha * kDeg2Rad);
9577 cosb = cos(beta * kDeg2Rad);
9578 cosg = cos(gamma * kDeg2Rad);
9579 sing = sin(gamma * kDeg2Rad);
9580 cp->axes[0].x = a * sing;
9581 cp->axes[0].y = a * cosg;
9586 cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
9587 cp->axes[2].y = c * cosa;
9588 cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
9590 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
9591 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
9592 MoleculeCalculateCellFromAxes(cp, 0);
9593 TransformMul(cmat, cp->tr, cmat);
9596 /* Update the coordinates (if requested) */
9597 if (convertCoordinates) {
9598 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9599 TransformVec(&(ap->r), cmat, &(ap->r));
9603 /* Update the anisotropic parameters */
9604 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9605 Aniso *anp = ap->aniso;
9607 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
9610 __MoleculeUnlock(mp);
9611 sMoleculeNotifyChangeAppearance(mp);
9615 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x13, Double x23, const Double *sigmaptr)
9619 const Double log2 = 0.693147180559945;
9620 const Double pi22 = 19.7392088021787; /* 2*pi**2 */
9626 if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
9628 anp = mp->atoms[n1].aniso;
9631 anp = (Aniso *)calloc(sizeof(Aniso), 1);
9633 __MoleculeUnlock(mp);
9634 Panic("Low memory during setting anisotropic atom parameters");
9636 mp->atoms[n1].aniso = anp;
9639 case 1: d = 1; dx = 0.5; break;
9640 case 2: d = log2; dx = log2; break;
9641 case 3: d = log2; dx = log2 * 0.5; break;
9642 case 4: u = 1; d = 0.25; dx = 0.25; break;
9643 case 5: u = 1; d = 0.25; dx = 0.125; break;
9644 case 8: u = 1; d = pi22; dx = pi22; break;
9645 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
9646 case 10: d = pi22; dx = pi22; break;
9647 default: d = dx = 1; break;
9649 anp->bij[0] = x11 * d;
9650 anp->bij[1] = x22 * d;
9651 anp->bij[2] = x33 * d;
9652 anp->bij[3] = x12 * dx;
9653 anp->bij[4] = x13 * dx;
9654 anp->bij[5] = x23 * dx;
9655 if (sigmaptr != NULL) {
9657 anp->bsig[0] = sigmaptr[0] * d;
9658 anp->bsig[1] = sigmaptr[1] * d;
9659 anp->bsig[2] = sigmaptr[2] * d;
9660 anp->bsig[3] = sigmaptr[3] * dx;
9661 anp->bsig[4] = sigmaptr[4] * dx;
9662 anp->bsig[5] = sigmaptr[5] * dx;
9665 anp->bsig[0] = anp->bsig[1] = anp->bsig[2] = anp->bsig[3] = anp->bsig[4] = anp->bsig[5] = 0.0;
9668 if (cp != NULL && u == 1) {
9669 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
9670 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
9671 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
9672 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
9673 anp->bij[4] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[3] * kDeg2Rad); */
9674 anp->bij[5] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[4] * kDeg2Rad); */
9675 if (sigmaptr != NULL) {
9676 anp->bsig[0] *= cp->rcell[0] * cp->rcell[0];
9677 anp->bsig[1] *= cp->rcell[1] * cp->rcell[1];
9678 anp->bsig[2] *= cp->rcell[2] * cp->rcell[2];
9679 anp->bsig[3] *= cp->rcell[0] * cp->rcell[1];
9680 anp->bsig[4] *= cp->rcell[2] * cp->rcell[0];
9681 anp->bsig[5] *= cp->rcell[1] * cp->rcell[2];
9685 /* Calculate the principal axes (in Cartesian coordinates) */
9686 /* The principal axes are the eigenvectors of matrix At(B^-1)A, where
9687 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
9688 in which x and z are the crystal-space and cartesian coordinates. */
9689 m1[0] = anp->bij[0] / pi22;
9690 m1[4] = anp->bij[1] / pi22;
9691 m1[8] = anp->bij[2] / pi22;
9692 m1[1] = m1[3] = anp->bij[3] / pi22;
9693 m1[2] = m1[6] = anp->bij[4] / pi22;
9694 m1[5] = m1[7] = anp->bij[5] / pi22;
9695 MatrixInvert(m1, m1);
9697 memmove(m2, cp->rtr, sizeof(Mat33));
9698 MatrixMul(m1, m1, m2);
9699 MatrixTranspose(m2, m2);
9700 MatrixMul(m1, m2, m1);
9702 MatrixSymDiagonalize(m1, val, axis);
9703 for (u = 0; u < 3; u++) {
9705 fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
9708 val[u] = 1 / sqrt(val[u]);
9710 anp->pmat[u*3] = axis[u].x * val[u];
9711 anp->pmat[u*3+1] = axis[u].y * val[u];
9712 anp->pmat[u*3+2] = axis[u].z * val[u];
9714 __MoleculeUnlock(mp);
9717 /* Set the anisotropic parameter for atom idx according to the symop. If symop is not alive, nothing is done. */
9719 MoleculeSetAnisoBySymop(Molecule *mp, int idx)
9723 if (mp == NULL || idx < 0 || idx >= mp->natoms)
9725 ap = ATOM_AT_INDEX(mp->atoms, idx);
9726 if (!SYMOP_ALIVE(ap->symop))
9728 ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
9729 if (ap2->aniso == NULL) {
9730 if (ap->aniso != NULL) {
9736 if (ap->aniso == NULL)
9737 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
9738 if (ap->symop.sym == 0 || ap->symop.sym >= mp->nsyms) {
9739 /* Just copy the aniso parameters */
9740 memmove(ap->aniso, ap2->aniso, sizeof(Aniso));
9743 memmove(t1, SYMMETRY_AT_INDEX(mp->syms, ap->symop.sym), sizeof(Transform));
9744 t1[9] = t1[10] = t1[11] = 0.0;
9745 memset(t2, 0, sizeof(Transform));
9746 t2[0] = ap2->aniso->bij[0];
9747 t2[4] = ap2->aniso->bij[1];
9748 t2[8] = ap2->aniso->bij[2];
9749 t2[1] = t2[3] = ap2->aniso->bij[3];
9750 t2[2] = t2[6] = ap2->aniso->bij[4];
9751 t2[5] = t2[7] = ap2->aniso->bij[5];
9752 TransformMul(t2, t1, t2);
9753 TransformInvert(t1, t1);
9754 TransformMul(t2, t2, t1);
9755 MoleculeSetAniso(mp, idx, 0, t2[0], t2[4], t2[8], t2[1], t2[2], t2[5], (ap2->aniso->has_bsig ? ap2->aniso->bsig : NULL));
9759 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic, int convertCoordinates)
9761 static Vector zeroVec = {0, 0, 0};
9768 if (mp->cell != NULL)
9769 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
9771 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
9773 if (mp->cell != NULL) {
9775 mp->needsMDRebuild = 1;
9780 memset(&b, 0, sizeof(b));
9781 b.axes[0] = (ax != NULL ? *ax : zeroVec);
9782 b.axes[1] = (ay != NULL ? *ay : zeroVec);
9783 b.axes[2] = (az != NULL ? *az : zeroVec);
9785 memmove(b.flags, periodic, 3);
9786 if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
9789 if (mp->cell == NULL) {
9790 mp->needsMDRebuild = 1;
9792 if (mp->cell->has_sigma) {
9793 /* Keep the sigma */
9795 memmove(b.cellsigma, mp->cell->cellsigma, sizeof(mp->cell->cellsigma));
9797 if ((b.flags[0] != mp->cell->flags[0]) || (b.flags[1] != mp->cell->flags[1]) || (b.flags[2] != mp->cell->flags[2])) {
9798 mp->needsMDRebuild = 1;
9802 mp->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
9803 if (mp->cell != NULL) {
9804 memmove(mp->cell, &b, sizeof(XtalCell));
9805 TransformMul(cmat, b.tr, cmat);
9806 /* Update the coordinates (if requested) */
9807 if (convertCoordinates) {
9808 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9809 TransformVec(&(ap->r), cmat, &(ap->r));
9813 /* Update the anisotropic parameters */
9814 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9815 Aniso *anp = ap->aniso;
9817 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
9821 } else n = -2; /* Out of memory */
9822 __MoleculeUnlock(mp);
9823 sMoleculeNotifyChangeAppearance(mp);
9827 #pragma mark ====== Fragment manipulation ======
9830 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
9834 if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
9836 IntGroupAdd(result, idx, 1);
9837 ap = ATOM_AT_INDEX(mp->atoms, idx);
9838 cp = AtomConnectData(&ap->connect);
9839 for (i = 0; i < ap->connect.count; i++) {
9841 if (IntGroupLookup(result, idx2, NULL))
9843 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, idx2)->anchor != NULL)
9844 continue; /* bond between two pi_anchors is ignored */
9845 sMoleculeFragmentSub(mp, idx2, result, exatoms);
9847 if (ap->anchor != NULL) {
9848 cp = AtomConnectData(&ap->anchor->connect);
9849 for (i = 0; i < ap->anchor->connect.count; i++) {
9851 if (IntGroupLookup(result, idx2, NULL))
9853 sMoleculeFragmentSub(mp, idx2, result, exatoms);
9858 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
9859 not containing the atoms in exatoms */
9861 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
9864 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9866 result = IntGroupNew();
9867 sMoleculeFragmentSub(mp, n1, result, exatoms);
9871 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
9872 not containing the atoms n2, n3, ... (terminated by -1) */
9874 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
9877 IntGroup *exatoms, *result;
9878 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9880 exatoms = IntGroupNew();
9881 for (i = 0; i < argc; i++)
9882 IntGroupAdd(exatoms, argv[i], 1);
9883 result = IntGroupNew();
9884 sMoleculeFragmentSub(mp, n1, result, exatoms);
9885 IntGroupRelease(exatoms);
9889 /* The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
9890 not containing the atoms in exatoms */
9892 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
9894 IntGroupIterator iter;
9897 if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
9899 IntGroupIteratorInit(inatoms, &iter);
9900 result = IntGroupNew();
9901 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9902 sMoleculeFragmentSub(mp, i, result, exatoms);
9904 IntGroupIteratorRelease(&iter);
9908 /* Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
9909 group is bound to the rest of the molecule via only one bond.
9910 If the result is true, then the atoms belonging to the (only) bond are returned
9911 in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
9912 and n2 can be NULL, if those informations are not needed. */
9914 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
9916 Int i, i1, i2, j, k, bond_count, nval1, nval2, *cp;
9918 if (mp == NULL || mp->natoms == 0 || group == NULL)
9919 return 0; /* Invalid arguments */
9921 for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
9922 i2 = IntGroupGetEndPoint(group, i);
9923 for (j = i1; j < i2; j++) {
9924 if (j < 0 || j >= mp->natoms)
9925 return 0; /* Invalid atom group */
9926 ap = ATOM_AT_INDEX(mp->atoms, j);
9927 cp = AtomConnectData(&ap->connect);
9928 for (k = 0; k < ap->connect.count; k++) {
9929 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, cp[k])->anchor != NULL)
9930 continue; /* Ignore bond between two pi_anchors */
9931 if (IntGroupLookup(group, cp[k], NULL) == 0) {
9936 return 0; /* Too many bonds */
9939 if (ap->anchor != NULL) {
9940 cp = AtomConnectData(&ap->anchor->connect);
9941 for (k = 0; k < ap->anchor->connect.count; k++) {
9942 if (IntGroupLookup(group, cp[k], NULL) == 0) {
9947 return 0; /* Too many bonds */
9953 if (bond_count == 1) {
9964 /* Returns non-zero if the given group is 'rotatable' in the molecule. The group
9965 is said to be 'rotatable' when either of the following conditions are met; (1)
9966 the group is detachable, or (2) the group consists of two bonded atoms that define
9967 a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
9968 (either a new IntGroup or 'group' with incremented reference count; thus the caller
9969 is responsible for releasing the returned value). */
9971 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
9974 if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
9975 if (rotGroup != NULL) {
9976 IntGroupRetain(group);
9981 if (group != NULL && IntGroupGetCount(group) == 2) {
9982 i1 = IntGroupGetNthPoint(group, 0);
9983 i2 = IntGroupGetNthPoint(group, 1);
9984 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
9985 IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
9988 i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
9990 IntGroupRelease(frag);
9991 if (rotGroup != NULL)
9995 if (rotGroup != NULL)
9997 else if (frag != NULL)
9998 IntGroupRelease(frag);
10005 #pragma mark ====== Multiple frame ======
10008 MoleculeGetNumberOfFrames(Molecule *mp)
10012 if (mp->nframes <= 0) {
10016 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10017 if (ap->nframes > n)
10024 return mp->nframes;
10028 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
10030 int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
10031 Vector *tempv, *vp;
10033 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
10036 n_old = MoleculeGetNumberOfFrames(mp);
10037 n_new = n_old + count;
10038 last_inserted = IntGroupGetNthPoint(group, count - 1);
10039 if (n_new <= last_inserted) {
10040 exframes = last_inserted - n_new + 1; /* number of extra frames that will be silently inserted */
10042 } else exframes = 0;
10044 tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4); /* "*4" for handling cells */
10048 __MoleculeLock(mp);
10050 /* Copy back the current coordinates */
10051 /* No change in the current coordinates, but the frame buffer is updated */
10052 MoleculeSelectFrame(mp, mp->cframe, 1);
10054 /* Expand ap->frames for all atoms */
10055 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10056 if (ap->frames == NULL)
10057 vp = (Vector *)calloc(sizeof(Vector), n_new);
10059 vp = (Vector *)realloc(ap->frames, sizeof(Vector) * n_new);
10061 __MoleculeUnlock(mp);
10064 for (j = ap->nframes; j < n_new; j++)
10068 if (mp->cell != NULL) {
10069 j = mp->nframe_cells;
10070 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, n_new - 1, NULL);
10071 for (i = j; i < n_new; i++) {
10072 /* Set the current cell parameters to the expanded frames */
10073 mp->frame_cells[i * 4] = mp->cell->axes[0];
10074 mp->frame_cells[i * 4 + 1] = mp->cell->axes[1];
10075 mp->frame_cells[i * 4 + 2] = mp->cell->axes[2];
10076 mp->frame_cells[i * 4 + 3] = mp->cell->origin;
10080 /* group = [n0..n1-1, n2..n3-1, ...] */
10082 /* tempv[0..n0-1] <- ap[0..n0-1], s += n0,
10083 tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
10084 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
10085 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
10087 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
10088 At last, s will become n_old and t will become count. */
10089 for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10090 int s, t, ns, ne, mult;
10093 if (i == mp->natoms) {
10094 if (mp->cell == NULL || mp->frame_cells == NULL)
10096 vp = mp->frame_cells;
10103 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10105 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
10108 ne = IntGroupGetEndPoint(group, j);
10110 if (i == mp->natoms) {
10111 if (inFrameCell != NULL) {
10112 tempv[ns * 4] = inFrameCell[t * 4];
10113 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
10114 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
10115 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
10117 tempv[ns * 4] = mp->cell->axes[0];
10118 tempv[ns * 4 + 1] = mp->cell->axes[1];
10119 tempv[ns * 4 + 2] = mp->cell->axes[2];
10120 tempv[ns * 4 + 3] = mp->cell->origin;
10123 if (inFrame != NULL)
10124 tempv[ns] = inFrame[natoms * t + i];
10133 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
10136 if (i < mp->natoms)
10137 ap->nframes = n_new;
10138 memmove(vp, tempv, sizeof(Vector) * mult * n_new);
10141 mp->nframes = n_new;
10142 MoleculeSelectFrame(mp, last_inserted, 0);
10143 MoleculeIncrementModifyCount(mp);
10144 __MoleculeUnlock(mp);
10149 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
10151 int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
10152 Vector *tempv, *vp;
10154 IntGroup *group, *group2;
10156 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
10159 /* outFrame[] should have enough size for Vector * natoms * group.count */
10160 memset(outFrame, 0, sizeof(Vector) * natoms * count);
10161 if (mp->cell != NULL && mp->frame_cells != NULL)
10162 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
10164 n_old = MoleculeGetNumberOfFrames(mp);
10166 return -2; /* Cannot delete last frame */
10168 group = IntGroupNew();
10169 group2 = IntGroupNewWithPoints(0, n_old, -1);
10170 IntGroupIntersect(inGroup, group2, group);
10171 IntGroupRelease(group2);
10172 count = IntGroupGetCount(group);
10173 n_new = n_old - count;
10175 IntGroupRelease(group);
10176 return -2; /* Trying to delete too many frames */
10178 tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4); /* "*4" for handling cells */
10179 if (tempv == NULL) {
10180 IntGroupRelease(group);
10184 __MoleculeLock(mp);
10186 /* Copy back the current coordinates */
10187 /* No change in the current coordinates, but the frame buffer is updated */
10188 MoleculeSelectFrame(mp, mp->cframe, 1);
10190 /* Determine which frame should be selected after removal is completed */
10193 if (IntGroupLookup(group, mp->cframe, &i)) {
10194 /* cframe will be removed */
10195 n1 = IntGroupGetStartPoint(group, i) - 1;
10197 n1 = IntGroupGetEndPoint(group, i);
10198 } else n1 = mp->cframe;
10199 /* Change to that frame */
10200 MoleculeSelectFrame(mp, n1, 0);
10201 group2 = IntGroupNewFromIntGroup(group);
10202 IntGroupReverse(group2, 0, n_old);
10203 new_cframe = IntGroupLookupPoint(group2, n1);
10204 if (new_cframe < 0)
10205 return -3; /* This cannot happen */
10206 IntGroupRelease(group2);
10209 /* group = [n0..n1-1, n2..n3-1, ...] */
10211 /* tempv[0..n0-1] -> ap[0..n0-1], s += n0,
10212 tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
10213 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
10214 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
10216 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
10217 At last, s will become n_new and t will become count. */
10219 for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10220 int s, t, j, ns, ne;
10222 /* if i == mp->natoms, mp->frame_cells is handled */
10223 if (i == mp->natoms) {
10224 if (mp->cell == NULL || mp->frame_cells == NULL)
10227 vp = mp->frame_cells;
10233 ap->frames = vp = (Vector *)calloc(sizeof(Vector), n_old);
10235 __MoleculeUnlock(mp);
10239 old_count = ap->nframes;
10242 /* Copy vp to tempv */
10243 memset(tempv, 0, sizeof(Vector) * mult * n_old);
10244 memmove(tempv, vp, sizeof(Vector) * mult * (old_count > n_old ? n_old : old_count));
10245 ne = ns = s = t = 0;
10246 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10250 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (ns - ne));
10253 ne = IntGroupGetEndPoint(group, j);
10257 if (i < mp->natoms)
10258 outFrame[natoms * t + i] = tempv[ns];
10259 else if (outFrameCell != NULL) {
10260 outFrameCell[t * 4] = tempv[ns * 4];
10261 outFrameCell[t * 4 + 1] = tempv[ns * 4 + 1];
10262 outFrameCell[t * 4 + 2] = tempv[ns * 4 + 2];
10263 outFrameCell[t * 4 + 3] = tempv[ns * 4 + 3];
10270 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (n_old - ne));
10273 if (i < mp->natoms)
10278 if (i < mp->natoms) {
10283 free(mp->frame_cells);
10284 mp->frame_cells = NULL;
10285 mp->nframe_cells = 0;
10288 if (i < mp->natoms)
10289 ap->frames = (Vector *)realloc(ap->frames, sizeof(Vector) * s);
10291 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, s - 1, NULL);
10292 mp->nframe_cells = s;
10297 mp->nframes = nframes;
10299 /* Select the "last" frame; do not "copy back" the coordinates to the frame table */
10300 /* i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
10301 MoleculeSelectFrame(mp, new_cframe, 0);
10303 IntGroupRelease(group);
10305 MoleculeIncrementModifyCount(mp);
10306 __MoleculeUnlock(mp);
10311 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
10313 int i, cframe, nframes, modified;
10315 cframe = mp->cframe;
10316 nframes = MoleculeGetNumberOfFrames(mp);
10318 frame = mp->cframe;
10319 if (mp == NULL || mp->natoms == 0 || frame < 0 || frame >= nframes)
10322 __MoleculeLock(mp);
10323 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10324 if (copyback && cframe >= 0 && cframe < ap->nframes) {
10325 /* Write the current coordinate back to the frame array */
10326 ap->frames[cframe] = ap->r;
10328 if (frame != cframe && frame >= 0 && frame < ap->nframes) {
10329 /* Read the coordinate from the frame array */
10330 ap->r = ap->frames[frame];
10335 if (mp->cell != NULL && mp->frame_cells != NULL) {
10336 /* Write the current cell back to the frame_cells array */
10337 if (copyback && cframe >= 0) {
10338 Vector *vp = (Vector *)AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, cframe, NULL);
10339 vp[0] = mp->cell->axes[0];
10340 vp[1] = mp->cell->axes[1];
10341 vp[2] = mp->cell->axes[2];
10342 vp[3] = mp->cell->origin;
10344 /* Set the cell from the frame array */
10345 if (frame != cframe && frame >= 0 && frame < mp->nframe_cells) {
10346 MoleculeSetPeriodicBox(mp, &mp->frame_cells[frame * 4], &mp->frame_cells[frame * 4 + 1], &mp->frame_cells[frame * 4 + 2], &mp->frame_cells[frame * 4 + 3], mp->cell->flags, 0);
10348 MoleculeAmendBySymmetry(mp, NULL, NULL, NULL);
10351 mp->cframe = frame;
10353 mp->needsMDCopyCoordinates = 1;
10354 __MoleculeUnlock(mp);
10355 sMoleculeNotifyChangeAppearance(mp);
10359 /* If molecule is multi-frame, then flush the current information to the frame buffer.
10360 Returns the number of frames. */
10362 MoleculeFlushFrames(Molecule *mp)
10364 int nframes = MoleculeGetNumberOfFrames(mp);
10366 MoleculeSelectFrame(mp, mp->cframe, 1);
10370 #pragma mark ====== Pi Atoms ======
10373 sMoleculeCalculatePiAnchorPosition(Atom *ap, Atom *atoms)
10377 cp = AtomConnectData(&ap->anchor->connect);
10378 n = ap->anchor->connect.count;
10380 for (j = 0; j < n; j++) {
10381 Double w = ap->anchor->coeffs[j];
10382 ap2 = ATOM_AT_INDEX(atoms, cp[j]);
10383 VecScaleInc(ap->r, ap2->r, w);
10388 MoleculeUpdatePiAnchorPositions(Molecule *mol)
10392 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
10393 if (ap->anchor == NULL)
10395 sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
10400 MoleculeCalculatePiAnchorPosition(Molecule *mol, int idx)
10403 if (mol == NULL || idx < 0 || idx >= mol->natoms)
10405 ap = ATOM_AT_INDEX(mol->atoms, idx);
10406 if (ap->anchor == NULL)
10408 sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
10412 MoleculeSetPiAnchorList(Molecule *mol, Int idx, Int nentries, Int *entries, Double *weights, Int *nUndoActions, struct MolAction ***undoActions)
10415 Int *ip, i, j, n, *np;
10417 if (mol == NULL || idx < 0 || idx >= mol->natoms || nentries <= 1)
10418 return -1; /* Invalid argument */
10419 if (weights != NULL) {
10421 for (i = 0; i < nentries; i++) {
10422 if (weights[i] <= 0.0) {
10423 return 10; /* Weights must be positive */
10428 } else d = 1.0 / nentries;
10429 ap = ATOM_AT_INDEX(mol->atoms, idx);
10430 if (ap->anchor != NULL) {
10431 /* Already an anchor: check if bonds/angles/dihedrals have entries related to this anchor */
10432 IntGroup *bg, *ag, *dg, *ig;
10433 Int *ibuf, ibufsize;
10435 bg = ag = dg = ig = NULL;
10436 ip = AtomConnectData(&ap->anchor->connect);
10437 for (i = 0; i < ap->anchor->connect.count; i++) {
10439 for (j = 0; j < nentries; j++) {
10440 if (n == entries[j])
10443 if (j == nentries) {
10444 /* This entry will disappear: if any bond/angle/dihedral has idx-n pair, that should be removed. */
10445 for (j = 0, np = mol->bonds; j < mol->nbonds; j++, np += 2) {
10446 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[0])) {
10448 bg = IntGroupNew();
10449 IntGroupAdd(bg, j, 1);
10452 for (j = 0, np = mol->angles; j < mol->nangles; j++, np += 3) {
10453 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) ||
10454 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1])) {
10456 ag = IntGroupNew();
10457 IntGroupAdd(ag, j, 1);
10460 for (j = 0, np = mol->dihedrals; j < mol->ndihedrals; j++, np += 4) {
10461 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) || (idx == np[2] && n == np[3]) ||
10462 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[3] && n == np[2])) {
10464 dg = IntGroupNew();
10465 IntGroupAdd(dg, j, 1);
10468 for (j = 0, np = mol->impropers; j < mol->nimpropers; j++, np += 4) {
10469 if ((idx == np[0] && n == np[2]) || (idx == np[1] && n == np[2]) || (idx == np[3] && n == np[2]) ||
10470 (idx == np[2] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[2] && n == np[3])) {
10472 ig = IntGroupNew();
10473 IntGroupAdd(ig, j, 1);
10481 /* Delete impropers (with undo info) */
10482 i = IntGroupGetCount(ig);
10483 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
10484 MoleculeDeleteImpropers(mol, ibuf, ig);
10485 if (nUndoActions != NULL && undoActions != NULL) {
10486 act = MolActionNew(gMolActionAddImpropers, i * 4, ibuf, ig);
10487 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10489 IntGroupRelease(ig);
10492 /* Delete dihedrals (with undo info) */
10493 i = IntGroupGetCount(dg);
10494 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
10495 MoleculeDeleteDihedrals(mol, ibuf, dg);
10496 if (nUndoActions != NULL && undoActions != NULL) {
10497 act = MolActionNew(gMolActionAddDihedrals, i * 4, ibuf, dg);
10498 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10500 IntGroupRelease(dg);
10503 /* Delete angles (with undo info) */
10504 i = IntGroupGetCount(ag);
10505 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 3 - 1, NULL);
10506 MoleculeDeleteAngles(mol, ibuf, ag);
10507 if (nUndoActions != NULL && undoActions != NULL) {
10508 act = MolActionNew(gMolActionAddAngles, i * 3, ibuf, ag);
10509 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10511 IntGroupRelease(ag);
10514 /* Delete bonds (with undo info) */
10515 i = IntGroupGetCount(bg);
10516 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 2 - 1, NULL);
10517 MoleculeDeleteBonds(mol, ibuf, bg, NULL, NULL);
10518 if (nUndoActions != NULL && undoActions != NULL) {
10519 act = MolActionNew(gMolActionAddBondsForUndo, i * 2, ibuf, bg);
10520 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10522 IntGroupRelease(bg);
10525 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
10527 AtomConnectResize(&ap->anchor->connect, nentries);
10528 memmove(AtomConnectData(&ap->anchor->connect), entries, sizeof(Int) * nentries);
10529 AssignArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), nentries - 1, NULL);
10530 if (weights != NULL) {
10531 memmove(ap->anchor->coeffs, weights, sizeof(Double) * nentries);
10532 for (i = 0; i < nentries; i++)
10533 ap->anchor->coeffs[i] *= d; /* Normalize weight */
10535 for (i = 0; i < nentries; i++)
10536 ap->anchor->coeffs[i] = d;
10538 MoleculeCalculatePiAnchorPosition(mol, idx);
10542 #pragma mark ====== MO calculation ======
10544 /* Calculate an MO value for a single point. */
10545 /* Index is the MO number (1-based) */
10546 /* tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom. */
10548 sCalcMOPoint(const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
10552 Double val, tval, *cnp, *tmpp, *mobasep, *mop;
10554 /* Cache dr and |dr|^2 */
10555 for (i = 0; i < bset->natoms; i++) {
10556 Vector r = bset->pos[i];
10557 tmp[i * 4] = r.x = vp->x - r.x;
10558 tmp[i * 4 + 1] = r.y = vp->y - r.y;
10559 tmp[i * 4 + 2] = r.z = vp->z - r.z;
10560 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
10562 /* Iterate over all shells */
10564 mobasep = bset->mo + (index - 1) * bset->ncomps;
10565 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
10566 pp = bset->priminfos + sp->p_idx;
10567 cnp = bset->cns + sp->cn_idx;
10568 tmpp = tmp + sp->a_idx * 4;
10569 mop = mobasep + sp->m_idx;
10573 for (j = 0; j < sp->nprim; j++) {
10574 tval += *cnp++ * exp(-pp->A * tmpp[3]);
10577 val += mop[0] * tval;
10583 for (j = 0; j < sp->nprim; j++) {
10584 tval = exp(-pp->A * tmpp[3]);
10585 x += *cnp++ * tval;
10586 y += *cnp++ * tval;
10587 z += *cnp++ * tval;
10590 x *= mop[0] * tmpp[0];
10591 y *= mop[1] * tmpp[1];
10592 z *= mop[2] * tmpp[2];
10596 case kGTOType_SP: {
10599 for (j = 0; j < sp->nprim; j++) {
10600 tval = exp(-pp->A * tmpp[3]);
10601 t += *cnp++ * tval;
10602 x += *cnp++ * tval;
10603 y += *cnp++ * tval;
10604 z += *cnp++ * tval;
10608 x *= mop[1] * tmpp[0];
10609 y *= mop[2] * tmpp[1];
10610 z *= mop[3] * tmpp[2];
10611 val += t + x + y + z;
10615 Double xx, yy, zz, xy, xz, yz;
10616 xx = yy = zz = xy = xz = yz = 0;
10617 for (j = 0; j < sp->nprim; j++) {
10618 tval = exp(-pp->A * tmpp[3]);
10619 xx += *cnp++ * tval;
10620 yy += *cnp++ * tval;
10621 zz += *cnp++ * tval;
10622 xy += *cnp++ * tval;
10623 xz += *cnp++ * tval;
10624 yz += *cnp++ * tval;
10627 xx *= mop[0] * tmpp[0] * tmpp[0];
10628 yy *= mop[1] * tmpp[1] * tmpp[1];
10629 zz *= mop[2] * tmpp[2] * tmpp[2];
10630 xy *= mop[3] * tmpp[0] * tmpp[1];
10631 xz *= mop[4] * tmpp[0] * tmpp[2];
10632 yz *= mop[5] * tmpp[1] * tmpp[2];
10633 val += xx + yy + zz + xy + xz + yz;
10636 case kGTOType_D5: {
10637 Double d0, d1p, d1n, d2p, d2n;
10638 d0 = d1p = d1n = d2p = d2n = 0;
10639 for (j = 0; j < sp->nprim; j++) {
10640 tval = exp(-pp->A * tmpp[3]);
10641 d0 += *cnp++ * tval;
10642 d1p += *cnp++ * tval;
10643 d1n += *cnp++ * tval;
10644 d2p += *cnp++ * tval;
10645 d2n += *cnp++ * tval;
10648 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
10649 d1p *= mop[1] * tmpp[0] * tmpp[2];
10650 d1n *= mop[2] * tmpp[1] * tmpp[2];
10651 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
10652 d2n *= mop[4] * tmpp[0] * tmpp[1];
10653 val += d0 + d1p + d1n + d2p + d2n;
10656 /* TODO: Support F/F7 and G/G9 type orbitals */
10662 /* Calculate one MO. The input vectors should be in bohr unit (angstrom * 1.889725989 = kAngstrom2Bohr). */
10663 /* mono is the MO number (1-based) */
10665 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)
10667 int ix, iy, iz, n, nn;
10670 if (mp == NULL || mp->bset == NULL)
10672 if (mp->bset->cns == NULL) {
10673 if (sSetupGaussianCoefficients(mp->bset) != 0)
10676 cp = (Cube *)calloc(sizeof(Cube), 1);
10680 cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
10681 if (cp->dp == NULL) {
10694 /* TODO: use multithread */
10695 tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms * 4);
10702 for (ix = 0; ix < nx; ix++) {
10704 for (iy = 0; iy < ny; iy++) {
10705 for (iz = 0; iz < nz; iz++) {
10706 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
10707 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
10708 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
10709 cp->dp[n++] = sCalcMOPoint(mp->bset, mono, &p, tmp);
10711 if (callback != NULL && n - nn > 100) {
10713 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
10717 return -2; /* User interrupt */
10724 AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
10725 return mp->bset->ncubes - 1;
10729 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
10732 Vector rmin, rmax, *vp;
10733 Double dr, dx, dy, dz;
10734 if (mp == NULL || mp->bset == NULL || mp->bset->natoms == 0)
10738 rmin.x = rmin.y = rmin.z = 1e10;
10739 rmax.x = rmax.y = rmax.z = -1e10;
10740 for (i = 0, vp = mp->bset->pos; i < mp->bset->natoms; i++, vp++) {
10741 dr = RadiusForAtomicNumber(ATOM_AT_INDEX(mp->atoms, i)->atomicNumber);
10744 dr = dr * kAngstrom2Bohr * 3.0 + 2.0;
10745 if (rmin.x > vp->x - dr)
10746 rmin.x = vp->x - dr;
10747 if (rmin.y > vp->y - dr)
10748 rmin.y = vp->y - dr;
10749 if (rmin.z > vp->z - dr)
10750 rmin.z = vp->z - dr;
10751 if (rmax.x < vp->x + dr)
10752 rmax.x = vp->x + dr;
10753 if (rmax.y < vp->y + dr)
10754 rmax.y = vp->y + dr;
10755 if (rmax.z < vp->z + dr)
10756 rmax.z = vp->z + dr;
10758 dx = rmax.x - rmin.x;
10759 dy = rmax.y - rmin.y;
10760 dz = rmax.z - rmin.z;
10761 dr = pow(dx * dy * dz / npoints, 1.0/3.0);
10762 *nx = floor(dx / dr + 0.5);
10763 *ny = floor(dy / dr + 0.5);
10764 *nz = floor(dz / dr + 0.5);
10772 xp->x = yp->y = zp->z = dr;
10773 xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
10778 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
10780 if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
10782 return mp->bset->cubes[index];
10786 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
10789 if (mp == NULL || mp->bset == NULL)
10791 for (i = 0; i < mp->bset->ncubes; i++) {
10792 if (mp->bset->cubes[i]->idn == mono)
10799 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
10802 if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
10804 CubeRelease(mp->bset->cubes[index]);
10806 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
10807 if (--(mp->bset->ncubes) == 0) {
10808 free(mp->bset->cubes);
10809 mp->bset->cubes = NULL;
10811 return mp->bset->ncubes;
10815 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
10820 if (mp == NULL || mp->bset == NULL)
10821 return -1; /* Molecule or the basis set information is empty */
10822 cp = MoleculeGetCubeAtIndex(mp, index);
10824 return -2; /* MO not yet calculated */
10825 fp = fopen(fname, "wb");
10827 return -3; /* Cannot create file */
10829 /* Comment lines */
10830 fprintf(fp, "%s MO=%d\n", comment, cp->idn);
10831 fprintf(fp, " MO coefficients\n");
10833 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms), cp->origin.x, cp->origin.y, cp->origin.z);
10834 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx, cp->dx.x, cp->dx.y, cp->dx.z);
10835 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny, cp->dy.x, cp->dy.y, cp->dy.z);
10836 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz, cp->dz.x, cp->dz.y, cp->dz.z);
10838 /* Atomic information */
10839 for (i = 0; i < mp->bset->natoms; i++) {
10840 Vector *vp = mp->bset->pos + i;
10841 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
10842 /* The second number should actually be the effective charge */
10843 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber, vp->x, vp->y, vp->z);
10845 fprintf(fp, "%5d%5d\n", 1, 1);
10848 for (i = n = 0; i < cp->nx; i++) {
10849 for (j = 0; j < cp->ny; j++) {
10850 for (k = 0; k < cp->nz; k++) {
10851 fprintf(fp, " %12.5e", cp->dp[n++]);
10852 if (k == cp->nz - 1 || k % 6 == 5)