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;
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 < 8; i++)
792 mview_fbuf[i] = kUndefined;
793 for (i = 0; i < 16; 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, "!:mm_exclude") == 0) {
881 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
886 /* idx mm_exclude periodic_exclude */
887 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
888 s_append_asprintf(errbuf, "line %d: mm_exclude flags cannot be read for atom %d", lineNumber, i + 1);
891 if (i >= mp->natoms) {
892 s_append_asprintf(errbuf, "line %d: too many mm_exclude flags\n", lineNumber);
895 ap = ATOM_AT_INDEX(mp->atoms, i);
896 ap->mm_exclude = (ibuf[1] != 0);
897 ap->periodic_exclude = (ibuf[2] != 0);
901 } else if (strcmp(buf, "!:pi_anchor") == 0) {
902 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
908 if ((j = sscanf(buf, "%d %d", &ibuf[0], &ibuf[1])) < 2) {
909 s_append_asprintf(errbuf, "line %d: bad format for pi_anchor", lineNumber);
913 ap = ATOM_AT_INDEX(mp->atoms, i);
914 if (ap->anchor != NULL) {
915 s_append_asprintf(errbuf, "line %d: warning: duplicate pi_anchor entry", lineNumber);
916 AtomConnectResize(&ap->anchor->connect, 0);
917 free(ap->anchor->coeffs);
920 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
921 if (ibuf[1] < 2 || ibuf[1] >= mp->natoms) {
922 s_append_asprintf(errbuf, "line %d: bad number of components for pi_anchor", lineNumber);
925 AtomConnectResize(&ap->anchor->connect, ibuf[1]);
926 ip = AtomConnectData(&ap->anchor->connect);
927 NewArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), ibuf[1]);
929 for (i = 0; i < j; i++) {
930 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
931 s_append_asprintf(errbuf, "line %d: unexpected end of file while reading pi_anchors", lineNumber);
934 if (sscanf(buf, "%d %lf", &ibuf[0], &dbuf[0]) < 2) {
935 s_append_asprintf(errbuf, "line %d: bad format for pi_anchor", lineNumber);
938 if (ibuf[0] < 0 || ibuf[0] >= mp->natoms) {
939 s_append_asprintf(errbuf, "line %d: atom index out of range", lineNumber);
942 if (dbuf[0] <= 0.0) {
943 s_append_asprintf(errbuf, "line %d: the pi anchor weights should be positive", lineNumber);
947 ap->anchor->coeffs[i] = dbuf[0];
951 } else if (strcmp(buf, "!:positions") == 0) {
953 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
959 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) {
960 s_append_asprintf(errbuf, "line %d: atom position cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
963 if (j > 4 && nframes != 0) {
964 s_append_asprintf(errbuf, "line %d: atom position sigma can only be given for frame 0", lineNumber);
967 if (j > 4 && j != 7) {
968 s_append_asprintf(errbuf, "line %d: atom position sigma cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
971 if (i >= mp->natoms) {
972 s_append_asprintf(errbuf, "line %d: too many atom position records\n", lineNumber);
978 ap = ATOM_AT_INDEX(mp->atoms, i);
980 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes, &v);
982 ap->frames[0] = ap->r;
986 ap->sigma.x = dbuf[3];
987 ap->sigma.y = dbuf[4];
988 ap->sigma.z = dbuf[5];
994 mp->nframes = nframes;
995 mp->cframe = nframes - 1;
997 mp->nframes = mp->cframe = 0;
1000 } else if (strcmp(buf, "!:bonds") == 0) {
1001 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1006 /* from1 to1 from2 to2 from3 to3 from4 to4 */
1007 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]);
1008 if (i < 2 || i % 2 != 0) {
1009 s_append_asprintf(errbuf, "line %d: bad bond format", lineNumber);
1012 for (j = 0; j < i; j += 2) {
1014 iibuf[1] = ibuf[j + 1];
1015 if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[0] == iibuf[1]) {
1016 s_append_asprintf(errbuf, "line %d: warning: bad bond specification (%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1]);
1018 } else if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[0])->connect), iibuf[1])) {
1019 s_append_asprintf(errbuf, "line %d: warning: bond %d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1]);
1022 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, iibuf);
1023 AtomConnectInsertEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[0])->connect), -1, iibuf[1]);
1024 AtomConnectInsertEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[1])->connect), -1, iibuf[0]);
1029 } else if (strcmp(buf, "!:angles") == 0) {
1030 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1035 /* a1 b1 c1 a2 b2 c2 a3 b3 c3 */
1036 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]);
1037 if (i == 0 || i % 3 != 0) {
1038 s_append_asprintf(errbuf, "line %d: bad angle format", lineNumber);
1041 for (j = 0; j < i; j += 3) {
1043 iibuf[1] = ibuf[j + 1];
1044 iibuf[2] = ibuf[j + 2];
1045 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]) {
1046 s_append_asprintf(errbuf, "line %d: warning: bad angle specification (%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1048 } else if (MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[2]) == 0) {
1049 s_append_asprintf(errbuf, "line %d: warning: angle with non-bonded atoms (%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1051 } else if (MoleculeLookupAngle(mp, iibuf[0], iibuf[1], iibuf[2]) >= 0) {
1052 s_append_asprintf(errbuf, "line %d: warning: angle %d-%d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1055 AssignArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, mp->nangles, iibuf);
1060 } else if (strcmp(buf, "!:dihedrals") == 0) {
1061 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1066 /* a1 b1 c1 d1 a2 b2 c2 d2 */
1067 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]);
1068 if (i == 0 || i % 4 != 0) {
1069 s_append_asprintf(errbuf, "line %d: bad dihedral format", lineNumber);
1072 for (j = 0; j < i; j += 4) {
1074 iibuf[1] = ibuf[j + 1];
1075 iibuf[2] = ibuf[j + 2];
1076 iibuf[3] = ibuf[j + 3];
1077 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]) {
1078 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]);
1080 } else if (MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[2]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[3]) == 0) {
1081 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]);
1083 } else if (MoleculeLookupDihedral(mp, iibuf[0], iibuf[1], iibuf[2], iibuf[3]) >= 0) {
1084 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]);
1087 AssignArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, mp->ndihedrals, iibuf);
1092 } else if (strcmp(buf, "!:impropers") == 0) {
1093 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1098 /* a1 b1 c1 d1 a2 b2 c2 d2 */
1099 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]);
1100 if (i == 0 || i % 4 != 0) {
1101 s_append_asprintf(errbuf, "line %d: bad improper format", lineNumber);
1104 for (j = 0; j < i; j += 4) {
1106 iibuf[1] = ibuf[j + 1];
1107 iibuf[2] = ibuf[j + 2];
1108 iibuf[3] = ibuf[j + 3];
1109 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]) {
1110 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]);
1112 } else if (MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[1]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[3]) == 0) {
1113 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]);
1115 } else if (MoleculeLookupImproper(mp, iibuf[0], iibuf[1], iibuf[2], iibuf[3]) >= 0) {
1116 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]);
1119 AssignArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, mp->nimpropers, iibuf);
1124 } else if (strcmp(buf, "!:xtalcell") == 0 && mp->cell == NULL) {
1125 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1130 /* a b c alpha beta gamma [sigmaflag] */
1131 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) {
1132 s_append_asprintf(errbuf, "line %d: bad xtalcell format", lineNumber);
1135 MoleculeSetCell(mp, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], 0);
1136 if (j == 7 && ibuf[0] != 0) {
1137 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1138 s_append_asprintf(errbuf, "line %d: sigma for xtalcell are missing", lineNumber);
1141 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1142 s_append_asprintf(errbuf,"line %d: bad xtalcell sigma format", lineNumber);
1145 if (mp->cell != NULL) {
1146 mp->cell->has_sigma = 1;
1147 for (i = 0; i < 6; i++) {
1148 mp->cell->cellsigma[i] = dbuf[i];
1151 s_append_asprintf(errbuf, "line %d: cell sigma are given while cell is not given", lineNumber);
1156 } else if (strcmp(buf, "!:symmetry_operations") == 0) {
1158 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1164 /* a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3 */
1165 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1166 s_append_asprintf(errbuf, "line %d: bad symmetry_operation format", lineNumber);
1171 tr[i + 3] = dbuf[1];
1172 tr[i + 6] = dbuf[2];
1180 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1185 } else if (strcmp(buf, "!:anisotropic_thermal_parameters") == 0) {
1187 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1192 /* b11 b22 b33 b12 b13 b23 [has_sigma] */
1193 if ((j = sscanf(buf, "%lf %lf %lf %lf %lf %lf %d", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5], &ibuf[0])) < 6) {
1194 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters cannot be read for atom %d", lineNumber, i + 1);
1197 if (i >= mp->natoms) {
1198 s_append_asprintf(errbuf, "line %d: too many anisotropic thermal parameters\n", lineNumber);
1201 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) {
1204 MoleculeSetAniso(mp, i, 0, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], NULL);
1206 if (j == 7 && ibuf[0] != 0) {
1207 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1208 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma missing", lineNumber);
1211 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1212 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma cannot be read for atom %d", lineNumber, i + 1);
1215 ap = ATOM_AT_INDEX(mp->atoms, i);
1216 if (ap->aniso == NULL) {
1217 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma are given while the parameters are not given", lineNumber);
1220 ap->aniso->has_bsig = 1;
1221 for (j = 0; j < 6; j++)
1222 ap->aniso->bsig[j] = dbuf[j];
1227 } else if (strcmp(buf, "!:periodic_box") == 0) {
1231 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1236 /* 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] */
1238 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1239 s_append_asprintf(errbuf, "line %d: bad periodic_box format", lineNumber);
1248 if ((j = sscanf(buf, "%d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3])) < 3) {
1249 s_append_asprintf(errbuf, "line %d: bad periodic_box format", lineNumber);
1252 if (j == 4 && ibuf[3] != 0)
1254 cbuf[0][0] = ibuf[0];
1255 cbuf[0][1] = ibuf[1];
1256 cbuf[0][2] = ibuf[2];
1257 MoleculeSetPeriodicBox(mp, vs, vs + 1, vs + 2, vs + 3, cbuf[0], 0);
1259 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1260 s_append_asprintf(errbuf, "line %d: sigma for cell parameters are missing", lineNumber);
1263 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1264 s_append_asprintf(errbuf, "line %d: bad periodic_box sigma format", lineNumber);
1267 if (mp->cell != NULL) {
1268 mp->cell->has_sigma = 1;
1269 for (i = 0; i < 6; i++) {
1270 mp->cell->cellsigma[i] = dbuf[i];
1273 s_append_asprintf(errbuf, "line %d: cell sigma are given while cell is not given", lineNumber);
1279 } else if (strcmp(buf, "!:frame_periodic_boxes") == 0) {
1282 /* mp->useFlexibleCell = 1; *//* The presence of this block causes asserting this flag */
1283 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1288 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1289 s_append_asprintf(errbuf, "line %d: bad frame_periodic_box format", lineNumber);
1297 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells, vs);
1301 if (mp->cframe < mp->nframe_cells) {
1302 /* mp->cframe should already have been set when positions are read */
1303 Vector *vp = &mp->frame_cells[mp->cframe * 4];
1304 static char defaultFlags[] = {1, 1, 1};
1305 char *flags = (mp->cell != NULL ? mp->cell->flags : defaultFlags);
1306 MoleculeSetPeriodicBox(mp, vp, vp + 1, vp + 2, vp + 3, flags, 0);
1309 } else if (strcmp(buf, "!:md_parameters") == 0) {
1311 if (mp->arena == NULL)
1312 mp->arena = md_arena_new(NULL);
1314 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1320 comp = strsep(&bufp, " \t");
1322 while (*bufp == ' ' || *bufp == '\t')
1324 valp = strsep(&bufp, "\n");
1326 if (strcmp(comp, "alchem_flags") == 0) {
1327 j = (valp == NULL ? 0 : atoi(valp));
1329 valp = (char *)malloc(j);
1331 while ((k = fgetc(fp)) >= 0) {
1333 if (k < '0' || k > '9') {
1334 s_append_asprintf(errbuf, "line %d: too few flags in alchem_flags block", lineNumber + 1);
1338 ReadLine(buf, sizeof buf, fp, &lineNumber);
1340 while (*bufp != 0) {
1341 if (*bufp >= '0' && *bufp <= '2') {
1343 s_append_asprintf(errbuf, "line %d: too many flags in alchem_flags block", lineNumber);
1347 valp[i++] = *bufp - '0';
1348 } else if (*bufp != ' ' && *bufp != '\t' && *bufp != '\n') {
1349 s_append_asprintf(errbuf, "line %d: strange character (0x%02x) in alchem_flags block", lineNumber, (int)*bufp);
1358 md_set_alchemical_flags(arena, j, valp);
1363 /* In the following, the redundant "!= NULL" is to suppress suprious warning */
1364 if ((strcmp(comp, "log_file") == 0 && (pp = &arena->log_result_name) != NULL)
1365 || (strcmp(comp, "coord_file") == 0 && (pp = &arena->coord_result_name) != NULL)
1366 || (strcmp(comp, "vel_file") == 0 && (pp = &arena->vel_result_name) != NULL)
1367 || (strcmp(comp, "force_file") == 0 && (pp = &arena->force_result_name) != NULL)
1368 || (strcmp(comp, "debug_file") == 0 && (pp = &arena->debug_result_name) != NULL)) {
1369 if (*valp == 0 || strstr(valp, "(null)") == valp)
1372 valp = strdup(valp);
1374 char *valp1 = strchr(valp, '\n');
1380 } else if ((strcmp(comp, "debug_output_level") == 0 && (ip = &arena->debug_output_level) != NULL)
1381 || (strcmp(comp, "coord_output_freq") == 0 && (ip = &arena->coord_output_freq) != NULL)
1382 || (strcmp(comp, "energy_output_freq") == 0 && (ip = &arena->energy_output_freq) != NULL)
1383 || (strcmp(comp, "coord_frame") == 0 && (ip = &arena->coord_result_frame) != NULL)
1384 || (strcmp(comp, "andersen_freq") == 0 && (ip = &arena->andersen_thermo_freq) != NULL)
1385 || (strcmp(comp, "random_seed") == 0 && (ip = &arena->random_seed) != NULL)
1386 || (strcmp(comp, "use_xplor_shift") == 0 && (ip = &arena->use_xplor_shift) != NULL)
1387 || (strcmp(comp, "relocate_center") == 0 && (ip = &arena->relocate_center) != NULL)
1388 || (strcmp(comp, "surface_potential_freq") == 0 && (ip = &arena->surface_potential_freq) != NULL)
1389 || (strcmp(comp, "use_graphite") == 0 && (ip = &arena->use_graphite) != NULL)) {
1390 *ip = (valp == NULL ? 0 : atoi(valp));
1391 } else if ((strcmp(comp, "timestep") == 0 && (dp = &arena->timestep) != NULL)
1392 || (strcmp(comp, "cutoff") == 0 && (dp = &arena->cutoff) != NULL)
1393 || (strcmp(comp, "electro_cutoff") == 0 && (dp = &arena->electro_cutoff) != NULL)
1394 || (strcmp(comp, "pairlist_distance") == 0 && (dp = &arena->pairlist_distance) != NULL)
1395 || (strcmp(comp, "switch_distance") == 0 && (dp = &arena->switch_distance) != NULL)
1396 || (strcmp(comp, "temperature") == 0 && (dp = &arena->temperature) != NULL)
1397 || (strcmp(comp, "andersen_coupling") == 0 && (dp = &arena->andersen_thermo_coupling) != NULL)
1398 || (strcmp(comp, "dielectric") == 0 && (dp = &arena->dielectric) != NULL)
1399 || (strcmp(comp, "gradient_convergence") == 0 && (dp = &arena->gradient_convergence) != NULL)
1400 || (strcmp(comp, "coordinate_convergence") == 0 && (dp = &arena->coordinate_convergence) != NULL)
1401 || (strcmp(comp, "scale14_vdw") == 0 && (dp = &arena->scale14_vdw) != NULL)
1402 || (strcmp(comp, "scale14_elect") == 0 && (dp = &arena->scale14_elect) != NULL)
1403 || (strcmp(comp, "surface_probe_radius") == 0 && (dp = &arena->probe_radius) != NULL)
1404 || (strcmp(comp, "surface_tension") == 0 && (dp = &arena->surface_tension) != NULL)
1405 || (strcmp(comp, "alchemical_lambda") == 0 && (dp = &arena->alchem_lambda) != NULL)
1406 || (strcmp(comp, "alchemical_delta_lambda") == 0 && (dp = &arena->alchem_dlambda) != NULL)) {
1407 *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1411 } else if (strcmp(buf, "!:pressure_control_parameters") == 0) {
1412 MDPressureArena *pressure;
1413 if (mp->arena == NULL)
1414 mp->arena = md_arena_new(mp);
1415 if (mp->arena->pressure == NULL)
1416 mp->arena->pressure = pressure_new();
1417 pressure = mp->arena->pressure;
1418 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1424 comp = strsep(&bufp, " \t");
1426 while (*bufp == ' ' || *bufp == '\t')
1428 valp = strsep(&bufp, "\n");
1430 if (strcmp(comp, "pressure") == 0) {
1431 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) {
1432 s_append_asprintf(errbuf, "line %d: bad format", lineNumber);
1435 for (i = 0; i < 9; i++)
1436 pressure->apply[i] = dbuf[i];
1437 } else if (strcmp(comp, "pressure_cell_flexibility") == 0) {
1438 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) {
1439 s_append_asprintf(errbuf, "line %d: bad format", lineNumber);
1442 for (i = 0; i < 8; i++)
1443 pressure->cell_flexibility[i] = dbuf[i];
1444 } else if ((strcmp(comp, "pressure_freq") == 0 && (ip = &pressure->freq) != NULL)) {
1445 *ip = (valp == NULL ? 0 : atoi(valp));
1446 } else if ((strcmp(comp, "pressure_coupling") == 0 && (dp = &pressure->coupling) != NULL)
1447 || (strcmp(comp, "pressure_fluctuate_cell_origin") == 0 && (dp = &pressure->fluctuate_cell_origin) != NULL)
1448 || (strcmp(comp, "pressure_fluctuate_cell_orientation") == 0 && (dp = &pressure->fluctuate_cell_orientation) != NULL)) {
1449 *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1453 } else if (strcmp(buf, "!:velocity") == 0) {
1455 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1461 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1462 s_append_asprintf(errbuf, "line %d: atom velocity cannot be read for atom %d", lineNumber, i + 1);
1465 if (i >= mp->natoms) {
1466 s_append_asprintf(errbuf, "line %d: too many atom velocity records\n", lineNumber);
1469 ap = ATOM_AT_INDEX(mp->atoms, i);
1476 } else if (strcmp(buf, "!:force") == 0) {
1478 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1484 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1485 s_append_asprintf(errbuf, "line %d: atom force cannot be read for atom %d", lineNumber, i + 1);
1488 if (i >= mp->natoms) {
1489 s_append_asprintf(errbuf, "line %d: too many atom force records\n", lineNumber);
1492 ap = ATOM_AT_INDEX(mp->atoms, i);
1499 } else if (strcmp(buf, "!:parameter") == 0 || strcmp(buf, "!:parameters") == 0) {
1500 Parameter *par = mp->par;
1502 mp->par = ParameterNew();
1507 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1512 j = ParameterReadFromString(par, buf, &bufp, fname, lineNumber, 0);
1514 s_append_asprintf(errbuf, "%s", bufp);
1520 MyAppCallback_setConsoleColor(1);
1521 MyAppCallback_showScriptMessage("%s", bufp);
1522 MyAppCallback_setConsoleColor(0);
1526 } else if (strcmp(buf, "!:trackball") == 0) {
1528 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1533 /* scale; trx try trz; theta_deg x y z */
1534 if ((i == 0 && sscanf(buf, "%f", &mview_fbuf[0]) < 1)
1535 || (i == 1 && sscanf(buf, "%f %f %f",
1536 &mview_fbuf[1], &mview_fbuf[2], &mview_fbuf[3]) < 3)
1537 || (i == 2 && sscanf(buf, "%f %f %f %f",
1538 &mview_fbuf[4], &mview_fbuf[5], &mview_fbuf[6], &mview_fbuf[7]) < 4)) {
1539 s_append_asprintf(errbuf, "line %d: bad trackball format", lineNumber);
1545 } else if (strcmp(buf, "!:view") == 0) {
1546 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1552 comp = strsep(&bufp, " \t");
1554 while (*bufp == ' ' || *bufp == '\t')
1556 valp = strsep(&bufp, "\n");
1558 /* In the following, the redundant "!= NULL" is to suppress suprious warning */
1559 if ((strcmp(comp, "show_unit_cell") == 0 && (i = 1))
1560 || (strcmp(comp, "show_periodic_box") == 0 && (i = 2))
1561 || (strcmp(comp, "show_expanded_atoms") == 0 && (i = 3))
1562 || (strcmp(comp, "show_ellipsoids") == 0 && (i = 4))
1563 || (strcmp(comp, "show_hydrogens") == 0 && (i = 5))
1564 || (strcmp(comp, "show_dummy_atoms") == 0 && (i = 6))
1565 || (strcmp(comp, "show_rotation_center") == 0 && (i = 7))
1566 || (strcmp(comp, "show_graphite_flag") == 0 && (i = 8))
1567 || (strcmp(comp, "show_periodic_image_flag") == 0 && (i = 9))
1568 || (strcmp(comp, "show_graphite") == 0 && (i = 10))) {
1569 mview_ibuf[i - 1] = atoi(valp);
1570 } else if (strcmp(comp, "show_periodic_image") == 0) {
1571 sscanf(valp, "%d %d %d %d %d %d",
1572 &mview_ibuf[10], &mview_ibuf[11], &mview_ibuf[12],
1573 &mview_ibuf[13], &mview_ibuf[14], &mview_ibuf[15]);
1578 /* Unknown sections are silently ignored */
1581 MoleculeCleanUpResidueTable(mp);
1582 if (mp->arena != NULL)
1583 md_arena_set_molecule(mp->arena, mp);
1586 if (mp->mview != NULL) {
1587 if (mview_ibuf[0] != kUndefined)
1588 mp->mview->showUnitCell = mview_ibuf[0];
1589 if (mview_ibuf[1] != kUndefined)
1590 mp->mview->showPeriodicBox = mview_ibuf[1];
1591 if (mview_ibuf[2] != kUndefined)
1592 mp->mview->showExpandedAtoms = mview_ibuf[2];
1593 if (mview_ibuf[3] != kUndefined)
1594 mp->mview->showEllipsoids = mview_ibuf[3];
1595 if (mview_ibuf[4] != kUndefined)
1596 mp->mview->showHydrogens = mview_ibuf[4];
1597 if (mview_ibuf[5] != kUndefined)
1598 mp->mview->showDummyAtoms = mview_ibuf[5];
1599 if (mview_ibuf[6] != kUndefined)
1600 mp->mview->showRotationCenter = mview_ibuf[6];
1601 if (mview_ibuf[7] != kUndefined)
1602 mp->mview->showGraphiteFlag = mview_ibuf[7];
1603 if (mview_ibuf[8] != kUndefined)
1604 mp->mview->showPeriodicImageFlag = mview_ibuf[8];
1605 if (mview_ibuf[9] != kUndefined)
1606 mp->mview->showGraphite = mview_ibuf[9];
1607 for (i = 0; i < 6; i++) {
1608 if (mview_ibuf[10 + i] != kUndefined)
1609 mp->mview->showPeriodicImage[i] = mview_ibuf[10 + i];
1611 if (mp->mview->track != NULL) {
1612 if (mview_fbuf[0] != kUndefined)
1613 TrackballSetScale(mp->mview->track, mview_fbuf[0]);
1614 if (mview_fbuf[1] != kUndefined)
1615 TrackballSetTranslate(mp->mview->track, mview_fbuf + 1);
1616 if (mview_fbuf[4] != kUndefined)
1617 TrackballSetRotate(mp->mview->track, mview_fbuf + 4);
1625 /* The content of mp may be broken, so make it empty */
1631 MoleculeLoadPsfFile(Molecule *mp, const char *fname, char **errbuf)
1640 Vector *frames = NULL;
1646 else MoleculeClear(mp);
1647 fp = fopen(fname, "rb");
1649 s_append_asprintf(errbuf, "Cannot open file");
1652 /* flockfile(fp); */
1655 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1656 if (strncmp(buf, "PSF", 3) == 0) {
1660 for (p = buf; *p != 0 && isspace(*p); p++) {}
1666 if (strstr(buf, "!COORD") != NULL) {
1667 /* Extended psf file with coordinates */
1669 /* Allocate a temporary storage for frames */
1670 size_t size = sizeof(Vector) * mp->natoms * fn;
1672 frames = (Vector *)malloc(size);
1674 frames = (Vector *)realloc(frames, size);
1679 /* Copy the coordinates of the first frame */
1680 for (i = 0; i < mp->natoms; i++) {
1681 ap = ATOM_AT_INDEX(mp->atoms, i);
1685 /* Copy the coordinates of the last frame to the newly created frame */
1686 memmove(frames + sizeof(Vector) * mp->natoms * fn, frames + sizeof(Vector) * mp->natoms * (fn - 1), sizeof(Vector) * mp->natoms);
1689 /* Read coordinates */
1690 for (i = 0; i < mp->natoms; i++) {
1693 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1695 s_append_asprintf(errbuf, "line %d: premature end of file while reading coordinates (frame %d)", lineNumber, fn);
1698 if (sscanf(buf, "%lg %lg %lg", dval, dval + 1, dval + 2) != 3) {
1700 s_append_asprintf(errbuf, "line %d: coordinates cannot be read for atom %d", lineNumber, i + 1);
1707 ATOM_AT_INDEX(mp->atoms, i)->r = r;
1709 frames[mp->natoms * (fn - 1) + i] = r;
1718 ReadFormat(buf, "I8", &natoms);
1721 if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
1724 for (i = 0; i < natoms; i++) {
1726 char segName[5], resName[4], atomName[5], atomType[3], element[3];
1729 memset(&w, 0, sizeof(w));
1730 ap = ATOM_AT_INDEX(mp->atoms, i);
1731 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1733 s_append_asprintf(errbuf, "line %d: premature end of file while reading atoms", lineNumber);
1736 ReadFormat(buf, "I8 x1 S4 I5 x1 S3 x2 S4 x1 S4 F16 F10",
1737 &w.serial, w.segName, &ap->resSeq, w.resName, w.atomName,
1738 w.atomType, &ap->charge, &ap->weight);
1739 strncpy(ap->segName, w.segName, 4);
1740 strncpy(ap->resName, w.resName, 3);
1741 strncpy(ap->aname, w.atomName, 4);
1742 ap->type = AtomTypeEncodeToUInt(w.atomType);
1743 /* $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl */
1744 ap->atomicNumber = GuessAtomicNumber(w.atomName, ap->weight);
1745 ElementToString(ap->atomicNumber, w.element);
1746 strncpy(ap->element, w.element, 2);
1747 /* w.element[0] = 0;
1748 for (p = w.atomName; *p != 0; p++) {
1749 if (isalpha(*p) && *p != '_') {
1750 w.element[0] = toupper(*p);
1751 if (isalpha(p[1]) && p[1] != '_') {
1752 w.element[1] = toupper(p[1]);
1760 strncpy(ap->element, w.element, 2);
1761 ap->atomicNumber = ElementToInt(w.element); */
1762 if (w.resName[0] == 0)
1763 strncpy(ap->resName, "XXX", 3);
1764 if (ap->resSeq > mp->nresidues)
1765 mp->nresidues = ap->resSeq;
1767 if (mp->residues != NULL)
1769 if (NewArray(&mp->residues, &mp->nresidues, sizeof(char (*)[4]), mp->nresidues + 1) == 0)
1771 for (i = 0; i < mp->natoms; i++) {
1772 j = mp->atoms[i].resSeq;
1773 if (mp->residues[j][0] == 0)
1774 strncpy(mp->residues[j], mp->atoms[i].resName, 4);
1777 } else if (section == 3) {
1781 ReadFormat(buf, "I8", &nbonds);
1784 if (NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, nbonds) == NULL)
1787 for (i = 0; i < nbonds; i += 4) {
1788 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1789 s_append_asprintf(errbuf, "line %d: premature end of file while reading bonds", lineNumber);
1793 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1794 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1795 for (j = 0; j < 4 && i + j < nbonds; j++) {
1798 b1 = ibuf[j * 2] - 1; /* Internal atom number is 0-based */
1799 b2 = ibuf[j * 2 + 1] - 1;
1800 if (b1 < 0 || b1 >= mp->natoms || b2 < 0 || b2 >= mp->natoms) {
1801 s_append_asprintf(errbuf, "line %d: The bond %d-%d includes non-existent atom", lineNumber, b1+1, b2+1);
1807 ap = ATOM_AT_INDEX(mp->atoms, b1);
1808 AtomConnectInsertEntry(&ap->connect, -1, b2);
1809 ap = ATOM_AT_INDEX(mp->atoms, b2);
1810 AtomConnectInsertEntry(&ap->connect, -1, b1);
1814 } else if (section == 4) {
1818 ReadFormat(buf, "I8", &nangles);
1821 if (NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, nangles) == NULL)
1824 for (i = 0; i < nangles; i += 3) {
1825 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1826 s_append_asprintf(errbuf, "line %d: premature end of file while reading angles", lineNumber);
1830 ReadFormat(buf, "I8I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
1831 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7, ibuf + 8);
1832 for (j = 0; j < 3 && i + j < nangles; j++) {
1834 a1 = ibuf[j * 3] - 1; /* Internal atom number is 0-based */
1835 a2 = ibuf[j * 3 + 1] - 1;
1836 a3 = ibuf[j * 3 + 2] - 1;
1837 if (a1 < 0 || a1 >= mp->natoms || a2 < 0 || a2 >= mp->natoms || a3 < 0 || a3 >= mp->natoms) {
1838 s_append_asprintf(errbuf, "line %d: The angle %d-%d-%d includes non-existent atom", lineNumber, a1+1, a2+1, a3+1);
1848 } else if (section == 5 || section == 6) {
1849 /* Dihedrals and Impropers */
1852 ReadFormat(buf, "I8", &ndihedrals);
1853 if (ndihedrals == 0)
1856 if (NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, ndihedrals) == NULL)
1860 if (NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, ndihedrals) == NULL)
1864 for (i = 0; i < ndihedrals; i += 2) {
1865 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1867 s_append_asprintf(errbuf, "line %d: premature end of file while reading %s", lineNumber, (section == 5 ? "dihedral" : "improper"));
1871 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3, ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
1872 for (j = 0; j < 2 && i + j < ndihedrals; j++) {
1874 d1 = ibuf[j * 4] - 1; /* Internal atom number is 0-based */
1875 d2 = ibuf[j * 4 + 1] - 1;
1876 d3 = ibuf[j * 4 + 2] - 1;
1877 d4 = ibuf[j * 4 + 3] - 1;
1878 if (d1 < 0 || d1 >= mp->natoms || d2 < 0 || d2 >= mp->natoms || d3 < 0 || d3 >= mp->natoms || d4 < 0 || d4 >= mp->natoms) {
1879 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);
1893 /* Create frames for each atom if necessary */
1895 for (i = 0; i < mp->natoms; i++) {
1896 ap = ATOM_AT_INDEX(mp->atoms, i);
1897 ap->frames = (Vector *)malloc(sizeof(Vector) * fn);
1898 if (ap->frames == NULL)
1901 for (j = 0; j < fn; j++)
1902 ap->frames[j] = frames[mp->natoms * j + i];
1909 /* funlockfile(fp); */
1911 mp->nframes = -1; /* Should be recalculated later */
1914 else if (section == -1)
1918 Panic("low memory while reading structure file %s", fname);
1919 return 1; /* not reached */
1922 /* ("-x", "y", "-z+0.5") -> (-1,0,0,0,0,1,0,0,0,0,-1,0.5) */
1924 sMoleculeSymopStringsToTransform(char **symops, Transform tr)
1928 memset(tr, 0, sizeof(Transform));
1929 for (i = 0; i < 3; i++) {
1933 while (*symop != 0) {
1935 while (isspace(*symop))
1937 if (*symop == 0 || *symop == '\r' || *symop == 'n')
1939 if (*symop == '-') {
1942 } else if (*symop == '+') {
1946 while (isspace(*symop))
1948 if (*symop == '.' || isdigit(*symop)) {
1949 /* Numerical offset */
1950 double d = strtod(symop, &symop);
1951 if (*symop == '/') {
1952 double dd = strtod(symop + 1, &symop);
1956 return 1; /* Bad format */
1959 } else if (*symop == 'x' || *symop == 'X') {
1962 } else if (*symop == 'y' || *symop == 'Y') {
1965 } else if (*symop == 'z' || *symop == 'Z') {
1968 } else return 1; /* Bad format */
1969 } /* end while (*symop != 0) */
1975 sMoleculeGenerateSymopWithTransform(Molecule *mp, Transform gtr, int num)
1981 for (i = 0; i < num; i++) {
1982 memmove(tr, mp->syms[i], sizeof(Transform));
1983 TransformMul(tr, gtr, tr);
1984 for (j = 9; j < 12; j++) {
1987 else if (tr[j] <= 0.0)
1990 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1997 char *p = buf + strlen(buf) - 1;
1998 if (p >= buf && (*p == '\n' || *p == '\r')) {
2000 if (--p >= buf && (*p == '\n' || *p == '\r')) {
2008 MoleculeLoadTepFile(Molecule *mp, const char *fname, char **errbuf)
2021 fp = fopen(fname, "rb");
2023 s_append_asprintf(errbuf, "Cannot open file");
2027 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2028 if (section == -1) {
2035 ReadFormat(buf, "I1F8F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5);
2037 MoleculeSetCell(mp, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], 0);
2044 if (cellType == 0) {
2045 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);
2059 char *symops[3], *brks;
2061 memset(tr, 0, sizeof(Transform));
2062 ReadFormat(buf, "I1", ibuf);
2063 symops[0] = strtok_r(buf + 1, ", ", &brks);
2064 symops[1] = strtok_r(NULL, ", ", &brks);
2065 symops[2] = strtok_r(NULL, ", ", &brks);
2066 if (sMoleculeSymopStringsToTransform(symops, tr)) {
2067 s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2071 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2077 if (section == 2) { /* Atoms */
2081 int atomIndex = mp->natoms;
2082 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2083 memset(ap, 0, gSizeOfAtomRecord);
2084 ReadFormat(buf, "S6x3x9x9F9F9F9F9", name, fbuf, fbuf+1, fbuf+2, fbuf+3);
2085 strncpy(ap->aname, name, 4);
2089 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2090 /* ap->atomicNumber = AtomNameToElement(ap->name);
2091 ElementToString(ap->atomicNumber, ap->element); */
2092 /* sAtomSetElement(ap, -1, ap->name); */
2095 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2096 s_append_asprintf(errbuf, "unexpected end of file");
2099 ReadFormat(buf, "I1F8F9F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2101 if ((atomType >= 0 && atomType <= 5) || (atomType >= 8 && atomType <= 10)) {
2102 /* Anisotropic thermal parameters */
2103 MoleculeSetAniso(mp, atomIndex, atomType, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[5], fbuf[4], NULL);
2111 MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
2113 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2116 mp->nframes = -1; /* Should be recalculated later */
2119 Panic("low memory while reading structure file %s", fname);
2120 return -1; /* not reached */
2124 MoleculeLoadShelxFile(Molecule *mp, const char *fname, char **errbuf)
2132 int currentResSeq = 0;
2133 char currentResName[6];
2140 char (*sfacs)[4] = NULL;
2145 currentResName[0] = 0;
2146 fp = fopen(fname, "rb");
2148 s_append_asprintf(errbuf, "Cannot open file");
2152 tr[0] = tr[4] = tr[8] = 1;
2153 tr[1] = tr[2] = tr[3] = tr[5] = tr[6] = tr[7] = tr[9] = tr[10] = tr[11] = 0;
2154 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), 0, tr) == 0)
2156 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2157 if (strncmp(buf, "CELL", 4) == 0) {
2159 sscanf(buf + 4, " %f %f %f %f %f %f %f", fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2160 MoleculeSetCell(mp, fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], fbuf[6], 0);
2162 } else if (strncmp(buf, "SFAC", 4) == 0) {
2164 for (p1 = strtok_r(buf + 4, " ", &p2); p1 != NULL; p1 = strtok_r(NULL, " ", &p2)) {
2165 char *pp = (char *)AssignArray(&sfacs, &nsfacs, 4, nsfacs, NULL);
2172 } else if (strncmp(buf, "LATT", 4) == 0) {
2173 sscanf(buf + 4, " %d", &latticeType);
2175 } else if (strncmp(buf, "SYMM", 4) == 0) {
2176 char *symops[3], *brks;
2177 memset(tr, 0, sizeof(Transform));
2178 // ReadFormat(buf + 4, "I1", ibuf);
2180 symops[0] = strtok_r(buf + 4, ",", &brks);
2181 symops[1] = strtok_r(NULL, ",", &brks);
2182 symops[2] = strtok_r(NULL, ",", &brks);
2183 if (sMoleculeSymopStringsToTransform(symops, tr)) {
2184 s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2187 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2190 } else if (strncmp(buf, "RESI", 4) == 0) {
2191 for (p1 = buf + 4; isspace(*p1); p1++);
2193 for (p2 = p1 + 1; isalnum(*p2); p2++);
2195 strncpy(currentResName, p1, 4);
2196 currentResName[4] = 0;
2198 } else currentResName[0] = 0;
2199 sscanf(buf + 4, " %d", ¤tResSeq);
2202 /* Atom name: [A-Za-z]{1,2}[0-9]* */
2203 for (p1 = buf; p1 < buf + 2 && (isalpha(*p1) && *p1 != '_'); p1++);
2205 while (isdigit(*p1))
2208 if (p1 > buf && p1 <= buf + 4 && isspace(*p1)) {
2212 int atomIndex = mp->natoms;
2213 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2214 memset(ap, 0, gSizeOfAtomRecord);
2215 strncpy(ap->aname, buf, 4);
2216 n = sscanf(p1, " %d %f %f %f %f %f %f %2s", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, cont);
2217 if (n == 8 && strcmp(cont, "=") == 0) {
2218 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2219 s_append_asprintf(errbuf, "line %d: unexpected end of file within the atom cards", lineNumber);
2222 sscanf(buf, " %f %f %f %f", fbuf+6, fbuf+7, fbuf+8, fbuf+9);
2224 } else n = 5; /* Iso */
2228 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2229 ap->occupancy = fbuf[3];
2230 if (ap->aname[0] != 'Q' && ibuf[0] >= 1 && ibuf[0] <= nsfacs) {
2231 strncpy(ap->element, sfacs[ibuf[0] - 1], 2);
2233 /* sAtomSetElement(ap, -1, sfacs[ibuf[0] - 1]); */
2234 /* strncpy(ap->element, sfacs[ibuf[0] - 1], 4);
2235 ap->atomicNumber = ElementToInt(ap->element); */
2237 sAtomSetElement(ap, -1, ap->name); */
2238 /* ap->atomicNumber = AtomNameToElement(ap->name);
2239 ElementToString(ap->atomicNumber, ap->element); */
2242 if (n == 10 || fbuf[4] >= 0.0) {
2244 /* Read in the standard deviations */
2245 ReadLine(buf, sizeof buf, fp, &lineNumber);
2246 for (i = 0; i < 9; i++) {
2250 dbuf[i] = strtod(buf + j, NULL);
2253 ap->sigma.x = dbuf[0];
2254 ap->sigma.y = dbuf[1];
2255 ap->sigma.z = dbuf[2];
2258 ap->tempFactor = fbuf[4] * 78.9568352087147; /* 8*pi*pi */
2260 MoleculeSetAniso(mp, atomIndex, 8, fbuf[4], fbuf[5], fbuf[6], fbuf[9], fbuf[7], fbuf[8], dbuf);
2261 ap->resSeq = currentResSeq;
2262 strncpy(ap->resName, currentResName, 4);
2269 /* Add symmetry operations according to the lattice type */
2270 switch (latticeType < 0 ? -latticeType : latticeType) {
2271 static Transform tr_i = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0.5};
2272 static Transform tr_c = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0};
2273 static Transform tr_a = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.5};
2274 static Transform tr_b = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5};
2275 static Transform tr_r1 = {0, 1, 0, -1, -1, 0, 0, 0, 1, 0, 0, 0};
2276 static Transform tr_r2 = {-1, -1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0};
2280 sMoleculeGenerateSymopWithTransform(mp, tr_i, 0);
2282 case 3: /* Rhombohedral obverse on hexagonal axes */
2284 sMoleculeGenerateSymopWithTransform(mp, tr_r1, n);
2285 sMoleculeGenerateSymopWithTransform(mp, tr_r2, n);
2289 sMoleculeGenerateSymopWithTransform(mp, tr_a, n);
2290 sMoleculeGenerateSymopWithTransform(mp, tr_b, n);
2291 sMoleculeGenerateSymopWithTransform(mp, tr_c, n);
2294 sMoleculeGenerateSymopWithTransform(mp, tr_a, 0);
2297 sMoleculeGenerateSymopWithTransform(mp, tr_b, 0);
2300 sMoleculeGenerateSymopWithTransform(mp, tr_c, 0);
2304 if (latticeType > 0) {
2305 static Transform tr_inv = {-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0};
2306 sMoleculeGenerateSymopWithTransform(mp, tr_inv, 0);
2309 MoleculeGuessBonds(mp, 1.2, &nbonds, &bonds);
2311 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2314 mp->nframes = -1; /* Should be recalculated later */
2317 Panic("low memory while reading structure file %s", fname);
2318 return -1; /* not reached */
2321 /* Add one gaussian orbital shell information (not undoable) */
2323 MoleculeAddGaussianOrbitalShell(Molecule *mol, Int sym, Int nprims, Int a_idx)
2328 return -1; /* Molecule is empty */
2331 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2333 return -2; /* Low memory */
2335 shellp = AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), bset->nshells, NULL);
2337 return -2; /* Low memory */
2339 case 0: shellp->sym = kGTOType_S; shellp->ncomp = 1; break;
2340 case 1: shellp->sym = kGTOType_P; shellp->ncomp = 3; break;
2341 case -1: shellp->sym = kGTOType_SP; shellp->ncomp = 4; break;
2342 case 2: shellp->sym = kGTOType_D; shellp->ncomp = 6; break;
2343 case -2: shellp->sym = kGTOType_D5; shellp->ncomp = 5; break;
2344 case 3: shellp->sym = kGTOType_F; shellp->ncomp = 10; break;
2345 case -3: shellp->sym = kGTOType_F7; shellp->ncomp = 7; break;
2346 case 4: shellp->sym = kGTOType_G; shellp->ncomp = 15; break;
2347 case -4: shellp->sym = kGTOType_G9; shellp->ncomp = 9; break;
2349 return -3; /* Unsupported shell type */
2351 shellp->nprim = nprims;
2352 shellp->a_idx = a_idx;
2353 if (bset->shells < shellp) {
2354 shellp->m_idx = shellp[-1].m_idx + shellp[-1].ncomp;
2355 shellp->p_idx = shellp[-1].p_idx + shellp[-1].nprim;
2363 /* Add a set of gaussian primitive coefficients (not undoable) */
2365 MoleculeAddGaussianPrimitiveCoefficients(Molecule *mol, Double exponent, Double contraction, Double contraction_sp)
2370 return -1; /* Molecule is empty */
2373 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2375 return -2; /* Low memory */
2377 primp = AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), bset->npriminfos, NULL);
2379 return -2; /* Low memory */
2380 primp->A = exponent;
2381 primp->C = contraction;
2382 primp->Csp = contraction_sp;
2386 /* Set MO coefficients for idx-th MO */
2388 MoleculeSetMOCoefficients(Molecule *mol, Int idx, Double energy, Int ncomps, Double *coeffs)
2393 return -1; /* Molecule is empty */
2396 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2398 return -2; /* Low memory */
2400 if (bset->nmos == 0) {
2401 if (bset->nshells > 0) {
2402 /* Shell info is already set: calculate the number of MOs from there */
2403 for (i = n = 0; i < bset->nshells; i++)
2404 n += bset->shells[i].ncomp;
2406 } else if (ncomps > 0) {
2407 bset->ncomps = ncomps;
2409 if (bset->rflag == 0)
2410 bset->nmos = bset->ncomps * 2;
2412 bset->nmos = bset->ncomps;
2413 if (bset->nmos <= 0)
2414 return -3; /* Bad or inconsistent number of MOs */
2415 bset->mo = (Double *)calloc(sizeof(Double), bset->nmos * bset->ncomps);
2416 bset->moenergies = (Double *)calloc(sizeof(Double), bset->nmos);
2417 if (bset->mo == NULL || bset->moenergies == NULL) {
2418 if (bset->mo != NULL)
2420 if (bset->moenergies != NULL)
2421 free(bset->moenergies);
2423 bset->moenergies = NULL;
2425 return -2; /* Low memory */
2428 if (idx < 0 || idx >= bset->nmos)
2429 return -4; /* Bad MO index */
2430 if (energy != -1000000)
2431 bset->moenergies[idx] = energy;
2432 if (ncomps < bset->ncomps)
2433 return -5; /* Insufficient number of data provided */
2434 memmove(bset->mo + (idx * bset->ncomps), coeffs, sizeof(Double) * bset->ncomps);
2435 if (bset->cns != NULL) {
2436 /* Clear the cached values */
2444 /* Allocate BasisSet record. rflag: UHF, 0; RHF, 1; ROHF, 2
2445 ne_alpha: number of alpha electrons, ne_beta: number of beta electrons
2446 The natoms and pos are copied from mol. */
2448 MoleculeAllocateBasisSetRecord(Molecule *mol, Int rflag, Int ne_alpha, Int ne_beta)
2453 if (mol == NULL || mol->natoms == 0)
2454 return -1; /* Molecule is empty */
2457 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2459 return -2; /* Low memory */
2461 if (bset->pos != NULL) {
2465 bset->natoms = mol->natoms;
2466 bset->pos = (Vector *)calloc(sizeof(Vector), bset->natoms);
2467 if (bset->pos == NULL)
2468 return -2; /* Low memory */
2469 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
2470 bset->pos[i].x = ap->r.x * kAngstrom2Bohr;
2471 bset->pos[i].y = ap->r.y * kAngstrom2Bohr;
2472 bset->pos[i].z = ap->r.z * kAngstrom2Bohr;
2474 bset->ne_alpha = ne_alpha;
2475 bset->ne_beta = ne_beta;
2476 bset->rflag = rflag;
2481 sSeparateTokens(char *inString, char **outPtr, int size)
2485 for (i = 0; i < size; i++) {
2486 p = strtok((i == 0 ? inString : NULL), " \r\n");
2497 sReadNumberArray(void *basep, Int *countp, Int size, Int num, FILE *fp, int *lnp)
2501 *((void **)basep) = NULL;
2503 if (AssignArray(basep, countp, size, num - 1, NULL) == NULL)
2504 return 4; /* Out of memory */
2506 while (ReadLine(buf, sizeof buf, fp, lnp) > 0) {
2507 char *tokens[16], *p;
2508 sSeparateTokens(buf, tokens, 16);
2509 for (i = 0; i < 16; i++) {
2510 if (tokens[i] == NULL)
2512 if (size == sizeof(Int)) {
2513 (*((Int **)basep))[n] = strtol(tokens[i], &p, 0);
2514 } else if (size == sizeof(Double)) {
2515 (*((Double **)basep))[n] = strtod(tokens[i], &p);
2516 } else return -1; /* Internal error */
2517 if (tokens[i] == p || *p != 0)
2518 return 1; /* Non-digit character */
2520 if (i < 15 && tokens[i + 1] != NULL)
2521 return 2; /* Too many data */
2522 return 0; /* All data are successfully read */
2526 return 3; /* Unexpected EOF */
2530 sSetupGaussianCoefficients(BasisSet *bset)
2537 /* Cache the contraction coefficients for efficient calculation */
2538 /* Sum up the number of components for all primitives */
2539 for (i = k = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2541 k += sp->nprim * sp->ncomp;
2543 /* Allocate memory for the cached values */
2544 if (AssignArray(&bset->cns, &bset->ncns, sizeof(Double), k - 1, NULL) == NULL)
2546 /* Iterate over all primitives */
2548 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2549 for (j = 0, pp = bset->priminfos + sp->p_idx; j < sp->nprim; j++, pp++) {
2552 // (8 alpha^3/pi^3)^0.25 exp(-alpha r^2)
2553 *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2556 // (128 alpha^5/pi^3)^0.25 [x|y|z]exp(-alpha r^2)
2557 d = pp->C * pow(pp->A, 1.25) * 1.425410941;
2563 *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
2564 d = pp->Csp * pow(pp->A, 1.25) * 1.425410941;
2570 // xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2)
2571 // xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2572 d = pp->C * pow(pp->A, 1.75);
2573 dp[0] = dp[1] = dp[2] = d * 1.645922781;
2574 dp[3] = dp[4] = dp[5] = d * 2.850821881;
2578 // 3zz-rr: (128 alpha^7/9pi^3)^0.25 (3zz-rr)exp(-alpha r^2)
2579 // xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
2580 // xx-yy: (128 alpha^7/pi^3)^0.25 (xx-yy)exp(-alpha r^2)
2581 d = pp->C * pow(pp->A, 1.75);
2582 dp[0] = d * 0.822961390;
2583 dp[1] = dp[2] = dp[4] = d * 2.850821881;
2584 dp[3] = d * 1.425410941;
2587 /* TODO: Support F/F7 and G/G9 type orbitals */
2595 MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char **errbuf)
2600 int natoms, nbasis, i, j, k, n, mxbond, retval, ncomps, nprims, nelec;
2614 bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2618 fp = fopen(fname, "rb");
2620 s_append_asprintf(errbuf, "Cannot open file");
2624 natoms = nbasis = -1;
2632 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2635 if (lineNumber == 2) {
2638 bset->rflag = 0; /* UHF */
2639 else if (buf[11] == 'O')
2640 bset->rflag = 2; /* ROHF */
2641 else bset->rflag = 1; /* RHF */
2644 while (p > buf && *p == ' ')
2647 sSeparateTokens(buf + 42, tokens, 16);
2648 if (strcmp(buf, "Number of atoms") == 0) {
2649 if (tokens[1] == NULL || (natoms = atoi(tokens[1])) <= 0) {
2650 s_append_asprintf(errbuf, "Line %d: strange number of atoms: %s", lineNumber, tokens[1]);
2654 /* Allocate atom records (all are empty for now) */
2655 AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms - 1, NULL);
2656 /* Also allocate atom position array for MO calculations */
2657 AssignArray(&bset->pos, &bset->natoms, sizeof(Vector), natoms - 1, NULL);
2658 /* Also allocate nuclear charge array */
2659 bset->nuccharges = (Double *)calloc(sizeof(Double), natoms);
2660 } else if (strcmp(buf, "Number of electrons") == 0) {
2661 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2662 s_append_asprintf(errbuf, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
2667 } else if (strcmp(buf, "Number of alpha electrons") == 0) {
2668 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2669 s_append_asprintf(errbuf, "Line %d: strange number of alpha electrons: %s", lineNumber, tokens[1]);
2674 } else if (strcmp(buf, "Number of beta electrons") == 0) {
2675 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
2676 s_append_asprintf(errbuf, "Line %d: strange number of beta electrons: %s", lineNumber, tokens[1]);
2681 if (bset->ne_alpha + bset->ne_beta != nelec) {
2682 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);
2686 } else if (strcmp(buf, "Number of basis functions") == 0) {
2687 if (tokens[1] == NULL || (nbasis = atoi(tokens[1])) <= 0) {
2688 s_append_asprintf(errbuf, "Line %d: strange number of basis functions: %s", lineNumber, tokens[1]);
2692 } else if (strcmp(buf, "Atomic numbers") == 0) {
2693 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2694 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2698 if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms, fp, &lineNumber) != 0) {
2699 s_append_asprintf(errbuf, "Line %d: cannot read atomic numbers", lineNumber);
2703 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2704 ap->atomicNumber = iary[i];
2705 bset->nuccharges[i] = iary[i];
2706 ElementToString(ap->atomicNumber, ap->element);
2707 memmove(ap->aname, ap->element, 4);
2708 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
2713 } else if (strcmp(buf, "Nuclear charges") == 0) {
2714 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
2715 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
2719 if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms, fp, &lineNumber) != 0) {
2720 s_append_asprintf(errbuf, "Line %d: cannot read nuclear charges", lineNumber);
2724 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
2725 bset->nuccharges[i] = dary[i];
2729 } else if (strcmp(buf, "Current cartesian coordinates") == 0) {
2730 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * 3) {
2731 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of cartesian coordinates: %s", lineNumber, tokens[2]);
2735 if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms * 3, fp, &lineNumber) != 0) {
2736 s_append_asprintf(errbuf, "Line %d: cannot read cartesian coordinates", lineNumber);
2740 for (i = 0, ap = mp->atoms, vp = bset->pos; i < natoms; i++, ap = ATOM_NEXT(ap), vp++) {
2741 vp->x = dary[i * 3];
2742 vp->y = dary[i * 3 + 1];
2743 vp->z = dary[i * 3 + 2];
2744 ap->r.x = vp->x * kBohr2Angstrom;
2745 ap->r.y = vp->y * kBohr2Angstrom;
2746 ap->r.z = vp->z * kBohr2Angstrom;
2750 } else if (strcmp(buf, "MxBond") == 0) {
2751 if (tokens[1] == NULL || (mxbond = atoi(tokens[1])) <= 0) {
2752 s_append_asprintf(errbuf, "Line %d: strange number of bonds per atom: %s", lineNumber, tokens[1]);
2756 } else if (strcmp(buf, "IBond") == 0) {
2758 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * mxbond) {
2759 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of bonds: %s", lineNumber, tokens[2]);
2763 if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms * mxbond, fp, &lineNumber) != 0) {
2764 s_append_asprintf(errbuf, "Line %d: cannot read bond information", lineNumber);
2768 bonds = (Int *)malloc(sizeof(Int) * (mxbond * 2 + 1));
2769 for (i = 0; i < natoms; i++) {
2770 for (j = k = 0; j < mxbond; j++) {
2771 n = iary[i * mxbond + j] - 1;
2773 /* Connect atom i and atom n */
2779 bonds[k] = kInvalidIndex;
2780 MoleculeAddBonds(mp, k / 2, bonds, NULL, 1);
2786 } else if (strcmp(buf, "Shell types") == 0) {
2787 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0) {
2788 s_append_asprintf(errbuf, "Line %d: wrong number of shell types: %s", lineNumber, tokens[2]);
2792 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2793 s_append_asprintf(errbuf, "Line %d: cannot read shell types", lineNumber);
2797 /* Allocate ShellInfo table and store shell type information */
2798 AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), nary - 1, NULL);
2799 for (i = n = 0, sp = bset->shells; i < nary; i++, sp++) {
2801 case 0: sp->sym = kGTOType_S; sp->ncomp = 1; break;
2802 case 1: sp->sym = kGTOType_P; sp->ncomp = 3; break;
2803 case -1: sp->sym = kGTOType_SP; sp->ncomp = 4; break;
2804 case 2: sp->sym = kGTOType_D; sp->ncomp = 6; break;
2805 case -2: sp->sym = kGTOType_D5; sp->ncomp = 5; break;
2806 case 3: sp->sym = kGTOType_F; sp->ncomp = 10; break;
2807 case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break;
2808 case 4: sp->sym = kGTOType_G; sp->ncomp = 15; break;
2809 case -4: sp->sym = kGTOType_G9; sp->ncomp = 9; break;
2811 s_append_asprintf(errbuf, "Line %d: unsupported shell type %d", lineNumber, iary[i]);
2818 bset->ncomps = ncomps = n;
2821 } else if (strcmp(buf, "Number of primitives per shell") == 0) {
2822 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2823 s_append_asprintf(errbuf, "Line %d: wrong size of the primitive table: %s", lineNumber, tokens[2]);
2827 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2828 s_append_asprintf(errbuf, "Line %d: cannot read primitive table", lineNumber);
2832 for (i = n = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2833 sp->nprim = iary[i];
2840 } else if (strcmp(buf, "Shell to atom map") == 0) {
2841 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
2842 s_append_asprintf(errbuf, "Line %d: wrong size of the shell-to-atom map: %s", lineNumber, tokens[2]);
2846 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
2847 s_append_asprintf(errbuf, "Line %d: cannot read shell-to-atom table", lineNumber);
2851 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
2852 sp->a_idx = iary[i] - 1;
2856 } else if (strcmp(buf, "Primitive exponents") == 0) {
2857 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nprims) {
2858 s_append_asprintf(errbuf, "Line %d: wrong number of primitive exponents: %s", lineNumber, tokens[2]);
2862 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2863 s_append_asprintf(errbuf, "Line %d: cannot read primitive exponents", lineNumber);
2867 /* Allocate PrimInfo table */
2868 AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), nprims - 1, NULL);
2869 for (i = 0, pp = bset->priminfos; i < nprims; i++, pp++) {
2874 } else if (strcmp(buf, "Contraction coefficients") == 0) {
2875 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2876 s_append_asprintf(errbuf, "Line %d: wrong number of contraction coefficients: %s", lineNumber, tokens[2]);
2880 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2881 s_append_asprintf(errbuf, "Line %d: cannot read contraction coefficients", lineNumber);
2885 for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2890 } else if (strcmp(buf, "P(S=P) Contraction coefficients") == 0) {
2891 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
2892 s_append_asprintf(errbuf, "Line %d: wrong number of P(S=P) contraction coefficients: %s", lineNumber, tokens[2]);
2896 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2897 s_append_asprintf(errbuf, "Line %d: cannot read P(S=P) contraction coefficients", lineNumber);
2901 for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
2906 } else if (strcmp(buf, "Alpha Orbital Energies") == 0) {
2907 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2908 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha orbitals: %s", lineNumber, tokens[2]);
2912 if (sReadNumberArray(&bset->moenergies, &bset->nmos, sizeof(Double), i, fp, &lineNumber) != 0) {
2913 s_append_asprintf(errbuf, "Line %d: cannot read alpha orbital energies", lineNumber);
2917 } else if (strcmp(buf, "Alpha MO coefficients") == 0) {
2918 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2919 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha MO coefficients: %s", lineNumber, tokens[2]);
2923 if (sReadNumberArray(&bset->mo, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2924 s_append_asprintf(errbuf, "Line %d: cannot read MO coefficients", lineNumber);
2928 } else if (strcmp(buf, "Beta Orbital Energies") == 0) {
2929 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
2930 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta orbitals: %s", lineNumber, tokens[2]);
2934 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2935 s_append_asprintf(errbuf, "Line %d: cannot read beta orbital energies", lineNumber);
2939 bset->moenergies = (Double *)realloc(bset->moenergies, sizeof(Double) * 2 * ncomps);
2940 bset->nmos = ncomps * 2;
2941 bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);
2942 memmove(bset->moenergies + ncomps, dary, sizeof(Double) * ncomps);
2943 memset(bset->mo + ncomps * ncomps, 0, sizeof(Double) * ncomps * ncomps);
2946 } else if (strcmp(buf, "Beta MO coefficients") == 0) {
2947 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
2948 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta MO coefficients: %s", lineNumber, tokens[2]);
2952 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2953 s_append_asprintf(errbuf, "Line %d: cannot read alpha MO coefficients", lineNumber);
2957 bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps); /* Should be unnecessary, just in case */
2958 memmove(bset->mo + ncomps * ncomps, dary, sizeof(Double) * ncomps * ncomps);
2961 } else if (strcmp(buf, "Total SCF Density") == 0) {
2962 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * (ncomps + 1) / 2) {
2963 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of SCF densities: %s", lineNumber, tokens[2]);
2967 if (sReadNumberArray(&bset->scfdensities, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
2968 s_append_asprintf(errbuf, "Line %d: cannot read SCF densities", lineNumber);
2974 if (mp->natoms == 0) {
2975 s_append_asprintf(errbuf, "Atom information is missing");
2979 if (bset->shells == NULL || bset->priminfos == NULL) {
2980 s_append_asprintf(errbuf, "Gaussian primitive information is missing");
2984 if (bset->mo == NULL) {
2985 s_append_asprintf(errbuf, "MO coefficients were not found");
2989 if (sSetupGaussianCoefficients(bset) != 0) {
2990 s_append_asprintf(errbuf, "Internal error during setup MO calculation");
3003 if (mp->bset != NULL) {
3004 BasisSetRelease(mp->bset);
3010 Panic("low memory while reading fchk file %s", fname);
3011 return -1; /* not reached */
3015 MoleculeLoadGamessDatFile(Molecule *mol, const char *fname, char **errbuf)
3020 int lineNumber, i, j, k, len, natoms = 0;
3027 Vector *vbuf = NULL;
3029 int optimizing = 0, status = 0;
3033 mol = MoleculeNew();
3035 if (mol->natoms == 0)
3038 fp = fopen(fname, "rb");
3040 s_append_asprintf(errbuf, "Cannot open file");
3044 /* ESP is cleared (not undoable!) */
3045 if (mol->elpots != NULL) {
3052 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3055 if (strncmp(buf, " $DATA", 6) == 0) {
3056 /* Initial geometry */
3058 vbuf = (Vector *)calloc(sizeof(Vector), mol->natoms);
3061 ReadLine(buf, sizeof buf, fp, &lineNumber); /* Title */
3062 ReadLine(buf, sizeof buf, fp, &lineNumber); /* Symmetry */
3063 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3064 if (strncmp(buf, " $END", 5) == 0)
3066 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3067 s_append_asprintf(errbuf, "Line %d: bad format in $DATA section", lineNumber);
3073 memset(&a, 0, sizeof(a));
3074 strncpy(a.aname, sval, 4);
3078 a.atomicNumber = (Int)dval[0];
3079 strncpy(a.element, ElementToString(a.atomicNumber, sval), 3);
3080 a.type = AtomTypeEncodeToUInt(a.element);
3081 a.weight = WeightForAtomicNumber(a.atomicNumber);
3082 MoleculeCreateAnAtom(mol, &a, mol->natoms);
3085 if (i >= mol->natoms) {
3086 s_append_asprintf(errbuf, "Line %d: too many atoms", lineNumber);
3090 if ((ap = ATOM_AT_INDEX(mol->atoms, i))->atomicNumber != dval[0]) {
3091 s_append_asprintf(errbuf, "Line %d: atomic number does not match", lineNumber);
3095 vbuf[i].x = dval[1];
3096 vbuf[i].y = dval[2];
3097 vbuf[i].z = dval[3];
3099 /* Skip until a blank line is found */
3100 /* 2013.6.11. Line including "PM3" is also recognized as the end of atom */
3101 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3102 for (j = 0; buf[j] == ' '; j++);
3103 if (buf[j] == '\n' || strncmp(buf, "PM3", 3) == 0)
3110 /* Set atom positions */
3112 if (natoms < mol->natoms) {
3113 s_append_asprintf(errbuf, "Line %d: too few atoms", lineNumber);
3117 ig = IntGroupNewWithPoints(0, natoms, -1);
3118 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, natoms, vbuf);
3119 IntGroupRelease(ig);
3122 vbuf = (Vector *)calloc(sizeof(Vector), natoms);
3123 nframes = MoleculeGetNumberOfFrames(mol);
3127 } else if (strstr(buf, "DATA FROM NSERCH") != NULL || (strstr(buf, "RESULTS FROM SUCCESSFUL") != NULL && (n1 = 1))) {
3128 /* Skip until the separator line is read (three or four lines) */
3132 s_append_asprintf(errbuf, "Line %d: the separator line at the top of the coordinates is not found: bad format?", lineNumber);
3136 ReadLine(buf, sizeof buf, fp, &lineNumber);
3137 } while (strstr(buf, "----------------------------") == NULL);
3138 for (i = 0; i < natoms; i++) {
3139 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3140 s_append_asprintf(errbuf, "Unexpected end of file in reading NSERCH data");
3144 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3145 s_append_asprintf(errbuf, "Line %d: bad format in NSERCH coordinate data", lineNumber);
3149 vbuf[i].x = dval[1];
3150 vbuf[i].y = dval[2];
3151 vbuf[i].z = dval[3];
3153 ig = IntGroupNewWithPoints(nframes, 1, -1);
3154 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, natoms, vbuf, 0, NULL);
3155 IntGroupRelease(ig);
3158 optimizing = 1; /* Flag to skip reading the VEC group */
3162 } else if (strstr(buf, "E(UHF)") != NULL || (strstr(buf, "E(RHF)") != NULL && (n1 = 1)) || (strstr(buf, "E(ROHF)") != NULL && (n1 = 2))) {
3163 if (mol->bset == NULL) {
3164 i = MoleculeAllocateBasisSetRecord(mol, n1, 0, 0);
3166 s_append_asprintf(errbuf, "Line %d: cannot allocate basis set internal buffer", lineNumber);
3171 } else if (strncmp(buf, " $VEC", 5) == 0) {
3173 /* Read the vec group */
3174 if (mol->bset == NULL || mol->bset->ncomps == 0)
3175 continue; /* Just ignore */
3177 continue; /* Ignore VEC group during optimization */
3178 coeffs = (Double *)calloc(sizeof(Double), mol->bset->ncomps);
3179 if (coeffs == NULL) {
3180 s_append_asprintf(errbuf, "Line %d: low memory during $VEC", lineNumber);
3185 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3187 if (strncmp(buf, " $END", 5) == 0)
3189 while ((j = 5 + (k % 5) * 15) <= len && buf[j] != 0 && buf[j] != '\n') {
3190 strncpy(sval, buf + j, 15);
3192 coeffs[k] = strtod(sval, NULL);
3197 if (k < mol->bset->ncomps)
3199 j = MoleculeSetMOCoefficients(mol, i, -1000000, k, coeffs);
3201 s_append_asprintf(errbuf, "Line %d: cannot set coefficients for MO %d", lineNumber, i + 1);
3212 } else if ((strstr(buf, "ELECTRIC POTENTIAL") != NULL || strstr(buf, "ELECTROSTATIC POTENTIAL") != NULL) && strstr(buf, "ELPOTT") != NULL) {
3214 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3216 if (strstr(buf, "TOTAL NUMBER OF GRID POINTS") != NULL)
3218 if (sscanf(buf, "%d %lf %lf %lf %lf", &ival[0], &dval[0], &dval[1], &dval[2], &dval[3]) < 5)
3220 ep = AssignArray(&mol->elpots, &mol->nelpots, sizeof(Elpot), i, NULL);
3221 ep->pos.x = dval[0];
3222 ep->pos.y = dval[1];
3223 ep->pos.z = dval[2];
3228 goto redo; /* This section has no end line, so the last line should be processed again */
3229 else break; /* End of file encountered or interrupted */
3230 } /* TODO: read MOLPLT info if present */
3233 s_append_asprintf(errbuf, "User interrupt at line %d", lineNumber);
3239 if (mol->natoms > 0)
3240 retval = 0; /* Return the partially constructed molecule */
3241 if (newmol && mol->nbonds == 0) {
3244 MoleculeGuessBonds(mol, 1.2, &nbonds, &bonds);
3246 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
3254 MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
3257 if (ftype == NULL || *ftype == 0) {
3259 cp = strrchr(fname, '.');
3263 cp = guessMoleculeType(fname);
3264 if (strcmp(cp, "???") != 0)
3268 if (strcasecmp(ftype, "pdb") == 0) {
3269 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3272 /* Try all formats once again */
3273 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3279 MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char **errbuf)
3285 int i, j, new_unit, retval;
3293 fp = fopen(fname, "rb");
3295 s_append_asprintf(errbuf, "Cannot open file");
3298 /* flockfile(fp); */
3299 if (mp->natoms == 0)
3302 /* Allocate buffer for undo-capable modification */
3303 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
3304 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3305 /* Retain current position if the atom info is missing in the input file */
3311 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3312 if (strncmp(buf, "END", 3) == 0)
3314 if (strncmp(buf, "HETATM", 6) == 0 || strncmp(buf, "ATOM", 4) == 0) {
3316 Int serial, intCharge, resSeq;
3319 char segName[5], resName[4], atomName[5], resSeqStr[5], atomType[3], element[3], occStr[6];
3321 memset(&w, 0, sizeof(w));
3322 ReadFormat(buf, "x6 I5 x1 S4 x1 S3 x1 x1 S4 x1 x3 F8 F8 F8 S6 F6 x6 S4 S2 I2",
3323 &w.serial, w.atomName, w.resName, w.resSeqStr, &w.r.x, &w.r.y, &w.r.z,
3324 w.occStr, &w.temp, w.segName, w.element, &w.intCharge);
3325 if (w.atomName[0] == 0) {
3326 continue; /* Atom name is empty */
3328 /* A workaround for residue number >= 10000 (XPLOR style) */
3329 if (w.resSeqStr[0] >= 'A' && w.resSeqStr[0] <= 'Z') {
3330 w.resSeq = (w.resSeqStr[0] - 'A' + 10) * 1000 + atoi(w.resSeqStr + 1);
3332 w.resSeq = atoi(w.resSeqStr);
3334 if (w.element[0] == 0) {
3335 /* $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl */
3336 for (p = w.atomName; *p != 0; p++) {
3337 if (isalpha(*p) && *p != '_') {
3338 w.element[0] = toupper(*p);
3339 if (isalpha(p[1]) && p[1] != '_') {
3340 w.element[1] = toupper(p[1]);
3349 if (w.occStr[0] == 0)
3352 w.occ = atof(w.occStr);
3353 if (w.serial <= 0) {
3354 s_append_asprintf(errbuf, "line %d: non-positive atom number %d", lineNumber, w.serial);
3358 w.serial--; /* The internal atom number is 0-based */
3359 if (w.serial >= mp->natoms) {
3361 /* Create a new atom entry */
3362 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, w.serial, NULL);
3364 s_append_asprintf(errbuf, "line %d: the atom number %d does not exist in the structure file", lineNumber, w.serial+1);
3370 ap = ATOM_AT_INDEX(mp->atoms, w.serial);
3372 ap->occupancy = w.occ;
3373 ap->tempFactor = w.temp;
3374 if (w.segName[0] == 0)
3375 strncpy(w.segName, "MAIN", 4);
3376 strncpy(ap->segName, w.segName, 4);
3377 ap->resSeq = w.resSeq;
3378 strncpy(ap->resName, w.resName, 4);
3379 strncpy(ap->aname, w.atomName, 4);
3380 strncpy(ap->element, w.element, 2);
3382 ap->atomicNumber = ElementToInt(ap->element);
3383 ap->type = AtomTypeEncodeToUInt(ap->element);
3384 ap->weight = WeightForAtomicNumber(ap->atomicNumber);
3385 ap->intCharge = w.intCharge;
3386 if (ap->resSeq > 0) {
3387 if (ap->resSeq < mp->nresidues) {
3388 /* Update the resName according to residues[] */
3389 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
3391 /* Register the resName to residues[] */
3392 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, w.resName);
3396 strcpy(ap->resName, "XXX");
3397 if (mp->nresidues == 0)
3398 AssignArray(&mp->residues, &mp->nresidues, 4, 0, ap->resName);
3400 i = ElementToInt(ap->element);
3402 ap->weight = gElementParameters[i].weight;
3404 /* Not a new unit: only the atom position is updated */
3408 } else if (strncmp(buf, "CONECT", 6) == 0 && new_unit) {
3409 i = ReadFormat(buf, "x6 I5I5I5I5I5I5I5I5I5I5I5I5",
3410 ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
3411 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7,
3412 ibuf + 8, ibuf + 9, ibuf + 10, ibuf + 11);
3416 for (j = 0; j < i; j++) {
3417 if (ibuf[j] < 0 || ibuf[j] > mp->natoms) {
3418 s_append_asprintf(errbuf, "line %d: The CONECT record contains non-existent atom %d", lineNumber, ibuf[j]);
3421 } else if (ibuf[j] == 0)
3427 for (j = 1, bi = 0; j < i; j++) {
3428 if (ibuf[0] < ibuf[j]) {
3429 if (MoleculeLookupBond(mp, ibuf[0], ibuf[j]) >= 0) {
3430 s_append_asprintf(errbuf, "line %d: warning: duplicate bond %d-%d\n", lineNumber, ibuf[0], ibuf[j]);
3432 bbuf[bi * 2] = ibuf[0] - 1;
3433 bbuf[bi * 2 + 1] = ibuf[j] - 1;
3441 retval = MoleculeAddBonds(mp, bi, bbuf, NULL, 1);
3443 s_append_asprintf(errbuf, "line %d: bad bond specification", lineNumber);
3450 /* funlockfile(fp); */
3453 /* Renumber atoms if some atom number is unoccupied */
3454 int *old2new, oldidx, newidx;
3455 old2new = (int *)calloc(sizeof(int), mp->natoms);
3456 if (old2new == NULL) {
3457 s_append_asprintf(errbuf, "Out of memory");
3461 for (oldidx = newidx = 0; oldidx < mp->natoms; oldidx++) {
3462 ap = ATOM_AT_INDEX(mp->atoms, oldidx);
3463 if (ap->aname[0] != 0) {
3464 old2new[oldidx] = newidx;
3465 if (oldidx > newidx)
3466 memmove(ATOM_AT_INDEX(mp->atoms, newidx), ap, gSizeOfAtomRecord);
3470 mp->natoms = newidx;
3471 if (oldidx > newidx) {
3472 /* Renumber the connects and bonds */
3474 for (i = 0; i < mp->natoms; i++) {
3475 ap = ATOM_AT_INDEX(mp->atoms, i);
3476 cp = AtomConnectData(&ap->connect);
3477 for (j = 0; j < ap->connect.count; j++) {
3478 cp[j] = old2new[cp[j]];
3481 for (i = 0; i < mp->nbonds * 2; i++) {
3482 mp->bonds[i] = old2new[mp->bonds[i]];
3485 retval = MoleculeRebuildTablesFromConnects(mp);
3487 /* This error may not happen */
3488 s_append_asprintf(errbuf, "Cannot build angle/dihedral/improper tables");
3492 /* Undo action: delete all atoms */
3495 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3496 act = MolActionNew(gMolActionUnmergeMolecule, ig);
3497 act->frame = mp->cframe;
3498 MolActionCallback_registerUndo(mp, act);
3499 MolActionRelease(act);
3500 IntGroupRelease(ig);
3503 /* Set the new atom positions */
3504 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3505 MolActionCreateAndPerform(mp, gMolActionSetAtomPositions, ig, mp->natoms, vp);
3506 IntGroupRelease(ig);
3510 mp->nframes = -1; /* Should be recalculated later */
3512 return 1; /* No atoms */
3516 /* funlockfile(fp); */
3522 return 1; /* Maybe different format? */
3527 MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char **errbuf)
3530 SFloat32 *xp, *yp, *zp;
3533 int n, errcount = 0;
3535 if (mp == NULL || mp->natoms == 0) {
3536 s_append_asprintf(errbuf, "Molecule is empty");
3539 n = DcdOpen(fname, &dcd);
3542 case -2: s_append_asprintf(errbuf, "Cannot open file"); break;
3543 case 1: s_append_asprintf(errbuf, "Premature EOF encountered"); break;
3544 case 2: s_append_asprintf(errbuf, "Bad block length of the first section"); break;
3545 case 3: s_append_asprintf(errbuf, "\"CORD\" signature is missing"); break;
3546 case 4: s_append_asprintf(errbuf, "Bad termination of the first section"); break;
3547 case 5: s_append_asprintf(errbuf, "The title section is not correct"); break;
3548 case 6: s_append_asprintf(errbuf, "The atom number section is not correct"); break;
3549 default: s_append_asprintf(errbuf, "Read error in dcd file"); break;
3553 if (dcd.natoms == 0) {
3554 s_append_asprintf(errbuf, "No atoms were found in the dcd file");
3556 } else if (dcd.nframes == 0) {
3557 s_append_asprintf(errbuf, "No frames were found in the dcd file");
3567 vp = (Vector *)calloc(sizeof(Vector), mp->natoms * dcd.nframes);
3569 cp = (Vector *)calloc(sizeof(Vector), dcd.nframes * 4);
3571 xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3572 yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3573 zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
3574 ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mp), dcd.nframes, -1);
3575 if (vp == NULL || xp == NULL || yp == NULL || zp == NULL || ig == NULL) {
3576 s_append_asprintf(errbuf, "Cannot allocate memory");
3582 if (ig) IntGroupRelease(ig);
3585 for (n = 0; n < dcd.nframes; n++) {
3588 SFloat32 dcdcell[6];
3589 if (DcdReadFrame(&dcd, n, xp, yp, zp, dcdcell)) {
3590 s_append_asprintf(errbuf, "Read error in dcd file");
3593 for (i = 0, vpp = &vp[n * mp->natoms]; i < dcd.natoms && i < mp->natoms; i++, vpp++) {
3601 /* dcdcell = {a, gamma, b, beta, alpha, c} */
3602 /* angles are described either in cosines (Charmm and NAMD > 2.5) or degrees (NAMD 2.5) */
3603 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) {
3604 dcdcell[4] = cos(dcdcell[4] * kDeg2Rad); /* cos(alpha) */
3605 dcdcell[3] = cos(dcdcell[3] * kDeg2Rad); /* cos(beta) */
3606 dcdcell[1] = cos(dcdcell[1] * kDeg2Rad); /* cos(gamma) */
3608 /* a axis lies along the cartesian x axis */
3609 sing = sqrt(1 - dcdcell[1] * dcdcell[1]);
3610 vpp[0].x = dcdcell[0];
3613 vpp[1].x = dcdcell[2] * dcdcell[1];
3614 vpp[1].y = dcdcell[2] * sing;
3616 vpp[2].x = dcdcell[5] * dcdcell[3];
3617 vpp[2].y = dcdcell[5] * (dcdcell[4] - dcdcell[3] * dcdcell[1]) / sing;
3618 vpp[2].z = sqrt(dcdcell[5] * dcdcell[5] - vpp[2].x * vpp[2].x - vpp[2].y * vpp[2].y);
3619 vpp[3].x = vpp[3].y = vpp[3].z = 0.0;
3620 if (mp->cell == NULL) {
3621 /* Create periodicity if not present */
3622 MolActionCreateAndPerform(mp, gMolActionSetBox, &vpp[0], &vpp[1], &vpp[2], &vpp[3], 7, 0);
3626 if (MolActionCreateAndPerform(mp, gMolActionInsertFrames, ig, mp->natoms * dcd.nframes, vp, (cp == NULL ? 0 : dcd.nframes * 4), cp) != 0)
3627 s_append_asprintf(errbuf, "Cannot insert frames");
3628 mp->startStep = dcd.nstart;
3629 mp->stepsPerFrame = dcd.ninterval;
3630 mp->psPerStep = dcd.delta;
3639 IntGroupRelease(ig);
3646 MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
3657 fp = fopen(fname, "rb");
3659 s_append_asprintf(errbuf, "Cannot open file");
3665 flags[0] = flags[1] = flags[2] = 0;
3666 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3667 if (strncmp(buf, "Bounding box:", 13) == 0) {
3668 for (i = 0; i < 3; i++) {
3669 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3670 s_append_asprintf(errbuf, "line %d: missing %d component of the bounding box", lineNumber, i + 1);
3674 n = sscanf(buf, "%lf %lf %lf %d", &d[0], &d[1], &d[2], &flag);
3676 vv.x = vv.y = vv.z = 0.0;
3678 case 0: vv.x = d[0]; break;
3679 case 1: vv.y = d[0]; break;
3680 case 2: vv.z = d[0]; break;
3682 if (n == 1 || (n == 2 && d[1] != 0.0))
3689 flags[i] = (flag != 0);
3691 flags[i] = (VecLength2(vv) != 0);
3695 if (mp->cell != NULL)
3696 vv = mp->cell->origin;
3698 vv.x = vv.y = vv.z = 0.0;
3699 MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
3700 } else if (strncmp(buf, "Bounding box origin:", 20) == 0) {
3701 if (mp->cell != NULL) {
3702 v[0] = mp->cell->axes[0];
3703 v[1] = mp->cell->axes[1];
3704 v[2] = mp->cell->axes[2];
3705 memmove(flags, mp->cell->flags, 3);
3707 v[0].x = 1.0; v[0].y = v[0].z = 0.0;
3708 v[1].y = 1.0; v[1].x = v[1].z = 0.0;
3709 v[2].z = 1.0; v[2].x = v[2].y = 0.0;
3710 flags[0] = flags[1] = flags[2] = 1.0;
3712 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0 || (n = sscanf(buf, "%lf %lf %lf", &d[0], &d[1], &d[2]) < 3)) {
3713 s_append_asprintf(errbuf, "line %d: wrong format for the bounding box origin", lineNumber);
3720 MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
3732 MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
3736 if (ftype == NULL || *ftype == 0) {
3738 cp = strrchr(fname, '.');
3742 cp = guessMoleculeType(fname);
3743 if (strcmp(cp, "???") != 0)
3747 if (strcasecmp(ftype, "psf") == 0) {
3748 retval = MoleculeWriteToPsfFile(mp, fname, errbuf);
3749 } else if (strcasecmp(ftype, "pdb") == 0) {
3750 retval = MoleculeWriteToPdbFile(mp, fname, errbuf);
3751 } else if (strcasecmp(ftype, "tep") == 0) {
3752 retval = MoleculeWriteToTepFile(mp, fname, errbuf);
3754 s_append_asprintf(errbuf, "The file format should be specified");
3758 MoleculeSetPath(mp, fname);
3763 MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char **errbuf)
3766 Int i, j, k, n1, n2, n3, n_aniso, nframes, nanchors;
3771 fp = fopen(fname, "wb");
3773 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
3778 nframes = MoleculeFlushFrames(mp);
3780 fprintf(fp, "!:atoms\n");
3781 fprintf(fp, "! idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge\n");
3782 n1 = n2 = n3 = n_aniso = nanchors = 0;
3783 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3784 strncpy(bufs[0], ap->segName, 4);
3786 strncpy(bufs[1], ap->resName, 4);
3788 strncpy(bufs[2], ap->aname, 4);
3790 AtomTypeDecodeToString(ap->type, bufs[3]);
3792 strncpy(bufs[4], ap->element, 4);
3794 for (j = 0; j < 5; j++) {
3795 if (bufs[j][0] == 0) {
3799 for (k = 0; k < 6; k++) {
3800 if (bufs[j][k] == 0)
3802 if (bufs[j][k] > 0 && bufs[j][k] < ' ')
3806 if (SYMOP_ALIVE(ap->symop))
3808 if (ap->fix_force != 0)
3810 if (ap->mm_exclude || ap->periodic_exclude)
3812 if (ap->aniso != NULL)
3814 if (ap->anchor != NULL)
3816 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);
3821 fprintf(fp, "!:atoms_symop\n");
3822 fprintf(fp, "! idx symop symbase\n");
3823 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3825 n = ap->symop.sym * 1000000 + ap->symop.dx * 10000 + ap->symop.dy * 100 + ap->symop.dz;
3826 fprintf(fp, "%d %d %d\n", i, n, ap->symbase);
3832 fprintf(fp, "!:atoms_fix\n");
3833 fprintf(fp, "! idx fix_force fix_pos\n");
3834 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3835 fprintf(fp, "%d %f %f %f %f\n", i, ap->fix_force, ap->fix_pos.x, ap->fix_pos.y, ap->fix_pos.z);
3841 fprintf(fp, "!:mm_exclude\n");
3842 fprintf(fp, "! idx mm_exclude periodic_exclude\n");
3843 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3844 fprintf(fp, "%d %d %d\n", i, ap->mm_exclude, ap->periodic_exclude);
3850 fprintf(fp, "!:pi_anchor\n");
3851 fprintf(fp, "! idx count; n1 weight1; n2 weight2; ...; nN weightN\n");
3852 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3854 if (ap->anchor == NULL)
3856 k = ap->anchor->connect.count;
3857 ip = AtomConnectData(&ap->anchor->connect);
3858 fprintf(fp, "%d %d\n", i, k);
3859 for (j = 0; j < k; j++) {
3860 fprintf(fp, "%d %f\n", ip[j], ap->anchor->coeffs[j]);
3871 for (i = 0; (i == n2 || i < n1); i++) {
3872 fprintf(fp, "!:positions ; frame %d\n", i);
3873 fprintf(fp, "! idx x y z [sx sy sz]\n");
3874 for (j = 0, ap = mp->atoms; j < mp->natoms; j++, ap = ATOM_NEXT(ap)) {
3877 if (i != n2 && i < ap->nframes)
3878 vp = ap->frames + i;
3881 if (ap->sigma.x != 0.0 || ap->sigma.y != 0.0 || ap->sigma.z != 0.0)
3884 fprintf(fp, "%d %.8f %.8f %.8f", j, vp->x, vp->y, vp->z);
3886 fprintf(fp, " %.8f %.8f %.8f", ap->sigma.x, ap->sigma.y, ap->sigma.z);
3893 if (mp->nbonds > 0) {
3894 fprintf(fp, "!:bonds\n");
3895 fprintf(fp, "! from1 to1 from2 to2 from3 to3 from4 to4\n");
3896 for (i = 0; i < mp->nbonds; i++) {
3897 fprintf(fp, "%d %d%c", mp->bonds[i * 2], mp->bonds[i * 2 + 1], (i % 4 == 3 || i == mp->nbonds - 1 ? '\n' : ' '));
3902 if (mp->nangles > 0) {
3903 fprintf(fp, "!:angles\n");
3904 fprintf(fp, "! a1 b1 c1 a2 b2 c2 a3 b3 c3\n");
3905 for (i = 0; i < mp->nangles; i++) {
3906 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' : ' '));
3911 if (mp->ndihedrals > 0) {
3912 fprintf(fp, "!:dihedrals\n");
3913 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3914 for (i = 0; i < mp->ndihedrals; i++) {
3915 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' : ' '));
3920 if (mp->nimpropers > 0) {
3921 fprintf(fp, "!:impropers\n");
3922 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
3923 for (i = 0; i < mp->nimpropers; i++) {
3924 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' : ' '));
3929 if (mp->cell != NULL) {
3930 fprintf(fp, "!:xtalcell\n");
3931 fprintf(fp, "! a b c alpha beta gamma\n");
3932 fprintf(fp, "! This information is redundant and overridden by the following periodic_box info\n");
3933 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]);
3936 fprintf(fp, "!:periodic_box\n");
3937 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");
3938 for (i = 0; i < 3; i++)
3939 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->axes[i].x, mp->cell->axes[i].y, mp->cell->axes[i].z);
3940 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->origin.x, mp->cell->origin.y, mp->cell->origin.z);
3941 fprintf(fp, "%d %d %d%s\n", mp->cell->flags[0], mp->cell->flags[1], mp->cell->flags[2], (mp->cell->has_sigma ? " 1" : ""));
3942 if (mp->cell->has_sigma) {
3943 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]);
3948 if (mp->nframe_cells > 0) {
3949 fprintf(fp, "!:frame_periodic_boxes\n");
3950 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz\n");
3951 for (i = 0; i < mp->nframe_cells * 4; i++) {
3952 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->frame_cells[i].x, mp->frame_cells[i].y, mp->frame_cells[i].z);
3957 if (mp->nsyms > 0) {
3958 fprintf(fp, "!:symmetry_operations\n");
3959 fprintf(fp, "! a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3\n");
3960 for (i = 0; i < mp->nsyms; i++) {
3961 Transform *tp = mp->syms + i;
3962 const unsigned char s_index_order[12] = {0, 3, 6, 1, 4, 7, 2, 5, 8, 9, 10, 11};
3963 for (j = 0; j < 12; j++)
3964 fprintf(fp, "%11.6f%c", (*tp)[s_index_order[j]], (j % 3 == 2 ? '\n' : ' '));
3970 fprintf(fp, "!:anisotropic_thermal_parameters\n");
3971 fprintf(fp, "! b11 b22 b33 b12 b13 b23 [sigma; sb11 sb22 sb33 sb12 sb13 sb23]\n");
3972 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3973 if (ap->aniso != NULL) {
3974 Double *bp = ap->aniso->bij;
3975 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" : ""));
3976 if (ap->aniso->has_bsig) {
3977 bp = ap->aniso->bsig;
3978 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]);
3981 fprintf(fp, "0 0 0 0 0 0\n");
3987 if (mp->arena != NULL) {
3988 MDArena *arena = mp->arena;
3989 fprintf(fp, "!:md_parameters\n");
3990 fprintf(fp, "log_file %s\n", arena->log_result_name);
3991 fprintf(fp, "coord_file %s\n", arena->coord_result_name);
3992 fprintf(fp, "vel_file %s\n", arena->vel_result_name);
3993 fprintf(fp, "force_file %s\n", arena->force_result_name);
3994 fprintf(fp, "debug_file %s\n", arena->debug_result_name);
3995 fprintf(fp, "debug_output_level %d\n", arena->debug_output_level);
3996 fprintf(fp, "step %d\n", arena->step);
3997 fprintf(fp, "coord_output_freq %d\n", arena->coord_output_freq);
3998 fprintf(fp, "energy_output_freq %d\n", arena->energy_output_freq);
3999 fprintf(fp, "coord_frame %d\n", arena->coord_result_frame);
4000 fprintf(fp, "timestep %g\n", arena->timestep);
4001 fprintf(fp, "cutoff %g\n", arena->cutoff);
4002 fprintf(fp, "electro_cutoff %g\n", arena->electro_cutoff);
4003 fprintf(fp, "pairlist_distance %g\n", arena->pairlist_distance);
4004 fprintf(fp, "switch_distance %g\n", arena->switch_distance);
4005 fprintf(fp, "temperature %g\n", arena->temperature);
4006 fprintf(fp, "andersen_freq %d\n", arena->andersen_thermo_freq);
4007 fprintf(fp, "andersen_coupling %g\n", arena->andersen_thermo_coupling);
4008 fprintf(fp, "random_seed %d\n", arena->random_seed);
4009 fprintf(fp, "dielectric %g\n", arena->dielectric);
4010 fprintf(fp, "gradient_convergence %g\n", arena->gradient_convergence);
4011 fprintf(fp, "coordinate_convergence %g\n", arena->coordinate_convergence);
4012 fprintf(fp, "use_xplor_shift %d\n", arena->use_xplor_shift);
4013 fprintf(fp, "scale14_vdw %g\n", arena->scale14_vdw);
4014 fprintf(fp, "scale14_elect %g\n", arena->scale14_elect);
4015 fprintf(fp, "relocate_center %d\n", arena->relocate_center);
4016 fprintf(fp, "surface_probe_radius %g\n", arena->probe_radius);
4017 fprintf(fp, "surface_tension %g\n", arena->surface_tension);
4018 fprintf(fp, "surface_potential_freq %d\n", arena->surface_potential_freq);
4019 fprintf(fp, "use_graphite %d\n", arena->use_graphite);
4020 fprintf(fp, "alchemical_lambda %g\n", arena->alchem_lambda);
4021 fprintf(fp, "alchemical_delta_lambda %g\n", arena->alchem_dlambda);
4022 if (arena->nalchem_flags > 0) {
4023 fprintf(fp, "alchem_flags %d", arena->nalchem_flags);
4024 for (i = 0; i < arena->nalchem_flags; i++) {
4027 else if (i % 10 == 0)
4029 fputc('0' + arena->alchem_flags[i], fp);
4033 if (arena->pressure != NULL) {
4035 fprintf(fp, "pressure_freq %d\n", arena->pressure->freq);
4036 fprintf(fp, "pressure_coupling %g\n", arena->pressure->coupling);
4037 dp = arena->pressure->apply;
4038 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]);
4039 dp = arena->pressure->cell_flexibility;
4040 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]);
4041 fprintf(fp, "pressure_fluctuate_cell_origin %g\n", arena->pressure->fluctuate_cell_origin);
4042 fprintf(fp, "pressure_fluctuate_cell_orientation %g\n", arena->pressure->fluctuate_cell_orientation);
4046 if (mp->par != NULL) {
4047 Parameter *par = mp->par;
4048 fprintf(fp, "!:parameters\n");
4049 ParameterAppendToFile(par, fp);
4053 fprintf(fp, "!:velocity\n");
4054 fprintf(fp, "! idx vx vy vz\n");
4055 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4056 fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->v.x, ap->v.y, ap->v.z);
4060 fprintf(fp, "!:force\n");
4061 fprintf(fp, "! idx fx fy fz\n");
4062 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4063 fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->f.x, ap->f.y, ap->f.z);
4068 if (mp->mview != NULL) {
4070 if (mp->mview->track != NULL) {
4071 fprintf(fp, "!:trackball\n");
4072 fprintf(fp, "! scale; trx try trz; theta_deg x y z\n");
4073 f[0] = TrackballGetScale(mp->mview->track);
4074 fprintf(fp, "%f\n", f[0]);
4075 TrackballGetTranslate(mp->mview->track, f);
4076 fprintf(fp, "%f %f %f\n", f[0], f[1], f[2]);
4077 TrackballGetRotate(mp->mview->track, f);
4078 fprintf(fp, "%f %f %f %f\n", f[0], f[1], f[2], f[3]);
4081 fprintf(fp, "!:view\n");
4082 fprintf(fp, "show_unit_cell %d\n", mp->mview->showUnitCell);
4083 fprintf(fp, "show_periodic_box %d\n", mp->mview->showPeriodicBox);
4084 fprintf(fp, "show_expanded_atoms %d\n", mp->mview->showExpandedAtoms);
4085 fprintf(fp, "show_ellipsoids %d\n", mp->mview->showEllipsoids);
4086 fprintf(fp, "show_hydrogens %d\n", mp->mview->showHydrogens);
4087 fprintf(fp, "show_dummy_atoms %d\n", mp->mview->showDummyAtoms);
4088 fprintf(fp, "show_rotation_center %d\n", mp->mview->showRotationCenter);
4089 fprintf(fp, "show_graphite_flag %d\n", mp->mview->showGraphiteFlag);
4090 fprintf(fp, "show_graphite %d\n", mp->mview->showGraphite);
4091 fprintf(fp, "show_periodic_image_flag %d\n", mp->mview->showPeriodicImageFlag);
4092 fprintf(fp, "show_periodic_image %d %d %d %d %d %d\n",
4093 mp->mview->showPeriodicImage[0], mp->mview->showPeriodicImage[1],
4094 mp->mview->showPeriodicImage[2], mp->mview->showPeriodicImage[3],
4095 mp->mview->showPeriodicImage[4], mp->mview->showPeriodicImage[5]);
4104 MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char **errbuf)
4110 fp = fopen(fname, "wb");
4112 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4115 fprintf(fp, "PSF\n\n");
4116 fprintf(fp, " 1 !NTITLE\n");
4117 fprintf(fp, " REMARKS FILENAME=\n");
4121 fprintf(fp, "%8d !NATOM\n", mp->natoms);
4122 for (i = 0; i < mp->natoms; i++) {
4124 ap = ATOM_AT_INDEX(mp->atoms, i);
4125 fprintf(fp, "%8d ", i + 1);
4126 if (ap->resSeq >= 10000) {
4127 fmt = "%-3.3s %-5d ";
4129 fmt = "%-4.4s %-4d ";
4131 fprintf(fp, fmt, ap->segName, ap->resSeq);
4132 fprintf(fp, "%-3.3s %-4.4s %-4.4s %12.6f %8.4f 0\n",
4133 ap->resName, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->charge, ap->weight);
4138 fprintf(fp, "%8d !NBOND: bonds\n", mp->nbonds);
4139 for (i = 0; i < mp->nbonds * 2; i++) {
4140 fprintf(fp, "%8d", mp->bonds[i] + 1);
4149 fprintf(fp, "%8d !NTHETA: angles\n", mp->nangles);
4150 for (i = 0; i < mp->nangles * 3; i++) {
4151 fprintf(fp, "%8d", mp->angles[i] + 1);
4160 fprintf(fp, "%8d !NPHI: dihedrals\n", mp->ndihedrals);
4161 for (i = 0; i < mp->ndihedrals * 4; i++) {
4162 fprintf(fp, "%8d", mp->dihedrals[i] + 1);
4171 fprintf(fp, "%8d !NIMPHI: impropers\n", mp->nimpropers);
4172 for (i = 0; i < mp->nimpropers * 4; i++) {
4173 fprintf(fp, "%8d", mp->impropers[i] + 1);
4181 fprintf(fp, "%8d !NDON: donors\n\n", 0);
4182 fprintf(fp, "%8d !NACC: acceptors\n\n", 0);
4183 fprintf(fp, "%8d !NNB: non-bonding exclusions\n\n", 0);
4184 for (i = 0; i < mp->natoms; i++) {
4185 fprintf(fp, "%8d", 0);
4192 fprintf(fp, "%8d !NGRP: groups\n", 1);
4193 fprintf(fp, " 0 0 0\n");
4197 if (i > 5 && strcmp(fname + i - 5, ".psfx") == 0) {
4198 /* Extended psf (with coordinates and other info) */
4199 fprintf(fp, "%8d !COORD: coordinates\n", mp->natoms);
4200 for (i = 0; i < mp->natoms; i++) {
4202 ap = ATOM_AT_INDEX(mp->atoms, i);
4204 fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->aname);
4208 if (mp->nframes > 0) {
4209 int fn; /* Frame number */
4210 for (fn = 0; fn < ap->nframes; fn++) {
4211 fprintf(fp, "%8d !COORD: coordinates for frame %d\n", mp->natoms, fn);
4212 for (i = 0; i < mp->natoms; i++) {
4214 ap = ATOM_AT_INDEX(mp->atoms, i);
4215 if (ap->frames == NULL || fn >= ap->nframes)
4219 fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->name);
4232 MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char **errbuf)
4238 fp = fopen(fname, "wb");
4240 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4243 for (i = 0; i < mp->natoms; i++) {
4245 ap = ATOM_AT_INDEX(mp->atoms, i);
4246 if (ap->resSeq >= 10000) {
4247 snprintf(buf, sizeof buf, "%c%03d", 'A' + (ap->resSeq - 10000) / 1000, ap->resSeq % 1000);
4249 snprintf(buf, sizeof buf, "%4d", ap->resSeq);
4251 fprintf(fp, "ATOM %5d %-4.4s%1.1s%-3.3s %1.1s%4.4s%1.1s "
4252 "%8.3f%8.3f%8.3f %5.2f %5.2f "
4253 "%-4.4s%-2.2s%-2d\n",
4254 i + 1, ap->aname, " ", ap->resName, " ", buf, " ",
4255 ap->r.x, ap->r.y, ap->r.z, ap->occupancy, ap->tempFactor,
4256 ap->segName, ap->element, ap->intCharge);
4258 for (i = 0; i < mp->natoms; i++) {
4260 ap = ATOM_AT_INDEX(mp->atoms, i);
4261 cp = AtomConnectData(&ap->connect);
4262 for (j = 0; j < ap->connect.count; j++) {
4266 fprintf(fp, "CONECT%5d", i + 1);
4268 fprintf(fp, "%5d", cp[j] + 1);
4273 fprintf(fp, "END\n");
4279 MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char **errbuf)
4282 SFloat32 *xp, *yp, *zp;
4285 if (mp == NULL || mp->natoms == 0) {
4286 s_append_asprintf(errbuf, "Molecule is empty");
4289 memset(&dcd, 0, sizeof(dcd));
4290 dcd.natoms = mp->natoms;
4291 dcd.nframes = MoleculeGetNumberOfFrames(mp);
4292 if (dcd.nframes == 0) {
4293 s_append_asprintf(errbuf, "no frame is present");
4296 dcd.nstart = mp->startStep;
4297 dcd.ninterval = mp->stepsPerFrame;
4298 if (dcd.ninterval == 0)
4300 dcd.nend = dcd.nstart + (dcd.nframes - 1) * dcd.ninterval;
4301 if (mp->cell != NULL)
4303 dcd.delta = mp->psPerStep;
4304 if (dcd.delta == 0.0)
4307 n = DcdCreate(fname, &dcd);
4310 s_append_asprintf(errbuf, "Cannot create dcd file");
4312 s_append_asprintf(errbuf, "Cannot write dcd header");
4317 xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4318 yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4319 zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4320 if (xp == NULL || yp == NULL || zp == NULL) {
4321 s_append_asprintf(errbuf, "Cannot allocate memory");
4328 for (n = 0; n < dcd.nframes; n++) {
4331 for (i = 0, ap = mp->atoms; i < dcd.natoms && mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4333 if (ap->frames == NULL || n >= ap->nframes)
4341 if (i < dcd.natoms) {
4342 size_t sz = (dcd.natoms - i) * sizeof(SFloat32);
4343 memset(xp + i, 0, sz);
4344 memset(yp + i, 0, sz);
4345 memset(zp + i, 0, sz);
4347 if (n < mp->nframe_cells && mp->frame_cells != NULL) {
4348 Vector *cp = &(mp->frame_cells[n * 4]);
4349 dcd.globalcell[0] = VecLength(cp[0]);
4350 dcd.globalcell[2] = VecLength(cp[1]);
4351 dcd.globalcell[5] = VecLength(cp[2]);
4352 dcd.globalcell[1] = VecDot(cp[0], cp[1]) / (dcd.globalcell[0] * dcd.globalcell[2]);
4353 dcd.globalcell[3] = VecDot(cp[0], cp[2]) / (dcd.globalcell[0] * dcd.globalcell[5]);
4354 dcd.globalcell[4] = VecDot(cp[1], cp[2]) / (dcd.globalcell[2] * dcd.globalcell[5]);
4356 if (DcdWriteFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
4357 s_append_asprintf(errbuf, "Write error in dcd file");
4373 MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
4379 fp = fopen(fname, "wb");
4381 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4384 if (mp->cell != NULL) {
4385 fprintf(fp, "Bounding box:\n");
4386 for (i = 0; i < 3; i++) {
4387 v = mp->cell->axes[i];
4388 fprintf(fp, "%.3f %.3f %.3f %d\n", v.x, v.y, v.z, mp->cell->flags[i]);
4390 fprintf(fp, "Bounding box origin:\n");
4391 v = mp->cell->origin;
4392 fprintf(fp, "%.3f %.3f %.3f\n", v.x, v.y, v.z);
4399 sCompareByElement(const void *ap, const void *bp)
4401 return ((*(Atom **)bp)->atomicNumber - (*(Atom **)ap)->atomicNumber);
4405 sMakeAdc(int n, int base, Symop symop)
4408 if (SYMOP_ALIVE(symop)) {
4410 sym = (symop.dx + 5) * 10000 + (symop.dy + 5) * 1000 + (symop.dz + 5) * 100 + symop.sym + 1;
4415 return (an + 1) * 100000 + sym;
4419 sCompareAdc(const void *ap, const void *bp)
4421 int n = *((Int *)ap) % 100000 - *((Int *)bp) % 100000;
4423 n = *((Int *)ap) / 100000 - *((Int *)bp) / 100000;
4428 sOutputAtomListInstructions(FILE *fp, int natoms, Atom *atoms)
4430 int i, j, k, an, sym;
4433 adc = (Int *)malloc(sizeof(Int) * natoms);
4436 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4437 if (ap->exflags & kAtomHiddenFlag)
4439 adc[i] = sMakeAdc(i, ap->symbase, ap->symop);
4441 mergesort(adc, natoms, sizeof(Int), sCompareAdc);
4443 /* Create the atom list */
4445 for (i = j = k = 0; i < natoms; i++) {
4446 int an1 = adc[i] / 100000;
4447 int sym1 = adc[i] % 100000;
4448 if (sym == sym1 && an1 == an + 1) {
4455 /* Output the last atom with a minus sign */
4456 adc[j++] = -(an * 100000 + sym);
4457 /* Output this atom */
4464 adc[j++] = -(an * 100000 + sym);
4466 /* Create the instruction cards */
4467 for (i = k = 0; i < j; i++) {
4469 fprintf(fp, " 401");
4470 fprintf(fp, "%9d", adc[i]);
4472 if (i == j - 1 || k == 6 || (k == 5 && i < j - 2 && adc[i + 2] < 0)) {
4481 sEllipsoidType(int an)
4483 return (an >= 18 ? 3 : (an >= 2 && an != 6 ? 2 : (an > 0 ? 1 : 0)));
4487 sOutputAtomTypeInstructions(FILE *fp, int natoms, Atom *atoms)
4491 int etype, elast, istart, ilast, n1, n2;
4492 elast = istart = ilast = -1;
4493 for (i = 0, ap = atoms; i <= natoms; i++, ap++) {
4495 if (SYMOP_ALIVE(ap->symop))
4497 if (ap->exflags & kAtomHiddenFlag)
4499 etype = sEllipsoidType(ap->atomicNumber);
4504 } else if (elast == etype && ilast == i - 1) {
4509 /* Output the instruction card for the 'last' block of atoms */
4512 n1 = 4; n2 = 0; break;
4514 n1 = 4; n2 = 5; break;
4516 n1 = 1; n2 = 0; break;
4518 fprintf(fp, " 1 715 %8d 0 %8d 0 0.100 0.000 0.000\n", n1, n2);
4519 fprintf(fp, " %9d%9d\n", istart + 1, ilast + 1);
4526 sCompareBondType(const void *ap, const void *bp)
4528 /* Descending order */
4529 return *((int *)bp) - *((int *)ap);
4533 sOutputBondInstructions(FILE *fp, int natoms, Atom *atoms, int overlap_correction)
4537 int i, j, n[5], an, count, n1, n2, k;
4541 static const float sBondRad[4] = {0.060, 0.060, 0.060, 0.040};
4542 static const int sBondShade[4] = {5, 3, 1, 1};
4544 n[0] = n[1] = n[2] = n[3] = 0; /* Start index of 3rd row atoms (and higher), 2nd row, 1st row, and H */
4546 for (i = natoms - 1, ap = atoms + i; i >= 0; i--, ap--) {
4547 an = ap->atomicNumber;
4559 if (overlap_correction)
4560 strcpy(buf, " 2 1001 0.000\n");
4562 strcpy(buf, " 2 812\n");
4564 for (i = 0; i < 4; i++) {
4565 for (j = i; j < 4; j++) {
4566 /* Examine bonds between "group i" and "group j" */
4569 double min_bond = 10000.0; /* Minimum distance between bound atoms */
4570 double min_nonbond = 10000.0; /* Minimum distance between non-bound atoms */
4571 double max_bond = -10000.0; /* Maximum distance between bound atoms */
4572 int count_exbond = 0; /* Number of explicit bonds in this group */
4573 for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
4574 for (n2 = n[j], ap2 = atoms + n2; n2 < n[j + 1]; n2++, ap2++) {
4577 VecSub(dr, ap->r, ap2->r);
4579 cp = AtomConnectData(&ap->connect);
4580 for (k = ap->connect.count - 1; k >= 0; k--) {
4585 /* n1 and n2 are bound */
4591 /* n1 and n2 are not bound */
4592 if (d < min_nonbond)
4597 if (min_bond == 10000.0)
4598 continue; /* No bonds between these groups */
4600 if (max_bond + 0.002 < min_nonbond)
4603 max_bond = min_nonbond - 0.002;
4604 /* Some bonds may be omitted, so scan all bonds again */
4605 for (n1 = n[i], ap = ATOM_AT_INDEX(atoms, n1); n1 < n[i + 1]; n1++, ap = ATOM_NEXT(ap)) {
4606 cp = AtomConnectData(&ap->connect);
4607 for (k = ap->connect.count - 1; k >= 0; k--) {
4609 if (n2 < n[j] || n2 >= n[j + 1])
4612 VecSub(dr, ap->r, ap2->r);
4615 /* This bond should be explicitly defined */
4617 if (count_exbond == 0) {
4618 adc1 = -(i + 1); /* Bond type */
4619 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4621 adc1 = sMakeAdc(n1, ap->symbase, ap->symop);
4622 adc2 = sMakeAdc(n2, ap2->symbase, ap2->symop);
4623 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
4624 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc2);
4630 /* Output the last instruction card */
4632 /* Make a new trailer card */
4633 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]);
4638 /* Output the last trailer card */
4643 if (count == 0 && overlap_correction) {
4644 /* 1001 card is not yet written, so write it */
4648 snprintf(buf, sizeof(buf), " 1 %3d", (overlap_correction ? 821 : 811));
4649 k = -exbonds[0] - 1; /* Bond type for the first block */
4650 i = 1; /* Index for exbonds[] */
4651 j = 0; /* Count in this block */
4652 while (i <= nexbonds) {
4653 if (j >= 29 || i == nexbonds || exbonds[i] < 0) {
4657 /* The trailer card */
4658 fprintf(fp, " %3d %6.3f\n", sBondShade[k], sBondRad[k]);
4662 k = -exbonds[i++] - 1; /* The new bond type */
4664 } else if (j > 0 && j % 3 == 0) {
4670 snprintf(buf + 9 + (j % 3) * 18, sizeof(buf) - 9 - (j % 3) * 18, "%9d%9d\n", n1, n2);
4679 /* Explicit bond table, sorted by bond type */
4680 for (i = j = 0; i < mp->nbonds; i++) {
4681 n1 = mp->bonds[i * 2];
4682 n2 = mp->bonds[i * 2 + 1];
4683 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
4684 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
4685 if ((ap1->exflags & kAtomHiddenFlag) || (ap2->exflags & kAtomHiddenFlag))
4687 if (ap1->atomicNumber > 18 || ap2->atomicNumber > 18) {
4689 } else if (ap1->atomicNumber > 1 && ap1->atomicNumber > 1) {
4695 ip[j * 3 + 1] = sMakeAdc(n1, ap1->symbase, ap1->symop);
4696 ip[j * 3 + 2] = sMakeAdc(n2, ap2->symbase, ap2->symop);
4699 mergesort(ip, j, sizeof(int) * 3, sCompareBondType);
4701 /* Output instruction cards */
4702 strcpy(buf, " 1 811");
4703 for (i = n1 = 0; i < j; i++) {
4704 n2 = (n1 % 3) * 18 + 9;
4705 snprintf(buf + n2, 80 - n2, "%9d%9d\n", ip[i * 3 + 1], ip[i * 3 + 2]);
4706 if (i == j - 1 || n1 >= 29 || ip[i * 3] != ip[i * 3 + 3]) {
4707 /* End of this instruction */
4710 switch (ip[i * 3]) {
4711 case 3: rad = 0.06; nshades = 5; break;
4712 case 2: rad = 0.06; nshades = 1; break;
4713 default: rad = 0.04; nshades = 1; break;
4715 fprintf(fp, " %3d %6.3f\n", nshades, rad);
4716 strcpy(buf, " 1 811");
4719 } else if (n1 % 3 == 2) {
4730 MoleculeWriteToTepFile(Molecule *mp, const char *fname, char **errbuf)
4733 int i, j, natoms, *ip;
4735 Atom *ap, *atoms, **app;
4737 static Double sUnit[] = {1, 1, 1, 90, 90, 90};
4741 /* Create sorted array of atoms */
4742 natoms = mp->natoms;
4743 atoms = (Atom *)calloc(sizeof(Atom), natoms);
4744 app = (Atom **)calloc(sizeof(Atom *), natoms);
4745 ip = (int *)calloc(sizeof(int), natoms);
4746 if (atoms == NULL || app == NULL || ip == NULL) {
4747 s_append_asprintf(errbuf, "Cannot allocate memory");
4750 /* Sort the atom pointer by atomic number */
4751 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
4753 mergesort(app, natoms, sizeof(Atom *), sCompareByElement);
4754 for (i = 0; i < natoms; i++) {
4755 /* ip[old_index] is new_index */
4756 ip[app[i] - mp->atoms] = i;
4758 /* Copy the atom record to atoms[] */
4759 /* The 'v' member contains crystallographic coordinates */
4760 /* The connection table and symbase are renumbered */
4761 /* Hidden flags are modified to reflect the visibility in the MainView */
4762 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4763 AtomDuplicateNoFrame(ap, app[i]);
4764 /* memmove(ap, app[i], gSizeOfAtomRecord); */
4765 MoleculeCartesianToXtal(mp, &(ap->v), &(ap->r));
4766 cp = AtomConnectData(&ap->connect);
4767 for (j = ap->connect.count - 1; j >= 0; j--) {
4770 if (SYMOP_ALIVE(ap->symop))
4771 ap->symbase = ip[ap->symbase];
4772 if (MainView_isAtomHidden(mp->mview, i)) {
4773 ap->exflags |= kAtomHiddenFlag;
4775 ap->exflags &= ~kAtomHiddenFlag;
4781 fp = fopen(fname, "wb");
4783 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4788 fprintf(fp, "Generated by Molby\n");
4791 if (mp->cell != NULL) {
4792 dp = mp->cell->cell;
4796 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]);
4798 /* Symmetry operations */
4799 if (mp->nsyms > 0) {
4800 for (i = 0; i < mp->nsyms; i++) {
4802 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]);
4805 fprintf(fp, "1 0 1 0 0 0 0 1 0 0 0 0 1\n");
4809 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
4810 /* The 'v' field contains crystallographic coordinates */
4811 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);
4812 if (ap->aniso != NULL) {
4813 dp = ap->aniso->bij;
4814 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);
4816 Double temp = ap->tempFactor;
4819 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", temp, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4822 /* Special points */
4824 Vector camera, lookat, up, xvec, yvec, zvec;
4825 MainView_getCamera(mp->mview, &camera, &lookat, &up);
4826 VecSub(zvec, lookat, camera);
4827 VecCross(xvec, zvec, up);
4828 NormalizeVec(&xvec, &xvec);
4829 NormalizeVec(&yvec, &up);
4830 VecInc(xvec, lookat);
4831 VecInc(yvec, lookat);
4832 MoleculeCartesianToXtal(mp, &lookat, &lookat);
4833 MoleculeCartesianToXtal(mp, &xvec, &xvec);
4834 MoleculeCartesianToXtal(mp, &yvec, &yvec);
4835 fprintf(fp, " ORGN %9g%9g%9g 0\n", 0.0, 0.0, 0.0);
4836 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4837 fprintf(fp, " CNTR %9g%9g%9g 0\n", lookat.x, lookat.y, lookat.z);
4838 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4839 fprintf(fp, " X %9g%9g%9g 0\n", xvec.x, xvec.y, xvec.z);
4840 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
4841 fprintf(fp, " Y %9g%9g%9g 0\n", yvec.x, yvec.y, yvec.z);
4842 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);
4846 fprintf(fp, " 201\n");
4847 fprintf(fp, " 205 12\n");
4848 fprintf(fp, " 301 6.6 6.6 0 0.8\n");
4849 sOutputAtomListInstructions(fp, natoms, atoms);
4850 fprintf(fp, " 501%4d55501%4d55501%4d55501%4d55501%4d55501 1\n", natoms + 2, natoms + 2, natoms + 3, natoms + 2, natoms + 4);
4851 fprintf(fp, " 502 1 0.0 2 0.0 3 0.0\n");
4852 fprintf(fp, " 604 1.538\n");
4854 sOutputBondInstructions(fp, natoms, atoms, 1);
4855 sOutputAtomTypeInstructions(fp, natoms, atoms);
4856 sOutputBondInstructions(fp, natoms, atoms, 0);
4858 for (i = 0; i < natoms; i++) {
4859 AtomClean(atoms + i);
4863 fprintf(fp, " 202\n");
4864 fprintf(fp, " 0 -1\n");
4870 MoleculeDump(Molecule *mol)
4875 for (i = 0; i < mol->natoms; i++) {
4877 ap = ATOM_AT_INDEX(mol->atoms, i);
4878 snprintf(buf1, sizeof buf1, "%3.4s.%d", ap->resName, ap->resSeq);
4879 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);
4880 cp = AtomConnectData(&ap->connect);
4881 for (j = 0; j < ap->connect.count; j++) {
4882 fprintf(stderr, "%s%d", (j > 0 ? "," : ""), cp[j]);
4884 fprintf(stderr, "]\n");
4888 #pragma mark ====== MD support (including modification of Molecule) ======
4890 /* Call md_prepare for the MDArena. If MDArena has not been created, a new arena is created.
4891 If something goes wrong, returns 1 (for missing parameters) or -1 (more serious error).
4892 If retmsg is not NULL, a message describing the problem is returned there. This message
4893 must be free'd by the caller. */
4895 MoleculePrepareMDArena(Molecule *mol, int check_only, char **retmsg)
4898 Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
4900 IntGroup *ig1, *ig2, *ig3;
4901 MDArena *arena = mol->arena;
4903 if (arena == NULL) {
4906 } else if (arena->xmol != mol)
4907 md_arena_set_molecule(arena, mol);
4909 arena->is_initialized = 0;
4911 /* Rebuild the tables */
4912 ig1 = ig2 = ig3 = NULL;
4913 nangles = MoleculeFindMissingAngles(mol, &angles);
4914 ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
4915 nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
4917 ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
4918 MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
4920 IntGroupRelease(ig1);
4922 if (ndihedrals > 0) {
4923 ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
4924 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
4926 IntGroupRelease(ig2);
4928 if (nimpropers > 0) {
4929 ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
4930 MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
4932 IntGroupRelease(ig3);
4936 /* Update the path information of the molecule before MD setup */
4937 char *buf = (char *)malloc(4096);
4938 MoleculeCallback_pathName(mol, buf, sizeof buf);
4939 MoleculeSetPath(mol, buf);
4943 /* Prepare parameters and internal information */
4944 msg = md_prepare(arena, check_only);
4946 /* Some parameters are missing? */
4948 if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
4952 asprintf(retmsg, "cannot initialize for MD: %s", msg);
4957 /* The local parameter list is updated */
4960 if (mol->par == NULL)
4961 mol->par = ParameterNew();
4962 for (parType = kFirstParType; parType <= kLastParType; parType++) {
4963 /* Delete global and undefined parameters */
4964 UnionPar *up, *upbuf;
4966 ig1 = IntGroupNew();
4967 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
4968 if (up->bond.src != 0)
4969 IntGroupAdd(ig1, idx, 1);
4971 if (IntGroupGetCount(ig1) > 0)
4972 MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
4973 IntGroupRelease(ig1);
4974 /* Copy global and undefined parameters from arena and insert to mol->par */
4975 nparams = ParameterGetCountForType(arena->par, parType);
4978 upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
4979 ig1 = IntGroupNew();
4980 ig2 = IntGroupNew();
4981 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
4982 if (up->bond.src > 0)
4983 IntGroupAdd(ig1, idx, 1); /* Global parameter */
4984 else if (up->bond.src < 0)
4985 IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
4987 if ((count = IntGroupGetCount(ig1)) > 0) {
4988 /* Insert global parameters (at the top) */
4989 ParameterCopy(arena->par, parType, upbuf, ig1);
4990 ig3 = IntGroupNewWithPoints(0, count, -1);
4991 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
4992 IntGroupRelease(ig3);
4994 if ((count = IntGroupGetCount(ig2)) > 0) {
4995 /* Insert undefined parameters (at the bottom) */
4996 ParameterCopy(arena->par, parType, upbuf, ig2);
4997 idx = ParameterGetCountForType(mol->par, parType);
4998 ig3 = IntGroupNewWithPoints(idx, count, -1);
4999 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5000 IntGroupRelease(ig3);
5002 IntGroupRelease(ig2);
5003 IntGroupRelease(ig1);
5006 mol->needsMDRebuild = 0; /* We know the "modified" parameters are consistent with the MDArena */
5011 *retmsg = strdup(msg);
5016 #pragma mark ====== Serialize ======
5019 MoleculeDeserialize(const char *data, Int length, Int *timep)
5029 par = ParameterNew();
5033 while (length >= 12) {
5034 const char *ptr = data + 8 + sizeof(Int);
5035 int len = *((const Int *)(data + 8));
5037 if (strcmp(data, "ATOM") == 0) {
5038 n = len / gSizeOfAtomRecord;
5039 NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, n);
5040 memmove(mp->atoms, ptr, len);
5041 } else if (strcmp(data, "ANISO") == 0) {
5042 n = len / (sizeof(Int) + sizeof(Aniso));
5043 for (i = 0; i < n; i++) {
5044 j = *((const Int *)ptr);
5045 if (j < 0 || j >= mp->natoms)
5047 ap = ATOM_AT_INDEX(mp->atoms, j);
5048 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
5049 if (ap->aniso == NULL)
5051 *(ap->aniso) = *((Aniso *)(ptr + sizeof(Int)));
5052 ptr += sizeof(Int) + sizeof(Aniso);
5054 } else if (strcmp(data, "FRAME") == 0) {
5055 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5056 if (ap->nframes == 0)
5058 ap->frames = (Vector *)malloc(sizeof(Vector) * ap->nframes);
5059 if (ap->frames == NULL)
5061 memmove(ap->frames, ptr, sizeof(Vector) * ap->nframes);
5062 ptr += sizeof(Vector) * ap->nframes;
5064 } else if (strcmp(data, "EXTCON") == 0) {
5065 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5066 if (ap->connect.count <= ATOM_CONNECT_LIMIT)
5068 n = ap->connect.count;
5069 ap->connect.count = 0;
5070 ap->connect.u.ptr = NULL;
5071 NewArray(&(ap->connect.u.ptr), &(ap->connect.count), sizeof(Int), n);
5072 memmove(ap->connect.u.ptr, ptr, sizeof(Int) * n);
5073 ptr += sizeof(Int) * n;
5075 } else if (strcmp(data, "BOND") == 0) {
5076 n = len / (sizeof(Int) * 2);
5077 NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, n);
5078 memmove(mp->bonds, ptr, len);
5079 } else if (strcmp(data, "ANGLE") == 0) {
5080 n = len / (sizeof(Int) * 3);
5081 NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, n);
5082 memmove(mp->angles, ptr, len);
5083 } else if (strcmp(data, "DIHED") == 0) {
5084 n = len / (sizeof(Int) * 4);
5085 NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, n);
5086 memmove(mp->dihedrals, ptr, len);
5087 } else if (strcmp(data, "IMPROP") == 0) {
5088 n = len / (sizeof(Int) * 4);
5089 NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, n);
5090 memmove(mp->impropers, ptr, len);
5091 } else if (strcmp(data, "RESIDUE") == 0) {
5093 NewArray(&mp->residues, &mp->nresidues, 4, n);
5094 memmove(mp->residues, ptr, len);
5095 } else if (strcmp(data, "CELL") == 0) {
5096 mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
5097 if (mp->cell == NULL)
5099 memmove(mp->cell, ptr, sizeof(XtalCell));
5100 } else if (strcmp(data, "SYMOP") == 0) {
5101 n = len / sizeof(Transform);
5102 NewArray(&mp->syms, &mp->nsyms, sizeof(Transform), n);
5103 memmove(mp->syms, ptr, len);
5104 } else if (strcmp(data, "ANCHOR") == 0) {
5105 const char *ptr2 = ptr + len;
5106 while (ptr < ptr2) {
5108 memset(&an, 0, sizeof(an));
5110 if (i >= 0 && i < mp->natoms) {
5111 n = *((Int *)(ptr + sizeof(Int)));
5112 AtomConnectResize(&(an.connect), n);
5113 memmove(AtomConnectData(&(an.connect)), ptr + sizeof(Int) * 2, sizeof(Int) * n);
5114 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), n);
5115 memmove(an.coeffs, ptr + sizeof(Int) * (2 + n), sizeof(Double) * n);
5116 ap = ATOM_AT_INDEX(mp->atoms, i);
5117 ap->anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
5118 memmove(ap->anchor, &an, sizeof(PiAnchor));
5120 ptr += sizeof(Int) * (2 + n) + sizeof(Double) * n;
5122 } else if (strcmp(data, "TIME") == 0) {
5124 *timep = *((Int *)ptr);
5125 } else if (strcmp(data, "BONDPAR") == 0) {
5127 n = len / sizeof(BondPar);
5128 NewArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), n);
5129 memmove(par->bondPars, ptr, len);
5130 } else if (strcmp(data, "ANGPAR") == 0) {
5132 n = len / sizeof(AnglePar);
5133 NewArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), n);
5134 memmove(par->anglePars, ptr, len);
5135 } else if (strcmp(data, "DIHEPAR") == 0) {
5137 n = len / sizeof(TorsionPar);
5138 NewArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), n);
5139 memmove(par->dihedralPars, ptr, len);
5140 } else if (strcmp(data, "IMPRPAR") == 0) {
5142 n = len / sizeof(TorsionPar);
5143 NewArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), n);
5144 memmove(par->improperPars, ptr, len);
5145 } else if (strcmp(data, "VDWPAR") == 0) {
5147 n = len / sizeof(VdwPar);
5148 NewArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), n);
5149 memmove(par->vdwPars, ptr, len);
5150 } else if (strcmp(data, "VDWPPAR") == 0) {
5152 n = len / sizeof(VdwPairPar);
5153 NewArray(&par->vdwpPars, &par->nvdwpPars, sizeof(VdwPairPar), n);
5154 memmove(par->vdwpPars, ptr, len);
5155 } else if (strcmp(data, "VCUTPAR") == 0) {
5157 n = len / sizeof(VdwCutoffPar);
5158 NewArray(&par->vdwCutoffPars, &par->nvdwCutoffPars, sizeof(VdwCutoffPar), n);
5159 memmove(par->vdwCutoffPars, ptr, len);
5161 len += 8 + sizeof(Int);
5165 if (mp->par == NULL)
5166 ParameterRelease(par);
5167 /* result = MoleculeRebuildTablesFromConnects(mp);
5173 Panic("Low memory while deserializing molecule data");
5174 return NULL; /* Not reached */
5177 Panic("internal error: bad format during deserializing molecule data");
5178 return NULL; /* Not reached */
5182 MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep)
5185 int len, len_all, i, naniso, nframes, nconnects, nanchors;
5188 /* Array of atoms */
5189 len = 8 + sizeof(Int) + gSizeOfAtomRecord * mp->natoms;
5190 ptr = (char *)malloc(len);
5193 memmove(ptr, "ATOM\0\0\0\0", 8);
5194 *((Int *)(ptr + 8)) = gSizeOfAtomRecord * mp->natoms;
5195 p = ptr + 8 + sizeof(Int);
5196 memmove(p, mp->atoms, gSizeOfAtomRecord * mp->natoms);
5197 naniso = nframes = nconnects = nanchors = 0;
5198 for (i = 0; i < mp->natoms; i++) {
5199 ap = ATOM_AT_INDEX(p, i);
5200 if (ap->aniso != NULL) {
5204 if (ap->frames != NULL) {
5205 nframes += ap->nframes;
5208 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5209 nconnects += ap->connect.count;
5210 ap->connect.u.ptr = NULL;
5212 if (ap->anchor != NULL) {
5219 /* Array of aniso */
5221 len = 8 + sizeof(Int) + (sizeof(Int) + sizeof(Aniso)) * naniso;
5222 ptr = (char *)realloc(ptr, len_all + len);
5226 memmove(p, "ANISO\0\0\0", 8);
5227 *((Int *)(p + 8)) = (sizeof(Int) + sizeof(Aniso)) * naniso;
5228 p += 8 + sizeof(Int);
5229 for (i = 0; i < mp->natoms; i++) {
5230 ap = ATOM_AT_INDEX(mp->atoms, i);
5231 if (ap->aniso != NULL) {
5233 *((Aniso *)(p + sizeof(Int))) = *(ap->aniso);
5234 p += sizeof(Int) + sizeof(Aniso);
5240 /* Array of frames */
5242 len = 8 + sizeof(Int) + sizeof(Vector) * nframes;
5243 ptr = (char *)realloc(ptr, len_all + len);
5247 memmove(p, "FRAME\0\0\0", 8);
5248 *((Int *)(p + 8)) = sizeof(Vector) * nframes;
5249 p += 8 + sizeof(Int);
5250 for (i = 0; i < mp->natoms; i++) {
5251 ap = ATOM_AT_INDEX(mp->atoms, i);
5252 if (ap->frames != NULL) {
5253 memmove(p, ap->frames, sizeof(Vector) * ap->nframes);
5254 p += sizeof(Vector) * ap->nframes;
5260 /* Array of connects */
5261 if (nconnects > 0) {
5262 len = 8 + sizeof(Int) + sizeof(Int) * nconnects;
5263 ptr = (char *)realloc(ptr, len_all + len);
5267 memmove(p, "EXTCON\0\0", 8);
5268 *((Int *)(p + 8)) = sizeof(Int) * nconnects;
5269 p += 8 + sizeof(Int);
5270 for (i = 0; i < mp->natoms; i++) {
5271 ap = ATOM_AT_INDEX(mp->atoms, i);
5272 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5273 memmove(p, ap->connect.u.ptr, sizeof(Int) * ap->connect.count);
5274 p += sizeof(Int) * ap->connect.count;
5280 /* Bonds, angles, dihedrals, impropers */
5281 if (mp->nbonds > 0) {
5282 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nbonds;
5283 ptr = (char *)realloc(ptr, len_all + len);
5287 memmove(p, "BOND\0\0\0\0", 8);
5288 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nbonds;
5289 p += 8 + sizeof(Int);
5290 memmove(p, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
5293 if (mp->nangles > 0) {
5294 len = 8 + sizeof(Int) + sizeof(Int) * 3 * mp->nangles;
5295 ptr = (char *)realloc(ptr, len_all + len);
5299 memmove(p, "ANGLE\0\0\0", 8);
5300 *((Int *)(p + 8)) = sizeof(Int) * 3 * mp->nangles;
5301 p += 8 + sizeof(Int);
5302 memmove(p, mp->angles, sizeof(Int) * 3 * mp->nangles);
5305 if (mp->ndihedrals > 0) {
5306 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->ndihedrals;
5307 ptr = (char *)realloc(ptr, len_all + len);
5311 memmove(p, "DIHED\0\0\0", 8);
5312 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->ndihedrals;
5313 p += 8 + sizeof(Int);
5314 memmove(p, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
5317 if (mp->nimpropers > 0) {
5318 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->nimpropers;
5319 ptr = (char *)realloc(ptr, len_all + len);
5323 memmove(p, "IMPROP\0\0", 8);
5324 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->nimpropers;
5325 p += 8 + sizeof(Int);
5326 memmove(p, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
5330 /* Array of residues */
5331 if (mp->nresidues > 0) {
5332 len = 8 + sizeof(Int) + 4 * mp->nresidues;
5333 ptr = (char *)realloc(ptr, len_all + len);
5337 memmove(p, "RESIDUE\0", 8);
5338 *((Int *)(p + 8)) = 4 * mp->nresidues;
5339 p += 8 + sizeof(Int);
5340 memmove(p, mp->residues, 4 * mp->nresidues);
5345 if (mp->cell != NULL) {
5346 len = 8 + sizeof(Int) + sizeof(XtalCell);
5347 ptr = (char *)realloc(ptr, len_all + len);
5351 memmove(p, "CELL\0\0\0\0", 8);
5352 *((Int *)(p + 8)) = sizeof(XtalCell);
5353 p += 8 + sizeof(Int);
5354 memmove(p, mp->cell, sizeof(XtalCell));
5358 /* Symmetry operations */
5359 if (mp->nsyms > 0) {
5360 len = 8 + sizeof(Int) + sizeof(Transform) * mp->nsyms;
5361 ptr = (char *)realloc(ptr, len_all + len);
5365 memmove(p, "SYMOP\0\0\0", 8);
5366 *((Int *)(p + 8)) = sizeof(Transform) * mp->nsyms;
5367 p += 8 + sizeof(Int);
5368 memmove(p, mp->syms, sizeof(Transform) * mp->nsyms);
5374 /* Estimate the necessary storage first */
5375 /* One entry consists of { atom_index (Int), number_of_connects (Int), connects (Int's), weights (Double's) } */
5376 len = 8 + sizeof(Int);
5377 for (i = 0; i < mp->natoms; i++) {
5378 ap = ATOM_AT_INDEX(mp->atoms, i);
5379 if (ap->anchor != NULL)
5380 len += sizeof(Int) * 2 + (sizeof(Int) + sizeof(Double)) * ap->anchor->connect.count;
5382 ptr = (char *)realloc(ptr, len_all + len);
5386 memmove(p, "ANCHOR\0\0", 8);
5387 *((Int *)(p + 8)) = len - (8 + sizeof(Int));
5388 p += 8 + sizeof(Int);
5389 for (i = 0; i < mp->natoms; i++) {
5391 ap = ATOM_AT_INDEX(mp->atoms, i);
5392 if (ap->anchor != NULL) {
5393 count = ap->anchor->connect.count;
5395 *((Int *)(p + sizeof(Int))) = count;
5396 p += sizeof(Int) * 2;
5397 ip = AtomConnectData(&(ap->anchor->connect));
5398 memmove(p, ip, sizeof(Int) * count);
5399 p += sizeof(Int) * count;
5400 memmove(p, ap->anchor->coeffs, sizeof(Double) * count);
5401 p += sizeof(Double) * count;
5408 if (mp->par != NULL) {
5410 for (type = kFirstParType; type <= kLastParType; type++) {
5411 const char *parname;
5412 Int parsize, parcount;
5416 parname = "BONDPAR\0";
5417 parsize = sizeof(BondPar);
5418 parcount = mp->par->nbondPars;
5419 parptr = mp->par->bondPars;
5422 parname = "ANGPAR\0\0";
5423 parsize = sizeof(AnglePar);
5424 parcount = mp->par->nanglePars;
5425 parptr = mp->par->anglePars;
5427 case kDihedralParType:
5428 parname = "DIHEPAR\0";
5429 parsize = sizeof(TorsionPar);
5430 parcount = mp->par->ndihedralPars;
5431 parptr = mp->par->dihedralPars;
5433 case kImproperParType:
5434 parname = "IMPRPAR\0";
5435 parsize = sizeof(TorsionPar);
5436 parcount = mp->par->nimproperPars;
5437 parptr = mp->par->improperPars;
5440 parname = "VDWPAR\0\0";
5441 parsize = sizeof(VdwPar);
5442 parcount = mp->par->nvdwPars;
5443 parptr = mp->par->vdwPars;
5445 case kVdwPairParType:
5446 parname = "VDWPPAR\0";
5447 parsize = sizeof(VdwPairPar);
5448 parcount = mp->par->nvdwpPars;
5449 parptr = mp->par->vdwpPars;
5451 case kVdwCutoffParType:
5452 parname = "VCUTPAR\0";
5453 parsize = sizeof(VdwCutoffPar);
5454 parcount = mp->par->nvdwCutoffPars;
5455 parptr = mp->par->vdwCutoffPars;
5461 len = 8 + sizeof(Int) + parsize * parcount;
5462 ptr = (char *)realloc(ptr, len_all + len);
5466 memmove(p, parname, 8);
5467 *((Int *)(p + 8)) = parsize * parcount;
5468 p += 8 + sizeof(Int);
5469 memmove(p, parptr, parsize * parcount);
5477 time_t tm = time(NULL);
5478 len = 8 + sizeof(Int) + sizeof(Int);
5479 ptr = (char *)realloc(ptr, len_all + len);
5483 memmove(p, "TIME\0\0\0\0", 8);
5484 *((Int *)(p + 8)) = sizeof(Int);
5485 p += 8 + sizeof(Int);
5486 *((Int *)p) = (Int)tm;
5492 if (outLength != NULL)
5493 *outLength = len_all;
5497 Panic("Low memory while serializing a molecule data");
5498 return NULL; /* Not reached */
5501 #pragma mark ====== Search for bonds, angles, dihedrals, impropers ======
5504 sMoleculeSearchIncludingAtoms(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5508 IntGroup *gp = NULL;
5509 if (atomgroup == NULL)
5511 for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5512 for (j = 0; j < nsize; j++) {
5513 if (IntGroupLookup(atomgroup, ip[j], NULL) != 0) {
5516 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5517 Panic("Low memory while searching %s", msg);
5526 MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5530 return sMoleculeSearchIncludingAtoms(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5534 MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5538 return sMoleculeSearchIncludingAtoms(mp->nangles, mp->angles, 3, atomgroup, "angles");
5542 MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5546 return sMoleculeSearchIncludingAtoms(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5550 MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
5554 return sMoleculeSearchIncludingAtoms(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5558 sMoleculeSearchAcrossAtomGroup(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
5562 IntGroup *gp = NULL;
5563 if (atomgroup == NULL)
5565 for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
5567 for (j = 0; j < nsize; j++) {
5569 kk = (IntGroupLookup(atomgroup, ip[j], NULL) != 0);
5573 /* This bond etc. crosses the atom group border */
5576 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
5577 Panic("Low memory while searching %s", msg);
5586 MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5590 return sMoleculeSearchAcrossAtomGroup(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
5594 MoleculeSearchAnglesAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5598 return sMoleculeSearchAcrossAtomGroup(mp->nangles, mp->angles, 3, atomgroup, "angles");
5602 MoleculeSearchDihedralsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5606 return sMoleculeSearchAcrossAtomGroup(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
5610 MoleculeSearchImpropersAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
5614 return sMoleculeSearchAcrossAtomGroup(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
5617 /* Subroutine for MoleculeGuessBonds. It can be also used independently, but make sure that *outNbonds/*outBonds
5618 _correctly_ represents an array of two integers (as in mp->nbonds/mp->bonds). */
5619 /* Find atoms within the given "distance" from the given atom. */
5620 /* If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5621 the threshold distance is given by the sum of van der Waals radii times limit. */
5622 /* If triangle is non-zero, then only atoms with lower indexes than index are looked for. */
5624 MoleculeFindCloseAtoms(Molecule *mp, Int index, Double limit, Int *outNbonds, Int **outBonds, Int triangle)
5626 Int n1, n2, j, nlim, newbond[2];
5627 Double a1, a2, alim;
5629 Atom *ap = ATOM_AT_INDEX(mp->atoms, index);
5630 n1 = ap->atomicNumber;
5631 if (n1 >= 0 && n1 < gCountElementParameters)
5632 a1 = gElementParameters[n1].radius;
5633 else a1 = gElementParameters[6].radius;
5635 nlim = (triangle ? index : mp->natoms);
5636 for (j = 0; j < nlim; j++) {
5637 Atom *bp = ATOM_AT_INDEX(mp->atoms, j);
5640 n2 = bp->atomicNumber;
5641 if (n2 >= 0 && n2 < gCountElementParameters)
5642 a2 = gElementParameters[n2].radius;
5643 else a2 = gElementParameters[6].radius;
5649 alim = limit * (a1 + a2);
5650 if (VecLength2(dr) < alim * alim) {
5653 /* MoleculeAddBonds(mp, 1, newbonds); */
5654 AssignArray(outBonds, outNbonds, sizeof(Int) * 2, *outNbonds, newbond);
5660 /* Guess the bonds from the coordinates */
5661 /* If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
5662 the threshold distance is given by the sum of van der Waals radii times limit. */
5664 MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds)
5666 Int nbonds, *bonds, i, newbond[2];
5667 /* int i, j, n1, n2;
5670 Double a1, a2, alim;
5672 ElementPar *p = gElementParameters; */
5675 for (i = 0; i < mp->natoms; i++) {
5676 MoleculeFindCloseAtoms(mp, i, limit, &nbonds, &bonds, 1);
5678 ap = ATOM_AT_INDEX(mp->atoms, i);
5679 n1 = ap->atomicNumber;
5680 if (n1 >= 0 && n1 < gCountElementParameters)
5682 else a1 = p[6].radius;
5684 for (j = 0; j < i; j++) {
5685 bp = ATOM_AT_INDEX(mp->atoms, j);
5686 n2 = bp->atomicNumber;
5687 if (n2 >= 0 && n2 < gCountElementParameters)
5689 else a2 = p[6].radius;
5695 alim = limit * (a1 + a2);
5696 if (VecLength2(dr) < alim * alim) {
5699 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5705 newbond[0] = kInvalidIndex;
5707 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
5710 if (outNbonds != NULL)
5711 *outNbonds = nbonds;
5712 if (outBonds != NULL)
5717 /* Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information */
5719 MoleculeRebuildTablesFromConnects(Molecule *mp)
5721 int i, j, k, retval;
5728 if (mp->nbonds == 0) {
5729 for (i = 0; i < mp->natoms; i++) {
5730 ap = ATOM_AT_INDEX(mp->atoms, i);
5731 cp = AtomConnectData(&ap->connect);
5732 for (j = 0; j < ap->connect.count; j++) {
5738 /* MoleculeAddBonds() should not be used, because it assumes connects[] and
5739 bonds are already in sync */
5740 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
5741 /* retval = MoleculeAddBonds(mp, 1, ibuf);
5749 if (mp->nangles == 0) {
5750 for (i = 0; i < mp->natoms; i++) {
5751 ap = ATOM_AT_INDEX(mp->atoms, i);
5752 cp = AtomConnectData(&ap->connect);
5753 for (j = 0; j < ap->connect.count; j++) {
5754 for (k = j + 1; k < ap->connect.count; k++) {
5759 retval = MoleculeAddAngles(mp, ibuf, NULL);
5767 /* Find dihedrals */
5768 if (mp->ndihedrals == 0) {
5769 for (i = 0; i < mp->natoms; i++) {
5770 ap = ATOM_AT_INDEX(mp->atoms, i);
5771 cp = AtomConnectData(&ap->connect);
5772 for (j = 0; j < ap->connect.count; j++) {
5779 apjj = ATOM_AT_INDEX(mp->atoms, jj);
5780 cpjj = AtomConnectData(&apjj->connect);
5781 for (k = 0; k < ap->connect.count; k++) {
5785 for (m = 0; m < apjj->connect.count; m++) {
5787 if (mm == i || mm == kk)
5794 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
5803 /* Find impropers */
5804 if (mp->nimpropers == 0) {
5805 for (i = 0; i < mp->natoms; i++) {
5806 int i1, i2, i4, n1, n2, n4;
5807 ap = ATOM_AT_INDEX(mp->atoms, i);
5808 cp = AtomConnectData(&ap->connect);
5809 for (i1 = 0; i1 < ap->connect.count; i1++) {
5811 for (i2 = i1 + 1; i2 < ap->connect.count; i2++) {
5813 for (i4 = i2 + 1; i4 < ap->connect.count; i4++) {
5820 retval = MoleculeAddImpropers(mp, ibuf, NULL);
5829 mp->needsMDRebuild = 1;
5830 __MoleculeUnlock(mp);
5834 __MoleculeUnlock(mp);
5839 MoleculeAreAtomsConnected(Molecule *mol, int idx1, int idx2)
5841 Atom *ap1 = ATOM_AT_INDEX(mol->atoms, idx1);
5842 if (AtomConnectHasEntry(&ap1->connect, idx2))
5844 else if (ap1->anchor != NULL && AtomConnectHasEntry(&(ap1->anchor->connect), idx2))
5849 #pragma mark ====== Atom names ======
5851 /* Look for the n1-th atom in resno-th residue (n1 is 0-based) */
5853 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
5857 if (mp == NULL || mp->natoms == 0)
5860 for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5861 if (ap->resSeq == resno) {
5868 return lasti; /* max */
5873 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
5877 n = strtol(s, &p, 0);
5889 if ((p = strchr(s, ':')) != NULL) {
5890 /* Residue is specified */
5892 if ((pp = strchr(s, '.')) != NULL && pp < p) {
5893 /* Residue number is also specified */
5896 *resSeq = strtol(pp + 1, &ppp, 0);
5898 return -2; /* Bad format */
5899 while (isspace(*ppp))
5902 return -2; /* Bad format */
5905 /* Check whether the "residue name" is an integer */
5906 n = strtol(s, &pp, 0);
5908 while (isspace(*pp))
5910 if (*pp == 0 || *pp == ':') {
5913 return -2; /* Bad format */
5921 if (n >= sizeof(resName))
5922 n = sizeof(resName) - 1;
5923 strncpy(resName, s, n);
5931 strncpy(atomName, p, 4);
5936 /* Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer */
5938 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
5945 n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
5946 if (atomName[0] == 0) {
5947 if (n >= mp->natoms)
5948 n = -1; /* Out of range */
5951 for (n = 0; n < mp->natoms; n++) {
5952 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
5953 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
5954 && (resSeq < 0 || ap->resSeq == resSeq)
5955 && strncmp(atomName, ap->aname, 4) == 0) {
5959 return -1; /* Not found */
5963 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
5967 if (mp == NULL || index < 0 || index >= mp->natoms) {
5971 ap = mp->atoms + index;
5972 if (ap->resSeq != 0) {
5973 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
5977 snprintf(buf, bufsize, "%.4s", ap->aname);
5980 #pragma mark ====== Selection ======
5983 sMoleculeNotifyChangeSelection(Molecule *mp)
5985 /* TODO: Finer control of notification types may be necessary */
5986 MoleculeCallback_notifyModification(mp, 0);
5990 MoleculeSetSelection(Molecule *mp, IntGroup *select)
5995 IntGroupRetain(select);
5996 if (mp->selection != NULL)
5997 IntGroupRelease(mp->selection);
5998 mp->selection = select;
5999 sMoleculeNotifyChangeSelection(mp);
6003 MoleculeGetSelection(Molecule *mp)
6007 else return mp->selection;
6011 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
6013 if (mp->selection == NULL)
6014 mp->selection = IntGroupNew();
6016 IntGroupClear(mp->selection);
6017 IntGroupAdd(mp->selection, n1, 1);
6018 sMoleculeNotifyChangeSelection(mp);
6022 MoleculeUnselectAtom(Molecule *mp, int n1)
6024 if (mp->selection != NULL)
6025 IntGroupRemove(mp->selection, n1, 1);
6026 sMoleculeNotifyChangeSelection(mp);
6030 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
6032 if (mp->selection == NULL)
6033 mp->selection = IntGroupNew();
6034 IntGroupReverse(mp->selection, n1, 1);
6035 sMoleculeNotifyChangeSelection(mp);
6039 MoleculeIsAtomSelected(Molecule *mp, int n1)
6041 if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
6047 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
6049 if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
6055 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
6058 IntGroup *remain, *ig1, *ig2;
6060 remain = IntGroupNewFromIntGroup(remove);
6064 status = IntGroupReverse(remain, 0, mp->natoms);
6066 ig1 = IntGroupNew();
6070 status = IntGroupDifference(selection, remove, ig1);
6073 ig2 = IntGroupNew();
6077 status = IntGroupDeconvolute(ig1, remain, ig2);
6080 IntGroupRelease(remain);
6082 IntGroupRelease(ig1);
6087 IntGroupRelease(ig2);
6092 #pragma mark ====== Atom Equivalence ======
6096 struct sEqList *next;
6097 struct sEqList *link;
6100 static struct sEqList *sListBase = NULL;
6101 static struct sEqList *sListFree = NULL;
6103 static struct sEqList *
6107 if (sListFree != NULL) {
6109 sListFree = lp->next;
6110 lp->i[0] = lp->i[1] = 0;
6114 lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
6115 lp->link = sListBase;
6121 sFreeEqList(struct sEqList *list)
6123 list->next = sListFree;
6128 sDeallocateEqLists(void)
6130 struct sEqList *lp, *lp_link;
6131 for (lp = sListBase; lp != NULL; lp = lp_link) {
6140 sExistInEqList(int i, int idx, struct sEqList *list)
6142 while (list != NULL) {
6143 if (list->i[idx] == i)
6150 static struct sEqList *
6151 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
6154 struct sEqList *list1, *list2;
6155 Int ii, jj, ni, nj, *cpi, *cpj;
6156 api = ATOM_AT_INDEX(mol->atoms, i);
6157 apj = ATOM_AT_INDEX(mol->atoms, j);
6158 if (api->atomicNumber != apj->atomicNumber)
6160 list1 = sAllocEqList();
6166 if (i == j || (db[i] != NULL && db[i] == db[j]))
6168 cpi = AtomConnectData(&api->connect);
6169 cpj = AtomConnectData(&apj->connect);
6170 for (ni = 0; ni < api->connect.count; ni++) {
6172 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
6174 if (sExistInEqList(ii, 0, list1))
6177 for (nj = 0; nj < apj->connect.count; nj++) {
6179 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6181 if (sExistInEqList(jj, 1, list1))
6183 list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
6187 if (list2 == NULL) {
6189 return NULL; /* No equivalent to ii */
6191 list1 = list2; /* ii is OK, try next */
6197 sDBInclude(Int *ip, int i)
6202 for (j = ip[0] - 1; j >= 0; j--) {
6210 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
6212 Int **db; /* List of equivalents for each atom */
6214 Atom *api, *apj, *apk;
6215 Int *cpi, *cpj, *ibuf, nibuf;
6216 int i, j, k, ii, jj, kk;
6217 if (mol == NULL || mol->natoms == 0)
6219 db = (Int **)calloc(sizeof(Int *), mol->natoms);
6223 /* Find the equivalent univalent atoms */
6224 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6225 if (api->connect.count < 2)
6227 cpi = AtomConnectData(&api->connect);
6228 for (j = 0; j < api->connect.count; j++) {
6232 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6234 AssignArray(&ibuf, &nibuf, sizeof(Int), n, &jj);
6236 apj = ATOM_AT_INDEX(mol->atoms, jj);
6237 if (apj->connect.count != 1 || db[jj] != NULL)
6239 cpj = AtomConnectData(&apj->connect);
6240 for (k = j + 1; k < api->connect.count; k++) {
6242 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
6244 apk = ATOM_AT_INDEX(mol->atoms, kk);
6245 if (apk->connect.count != 1 || db[kk] != NULL)
6247 if (apj->atomicNumber == apk->atomicNumber) {
6248 AssignArray(&ibuf, &nibuf, sizeof(Int), n, &kk);
6253 ip = (Int *)calloc(sizeof(Int), n + 1);
6257 memmove(ip + 1, ibuf, sizeof(Int) * n);
6258 for (k = 0; k < n; k++)
6268 /* Try matching (i,j) pair */
6269 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6270 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
6272 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
6273 struct sEqList *list;
6274 if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
6276 if (api->atomicNumber != apj->atomicNumber)
6277 continue; /* Different elements do not match */
6278 if (db[i] != NULL && db[i] == db[j])
6279 continue; /* Already equivalent */
6280 list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
6282 continue; /* (i,j) do not match */
6283 while (list != NULL) {
6286 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
6287 /* Merge db[ii] and db[jj] */
6288 k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
6289 ip = (Int *)calloc(sizeof(Int), k + 1);
6291 return NULL; /* Out of memory */
6292 if (db[ii] == NULL) {
6296 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
6299 if (db[jj] == NULL) {
6302 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
6311 for (k = 0; k < ip[0]; k++)
6315 printf("(%d,%d) matched: ", ii, jj);
6316 for (k = 0; k < ip[0]; k++) {
6317 printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
6327 /* Record the equivalent atoms with the lowest index for each atom */
6328 result = (Int *)calloc(sizeof(Int), mol->natoms);
6329 for (i = 0; i < mol->natoms; i++)
6331 for (i = 0; i < mol->natoms; i++) {
6332 if (result[i] >= 0 || (ip = db[i]) == NULL)
6335 for (j = 0; j < ip[0]; j++) {
6340 for (j = 0; j < ip[0]; j++) {
6341 result[ip[j + 1]] = k;
6342 db[ip[j + 1]] = NULL;
6346 sDeallocateEqLists();
6350 #pragma mark ====== Symmetry expansion ======
6353 MoleculeGetTransformForSymop(Molecule *mp, Symop symop, Transform *tf, int is_cartesian)
6356 if (mp == NULL || mp->cell == NULL)
6358 if (symop.sym >= mp->nsyms && symop.sym != 0)
6360 memmove(*tf, SYMMETRY_AT_INDEX(mp->syms, symop.sym), sizeof(Transform));
6361 (*tf)[9] += symop.dx;
6362 (*tf)[10] += symop.dy;
6363 (*tf)[11] += symop.dz;
6365 TransformMul(t, *tf, mp->cell->rtr);
6366 TransformMul(*tf, mp->cell->tr, t);
6372 MoleculeGetSymopForTransform(Molecule *mp, const Transform tf, Symop *symop, int is_cartesian)
6376 if (mp == NULL || mp->cell == NULL)
6379 TransformMul(t, tf, mp->cell->tr);
6380 TransformMul(t, mp->cell->rtr, t);
6382 memmove(t, tf, sizeof(Transform));
6384 for (i = 0; i < mp->nsyms || i == 0; i++) {
6385 Transform *tp = &(SYMMETRY_AT_INDEX(mp->syms, i));
6386 for (j = 0; j < 9; j++) {
6387 if (fabs((*tp)[j] - t[j]) > 1e-4)
6391 for (j = 9; j < 12; j++) {
6392 double f1 = t[j] - (*tp)[j];
6393 double f2 = floor(f1 + 0.5);
6394 if (fabs(f1 - f2) > 1e-4)
6404 symop->alive = (SYMOP_ALIVE((*symop)) != 0);
6409 return -3; /* Not found */
6413 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
6417 if (symop.sym >= mp->nsyms && symop.sym != 0)
6419 if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
6420 TransformVec(vpout, mp->cell->rtr, vpin);
6421 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpout);
6422 vpout->x += symop.dx;
6423 vpout->y += symop.dy;
6424 vpout->z += symop.dz;
6425 TransformVec(vpout, mp->cell->tr, vpout);
6427 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpin);
6428 vpout->x += symop.dx;
6429 vpout->y += symop.dy;
6430 vpout->z += symop.dz;
6435 /* Add expanded atoms. Returns the number of newly created atoms.
6436 If indices is non-NULL, it should be an array of Int with at least
6437 IntGroupGetCount(group) entries, and on return it contains the
6438 indices of the expanded atoms (may be existing atoms if the expanded
6439 atoms are already present)
6440 If allowOverlap is non-zero, then the new atom is created even when the
6441 coordinates coincide with the some other atom (special position) of the
6442 same element; otherwise, such atom will not be created and the existing
6443 atom is returned in indices[]. */
6445 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group, Int *indices, Int allowOverlap)
6447 int i, n, n0, n1, n2, base, count, *table;
6449 IntGroupIterator iter;
6455 if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
6457 if (symop.sym != 0 && symop.sym >= mp->nsyms)
6460 /* Create atoms, with avoiding duplicates */
6461 n0 = n1 = mp->natoms;
6462 table = (int *)malloc(sizeof(int) * n0);
6465 for (i = 0; i < n0; i++)
6467 IntGroupIteratorInit(group, &iter);
6468 MoleculeGetTransformForSymop(mp, symop, &tr, 0);
6470 for (i = 0; i < count; i++) {
6471 n = IntGroupIteratorNext(&iter);
6472 ap = ATOM_AT_INDEX(mp->atoms, n);
6473 if (SYMOP_ALIVE(ap->symop)) {
6474 /* Calculate the cumulative symop */
6475 MoleculeGetTransformForSymop(mp, ap->symop, &t1, 0);
6476 TransformMul(tr, tr, t1);
6477 if (MoleculeGetSymopForTransform(mp, tr, &symop1, 0) != 0) {
6478 if (indices != NULL)
6480 continue; /* Skip this atom */
6488 /* Calculate the expande position */
6489 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
6491 /* Is this expansion already present? */
6492 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
6493 /* Symmetry operation and the base atom are the same */
6494 if (ap2->symbase == base && SYMOP_EQUAL(symop1, ap2->symop))
6496 /* Atomic number and the position are the same */
6497 if (ap2->atomicNumber == ap->atomicNumber && allowOverlap == 0) {
6498 VecSub(dr, ap2->r, nr);
6499 if (VecLength2(dr) < 1e-6)
6504 /* If yes, then skip it */
6505 if (indices != NULL)
6509 /* Create a new atom */
6511 AtomDuplicate(&newAtom, ap);
6512 MoleculeCreateAnAtom(mp, &newAtom, -1);
6513 AtomClean(&newAtom);
6514 ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
6518 ap2->symop.alive = (symop.dx != 0 || symop.dy != 0 || symop.dz != 0 || symop.sym != 0);
6519 table[n] = n1; /* The index of the new atom */
6520 MoleculeSetAnisoBySymop(mp, n1); /* Recalculate anisotropic parameters according to symop */
6521 if (indices != NULL)
6526 IntGroupIteratorRelease(&iter);
6529 for (i = n0; i < n1; i++) {
6531 ap = ATOM_AT_INDEX(mp->atoms, i);
6532 if (MoleculeGetSymopForTransform(mp, tr, &ap->symop, 0) == 0) {
6533 /* For each connected atom, look for the transformed atom */
6535 ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6536 cp = AtomConnectData(&ap2->connect);
6537 n2 = ap2->connect.count;
6538 for (n = 0; n < n2; n++) {
6539 nr = ATOM_AT_INDEX(mp->atoms, cp[n])->r;
6540 TransformVec(&nr, tr, &nr);
6541 for (j = 0, ap2 = mp->atoms; j < mp->natoms; j++, ap2 = ATOM_NEXT(ap2)) {
6542 if (ap2->symbase == cp[n] && SYMOP_EQUAL(ap->symop, ap2->symop))
6544 VecSub(dr, nr, ap2->r);
6545 if (ap2->atomicNumber == ap->atomicNumber && VecLength2(dr) < 1e-6)
6548 if (j < mp->natoms) {
6549 /* Bond i-j is created */
6552 if (MoleculeLookupBond(mp, b[0], b[1]) < 0)
6553 MoleculeAddBonds(mp, 1, b, NULL, 1);
6560 for (i = 0; i < n0; i++) {
6564 if (b[0] < 0 || b[0] == i)
6566 ap = ATOM_AT_INDEX(mp->atoms, i);
6567 cp = AtomConnectData(&ap->connect);
6568 for (n = 0; n < ap->connect.count; n++) {
6569 b[1] = table[cp[n]];
6572 if (b[1] > n0 && b[0] > b[1])
6574 if (MoleculeLookupBond(mp, b[0], b[1]) >= 0)
6576 MoleculeAddBonds(mp, 1, b, NULL, 1);
6580 mp->needsMDRebuild = 1;
6581 __MoleculeUnlock(mp);
6583 return n1 - n0; /* The number of added atoms */
6586 /* Recalculate the coordinates of symmetry expanded atoms.
6587 (Also recalculate the positions of pi-anchor atoms)
6588 Returns the number of affected atoms.
6589 If group is non-NULL, only the expanded atoms whose base atoms are in the
6590 given group are considered.
6591 If groupout and vpout are non-NULL, the indices of the affected atoms
6592 and the original positions are returned (for undo operation).
6593 The pointers returned in *groupout and *vpout must be released and
6594 free()'ed by the caller */
6596 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
6601 IntGroup *ig = NULL;
6604 if (mp == NULL || mp->natoms == 0)
6609 if (mp->nsyms != 0) {
6610 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6611 if (!SYMOP_ALIVE(ap->symop))
6613 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
6615 bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
6616 MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
6617 VecSub(dr, nr, ap->r);
6618 if (VecLength2(dr) < 1e-20)
6620 if (groupout != NULL) {
6623 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
6626 IntGroupAdd(ig, i, 1);
6632 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6634 if (ap->anchor == NULL)
6636 if (group != NULL) {
6637 if (IntGroupLookup(group, i, NULL) == 0) {
6638 n = ap->anchor->connect.count;
6639 ip = AtomConnectData(&(ap->anchor->connect));
6640 for (j = 0; j < n; j++) {
6641 if (IntGroupLookup(group, ip[j], NULL) != 0)
6645 continue; /* This pi-anchor should not be modified */
6649 MoleculeCalculatePiAnchorPosition(mp, i);
6650 VecSub(dr, nr, ap->r);
6651 if (VecLength2(dr) < 1e-20) {
6652 ap->r = nr; /* No change */
6655 if (groupout != NULL) {
6658 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
6661 IntGroupAdd(ig, i, 1);
6665 mp->needsMDCopyCoordinates = 1;
6666 __MoleculeUnlock(mp);
6669 if (groupout != NULL && vpout != NULL) {
6671 *vpout = (Vector *)realloc(vp, sizeof(Vector) * count);
6673 IntGroupRelease(ig);
6677 if (groupout != NULL && vpout != NULL) {
6685 #pragma mark ====== Show/hide atoms ======
6688 sMoleculeNotifyChangeAppearance(Molecule *mp)
6690 /* TODO: Finer control of notification types may be necessary */
6691 MoleculeCallback_notifyModification(mp, 0);
6696 sMoleculeUnselectHiddenAtoms(Molecule *mp)
6699 if (mp == NULL || mp->selection == NULL)
6701 for (i = 0; i < mp->natoms; i++) {
6702 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6703 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
6704 IntGroupRemove(mp->selection, i, 1);
6706 sMoleculeNotifyChangeAppearance(mp);
6710 MoleculeShowAllAtoms(Molecule *mp)
6715 for (i = 0; i < mp->natoms; i++) {
6716 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6717 ap->exflags &= ~kAtomHiddenFlag;
6719 sMoleculeNotifyChangeAppearance(mp);
6724 MoleculeShowReverse(Molecule *mp)
6729 for (i = 0; i < mp->natoms; i++) {
6730 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6731 ap->exflags ^= kAtomHiddenFlag;
6733 sMoleculeUnselectHiddenAtoms(mp);
6734 sMoleculeNotifyChangeAppearance(mp);
6739 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
6742 if (mp == NULL || ig == NULL)
6744 for (i = 0; i < mp->natoms; i++) {
6745 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
6746 if (ap->exflags & kAtomHiddenFlag)
6747 continue; /* Already hidden */
6748 if (IntGroupLookupPoint(ig, i) >= 0)
6749 ap->exflags |= kAtomHiddenFlag;
6751 sMoleculeUnselectHiddenAtoms(mp);
6752 sMoleculeNotifyChangeAppearance(mp);
6756 #pragma mark ====== Reversible Editing ======
6760 sMoleculeNotifyModification(Molecule *mp)
6762 ** TODO: Finer control of notification types may be necessary **
6763 MoleculeCallback_notifyModification(mp, 0);
6767 /* Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup */
6769 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
6772 if (where == NULL) {
6773 /* Append the new objects at the end */
6774 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
6777 n1 = IntGroupGetCount(where); /* Position to get new object */
6778 n2 = nobjs; /* Position to get old object */
6779 n3 = n1 + n2; /* Position to place new/old object */
6780 for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
6781 int start = IntGroupGetStartPoint(where, i);
6782 int end = IntGroupGetEndPoint(where, i);
6784 /* old[end-(n3-n2)..n2-1] is moved to old[end..n3-1] */
6785 memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
6786 n2 = end - (n3 - n2);
6789 /* new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1] */
6790 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
6797 /* Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
6799 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6801 int n1, n2, n3, start, end, i;
6802 if (objs == NULL || where == NULL)
6803 return 1; /* Bad argument */
6804 n1 = 0; /* Position to move remaining elements to */
6805 n2 = 0; /* Position to move remaining elements from */
6806 n3 = 0; /* Position to move removed elements to */
6807 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6808 end = IntGroupGetEndPoint(where, i);
6810 /* Move (start - n2) elements from objs[n2] to objs[n1] */
6812 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
6816 /* Move (end - start) elements from objs[n2] to clip[n3] */
6818 memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
6819 n3 += (end - start);
6820 n2 += (end - start);
6822 /* Move (nobjs - n2) elements from objs[n2] to objs[n1] */
6824 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
6828 /* Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
6830 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
6832 int n1, start, end, i;
6833 if (objs == NULL || where == NULL)
6834 return 1; /* Bad argument */
6835 n1 = 0; /* Position to move removed elements to */
6836 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
6837 end = IntGroupGetEndPoint(where, i);
6838 /* Copy (end - start) elements from objs[start] to clip[n1] */
6840 memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
6841 n1 += (end - start);
6846 /* Create a new atom with no bonding information. ap must _not_ be inside the given molecule
6847 (Use AtomDuplicate() first) */
6849 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
6853 if (mp == NULL || ap == NULL || mp->noModifyTopology)
6856 if (pos < 0 || pos >= mp->natoms)
6858 ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
6860 goto error; /* Out of memory */
6861 ap1 = ATOM_AT_INDEX(mp->atoms, pos);
6862 if (pos < mp->natoms - 1) {
6863 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6865 if (AtomDuplicate(ap1, ap) == NULL) {
6866 /* Cannot duplicate: restore the original state */
6867 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
6871 ap1->connect.count = 0;
6872 if (ap1->resSeq >= mp->nresidues)
6873 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
6874 if (ap1->resName[0] == 0)
6875 strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
6876 if (ap1->segName[0] == 0)
6877 strncpy(ap1->segName, "MAIN", 4);
6878 if (pos < mp->natoms - 1) {
6879 /* Renumber the connect table, bonds, angles, etc. */
6880 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
6883 cp = AtomConnectData(&api->connect);
6884 for (j = 0; j < api->connect.count; j++) {
6888 if (api->anchor != NULL) {
6889 cp = AtomConnectData(&api->anchor->connect);
6890 for (j = 0; j < api->anchor->connect.count; j++) {
6896 for (i = 0; i < mp->nbonds * 2; i++) {
6897 if (mp->bonds[i] >= pos)
6900 for (i = 0; i < mp->nangles * 3; i++) {
6901 if (mp->angles[i] >= pos)
6904 for (i = 0; i < mp->ndihedrals * 4; i++) {
6905 if (mp->dihedrals[i] >= pos)
6908 for (i = 0; i < mp->nimpropers * 4; i++) {
6909 if (mp->impropers[i] >= pos)
6913 mp->nframes = -1; /* Should be recalculated later */
6914 MoleculeIncrementModifyCount(mp);
6915 mp->needsMDRebuild = 1;
6916 __MoleculeUnlock(mp);
6919 __MoleculeUnlock(mp);
6925 static int s_error_count;
6928 s_fprintf(FILE *fp, const char *fmt, ...)
6933 return vfprintf(fp, fmt, va);
6937 MoleculeCheckSanity(Molecule *mol)
6939 const char *fail = "Sanity check failure";
6940 Int i, j, *ip, c[4];
6943 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
6944 if (ap->resSeq >= mol->nresidues)
6945 s_fprintf(stderr, "%s: atom %d residue %d but nresidues %d\n", fail, i, ap->resSeq, mol->nresidues);
6946 if (ap->type != 0 && ap->type < kAtomTypeMinimum)
6947 s_fprintf(stderr, "%s: atom %d atom type %d less than minimum\n", fail, i, ap->type);
6948 if (ap->atomicNumber < 0 || ap->atomicNumber > 113)
6949 s_fprintf(stderr, "%s: atom %d atomic number %d\n", fail, i, ap->atomicNumber);
6950 ip = AtomConnectData(&ap->connect);
6951 for (j = 0; j < ap->connect.count; j++) {
6952 if (ip[j] < 0 || ip[j] >= mol->natoms)
6953 s_fprintf(stderr, "%s: atom %d connect[%d] = %d out of range\n", fail, i, j, ip[j]);
6954 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[j])->connect), i) == 0)
6955 s_fprintf(stderr, "%s: atom %d has connect %d but atom %d has no connect %d\n", fail, i, ip[j], ip[j], i);
6958 for (i = 0, ip = mol->bonds; i < mol->nbonds; i++, ip += 2) {
6959 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms)
6960 s_fprintf(stderr, "%s: bond %d %d-%d out of range\n", fail, i, ip[0], ip[1]);
6961 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[0])->connect), ip[1]) == 0)
6962 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]);
6964 for (i = 0, ip = mol->angles; i < mol->nangles; i++, ip += 3) {
6965 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms)
6966 s_fprintf(stderr, "%s: angle %d %d-%d-%d out of range\n", fail, i, ip[0], ip[1], ip[2]);
6967 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
6969 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]);
6970 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
6972 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]);
6973 if (c[0] == 2 && c[1] == 2)
6974 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]);
6976 for (i = 0, ip = mol->dihedrals; i < mol->ndihedrals; i++, ip += 4) {
6977 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)
6978 s_fprintf(stderr, "%s: dihedral %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
6979 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
6980 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
6981 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
6983 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]);
6985 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]);
6987 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]);
6989 for (i = 0, ip = mol->impropers; i < mol->nimpropers; i++, ip += 4) {
6990 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)
6991 s_fprintf(stderr, "%s: improper %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
6992 c[0] = MoleculeAreAtomsConnected(mol, ip[2], ip[0]);
6993 c[1] = MoleculeAreAtomsConnected(mol, ip[2], ip[1]);
6994 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
6996 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]);
6998 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]);
7000 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]);
7002 return s_error_count;
7006 /* Merge two molecules. We use this procedure for all add-atom operations. */
7007 /* resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
7008 /* If nactions and actions are non-NULL, then the corresponding undo actions are created and returned. */
7009 /* If forUndo is non-zero, then only the atoms are inserted; other information should be inserted
7010 separately by other undo actions. */
7012 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, Int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7015 Int i, j, n1, n2, n3, n4, *cp;
7016 Int *new2old, *old2new;
7021 if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
7022 return 0; /* Do nothing */
7024 if (dst->noModifyTopology)
7025 return 1; /* Prohibited operation */
7027 if (where != NULL && IntGroupGetCount(where) != src->natoms)
7028 return 1; /* Bad parameter */
7030 if (nactions != NULL)
7032 if (actions != NULL)
7036 __MoleculeLock(dst);
7040 if (resSeqOffset < 0)
7043 /* Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
7044 and ndst..ndst+nsrc-1 are for atoms in src. */
7045 new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
7046 if (new2old == NULL)
7048 old2new = new2old + ndst + nsrc;
7049 n1 = 0; /* dst index */
7050 n2 = 0; /* src index */
7051 n3 = 0; /* "merged" index */
7053 while (n1 < ndst || n2 < nsrc) {
7054 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
7057 /* n4 elements from dst[n1] will go to merged[n3] */
7058 for (j = 0; j < n4; j++) {
7059 old2new[n1 + j] = n3 + j;
7060 new2old[n3 + j] = n1 + j;
7064 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
7066 /* n4 elements from src[n2] will go to merged[n3] */
7067 for (j = 0; j < n4; j++) {
7068 old2new[ndst + n2 + j] = n3 + j;
7069 new2old[n3 + j] = ndst + n2 + j;
7076 /* Expand the destination array */
7077 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
7080 /* Move the atoms */
7081 if (where == NULL) {
7082 /* Duplicate atoms to the end of the destination array */
7083 for (i = 0; i < nsrc; i++) {
7084 ap = ATOM_AT_INDEX(dst->atoms, ndst + i);
7085 if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7087 if (forUndo) /* For undo action, all bonds come from another undo action, so connection info are cleared */
7088 AtomConnectResize(&ap->connect, 0);
7091 /* Duplicate to a temporary storage and then insert */
7092 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
7093 if (tempatoms == NULL)
7095 for (i = 0; i < nsrc; i++) {
7096 ap = ATOM_AT_INDEX(tempatoms, i);
7097 if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7099 if (forUndo) /* See above */
7100 AtomConnectResize(&ap->connect, 0);
7102 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
7106 dst->natoms = ndst + nsrc;
7108 /* Renumber the atom indices in connect[] and symbase, and modify the residue numbers */
7109 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7110 if (new2old[i] < ndst) {
7111 /* This atom is from dst */
7114 /* This atom is from src */
7115 n1 = ndst; /* Offset to the internal number */
7116 if (ap->resSeq != 0)
7117 ap->resSeq += resSeqOffset; /* Modify residue number */
7119 cp = AtomConnectData(&ap->connect);
7120 for (j = 0; j < ap->connect.count; j++)
7121 cp[j] = old2new[cp[j] + n1];
7122 if (SYMOP_ALIVE(ap->symop))
7123 ap->symbase = old2new[ap->symbase + n1];
7124 if (ap->anchor != NULL) {
7125 cp = AtomConnectData(&ap->anchor->connect);
7126 for (j = 0; j < ap->anchor->connect.count; j++)
7127 cp[j] = old2new[cp[j] + n1];
7131 /* Move the bonds, angles, dihedrals, impropers */
7132 for (i = 0; i < 4; i++) {
7133 Int *nitems, *nitems_src;
7134 Int **items, **items_src;
7135 Int nsize; /* Number of Ints in one element */
7138 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
7140 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
7142 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
7144 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
7146 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
7147 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
7149 /* During undo, no bonds etc. are copied from src; they will be taken care later
7154 /* Keep the old number of entries in dst, because it is updated by AssignArray() */
7156 /* Also keep the old number of entries in src, in case src and dst point the same molecule */
7158 /* Expand the array */
7159 if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
7161 /* Copy the items */
7162 memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
7165 for (j = 0; j < n1 * nsize; j++)
7166 (*items)[j] = old2new[(*items)[j]];
7167 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
7168 (*items)[j] = old2new[(*items)[j] + ndst];
7169 if (forUndo == 0 && actions != NULL) {
7170 ig = IntGroupNewWithPoints(n1, n2, -1);
7172 case 0: act = MolActionNew(gMolActionDeleteBonds, ig); break;
7173 case 1: act = MolActionNew(gMolActionDeleteAngles, ig); break;
7174 case 2: act = MolActionNew(gMolActionDeleteDihedrals, ig); break;
7175 case 3: act = MolActionNew(gMolActionDeleteImpropers, ig); break;
7177 IntGroupRelease(ig);
7178 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7183 /* Renumber existing parameters */
7184 if (dst->par != NULL) {
7186 for (type = kFirstParType; type <= kLastParType; type++) {
7188 n1 = ParameterGetCountForType(dst->par, type);
7189 for (i = 0; i < n1; i++) {
7190 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7191 ParameterRenumberAtoms(type, up1, ndst, old2new);
7196 /* Merge parameters from src */
7197 if (src->par != NULL && forUndo == 0) {
7198 UnionPar *up1, *up2;
7200 if (dst->par == NULL)
7201 dst->par = ParameterNew();
7203 /* Renumber existing parameters */
7204 for (type = kFirstParType; type <= kLastParType; type++) {
7205 n1 = ParameterGetCountForType(dst->par, type);
7206 for (i = 0; i < n1; i++) {
7207 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7208 ParameterRenumberAtoms(type, up1, ndst, old2new);
7213 for (type = kFirstParType; type <= kLastParType; type++) {
7214 n1 = ParameterGetCountForType(src->par, type);
7215 n2 = ParameterGetCountForType(dst->par, type);
7218 /* Determine which parameter should be copied from src to dst */
7219 for (i = 0; i < n1; i++) {
7221 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
7222 n3 = ParameterGetAtomTypes(type, up1, types);
7223 for (j = 0; j < n3; j++) {
7224 /* If it includes explicit atom index, then it should be copied */
7225 if (types[j] < kAtomTypeMinimum) {
7226 IntGroupAdd(ig, i, 1);
7231 for (j = 0; j < n2; j++) {
7232 up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
7233 if (ParameterCompare(up1, up2, type))
7237 /* This is an unknown parameter; should be copied */
7238 IntGroupAdd(ig, i, 1);
7241 n1 = IntGroupGetCount(ig);
7244 up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
7247 /* Copy parameters and renumber indices if necessary */
7248 for (i = j = 0; i < n1; i++) {
7249 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
7253 ParameterRenumberAtoms(type, up1 + j, nsrc, old2new + ndst);
7256 /* Merge parameters */
7258 IntGroupAdd(ig, n2, j);
7259 if (ParameterInsert(dst->par, type, up1, ig) < j)
7261 if (actions != NULL) {
7262 act = MolActionNew(gMolActionDeleteParameters, type, ig);
7263 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7269 IntGroupRelease(ig);
7272 /* Copy the residues if necessary */
7273 /* src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
7274 However, 1+resSeqOffset should not overwrite the existing residue in dst;
7275 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1]. */
7277 n1 = dst->nresidues;
7278 if (1 + resSeqOffset < n1) {
7280 } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
7281 if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
7282 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
7284 memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
7285 if (nactions != NULL) {
7286 act = MolActionNew(gMolActionChangeNumberOfResidues, n1);
7287 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7293 MoleculeCleanUpResidueTable(dst);
7296 dst->nframes = -1; /* Should be recalculated later */
7298 MoleculeIncrementModifyCount(dst);
7299 dst->needsMDRebuild = 1;
7300 __MoleculeUnlock(dst);
7304 __MoleculeUnlock(dst);
7305 Panic("Low memory while adding atoms");
7306 return 1; /* Not reached */
7309 /* Unmerge the molecule. If necessary, the undo actions are stored in nactions/actions array.
7310 (The nactions/actions array must be initialized by the caller) */
7312 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag, Int *nactions, MolAction ***actions, Int forUndo)
7314 Int nsrc, ndst, nsrcnew;
7315 Int i, j, n1, n2, n3, n4, *cp;
7316 Int *new2old, *old2new;
7317 IntGroup *move_g, *del_g, *remain_g, *dst_par_g, *remove_par_g;
7323 if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
7330 if (src->noModifyTopology && moveFlag)
7331 return 1; /* Prohibit editing */
7333 if ((ndst = IntGroupGetCount(where)) > src->natoms)
7334 return 1; /* Bad parameter */
7336 __MoleculeLock(src);
7341 nsrcnew = nsrc - ndst;
7342 if (resSeqOffset < 0)
7345 /* Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
7346 and nsrcnew..nsrc-1 are for atoms moved into dst. */
7347 new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
7348 if (new2old == NULL)
7350 old2new = new2old + nsrc;
7351 n1 = 0; /* src index */
7352 n2 = 0; /* dst index */
7353 n3 = 0; /* src index after "unmerge" */
7355 while (n1 < nsrc || n2 < ndst) {
7356 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
7359 /* n4 elements from src[n1] will go to unmerged[n3] */
7360 for (j = 0; j < n4; j++) {
7361 old2new[n1 + j] = n3 + j;
7362 new2old[n3 + j] = n1 + j;
7366 if ((n4 = IntGroupGetInterval(where, i)) < 0)
7368 /* n4 elements from src[n1] will go to dst[n2] */
7369 for (j = 0; j < n4; j++) {
7370 old2new[n1 + j] = nsrcnew + n2 + j;
7371 new2old[nsrcnew + n2 + j] = n1 + j;
7378 /* Atoms to remain in the source group */
7380 remain_g = IntGroupNewWithPoints(0, nsrc, -1);
7381 IntGroupRemoveIntGroup(remain_g, where);
7382 } else remain_g = NULL;
7384 /* Find parameters to be moved to the dst (dst_par_g), and to be removed from the src (remove_par_g) */
7385 if (src->par != NULL) {
7386 dst_par_g = IntGroupNew();
7388 remove_par_g = IntGroupNew();
7389 else remove_par_g = NULL;
7390 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7391 n2 = ParameterGetCountForType(src->par, n1);
7394 for (i = 0; i < n2; i++) {
7395 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7396 if (ParameterIsRelevantToAtomGroup(n1, up, src->atoms, where)) {
7397 /* This parameter is to be copied to dst */
7398 IntGroupAdd(dst_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7400 if (moveFlag && !ParameterIsRelevantToAtomGroup(n1, up, src->atoms, remain_g)) {
7401 /* This parameter is to be removed */
7402 IntGroupAdd(remove_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7406 } else dst_par_g = remove_par_g = NULL;
7408 /* Pi anchors should be modified if the anchor and its component atoms become separated between
7411 Int ibufsize, *ibuf, flag_i, flag_j;
7413 ibuf = (Int *)malloc(sizeof(Int) * ibufsize);
7414 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7415 if (ap->anchor == NULL)
7417 flag_i = (old2new[i] < nsrcnew);
7418 cp = AtomConnectData(&ap->anchor->connect);
7419 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7420 flag_j = (old2new[cp[j]] < nsrcnew);
7421 if (flag_i == flag_j) {
7422 if (n1 >= ibufsize) {
7424 ibuf = (Int *)realloc(ibuf, sizeof(Int) * ibufsize);
7430 /* Need to modify the pi anchor list */
7433 MolActionCreateAndPerform(src, SCRIPT_ACTION("isI"), "set_atom_attr", i, "anchor_list", n1, ibuf);
7438 /* Make a new molecule */
7440 dst = MoleculeNew();
7443 /* Expand the destination array */
7444 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
7446 dst_ap = dst->atoms;
7449 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
7454 /* Move the atoms */
7456 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
7458 src->natoms = nsrcnew;
7460 /* The atom record must be deallocated correctly */
7461 for (i = 0; i < ndst; i++)
7462 AtomClean(ATOM_AT_INDEX(dst_ap, i));
7466 for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
7467 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
7472 /* The dummy destination array is no longer needed */
7477 /* Renumber the atom indices in connect[] (src) */
7479 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7480 cp = AtomConnectData(&ap->connect);
7481 for (j = n1 = 0; j < ap->connect.count; j++) {
7482 n2 = old2new[cp[j]];
7486 AtomConnectResize(&ap->connect, n1);
7487 if (ap->anchor != NULL) {
7488 cp = AtomConnectData(&ap->anchor->connect);
7489 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7490 n2 = old2new[cp[j]];
7494 if (n1 != ap->anchor->connect.count) {
7495 /* This should not happen!! */
7496 AtomConnectResize(&ap->anchor->connect, n1);
7497 fprintf(stderr, "Internal error in sMoleculeUnmergeSub (line %d)\n", __LINE__);
7499 free(ap->anchor->coeffs);
7508 /* Renumber the atom indices in connect[] (dst) */
7510 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7511 if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
7512 ap->resSeq -= resSeqOffset;
7513 else ap->resSeq = 0;
7514 cp = AtomConnectData(&ap->connect);
7515 for (j = n1 = 0; j < ap->connect.count; j++) {
7516 n2 = old2new[cp[j]] - nsrcnew;
7520 AtomConnectResize(&ap->connect, n1);
7521 if (ap->anchor != NULL) {
7522 cp = AtomConnectData(&ap->anchor->connect);
7523 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7524 n2 = old2new[cp[j]] - nsrcnew;
7528 if (n1 != ap->anchor->connect.count) {
7529 /* This can happen, and the anchor info is silently modified */
7531 AtomConnectResize(&ap->anchor->connect, 0);
7532 free(ap->anchor->coeffs);
7537 AtomConnectResize(&ap->anchor->connect, n1);
7539 for (j = 0; j < n1; j++)
7540 d += ap->anchor->coeffs[j];
7541 for (j = 0; j < n1; j++)
7542 ap->anchor->coeffs[j] /= d;
7543 MoleculeCalculatePiAnchorPosition(dst, i);
7550 /* Separate the bonds, angles, dihedrals, impropers */
7551 /* TODO: Improper torsions should also be copied! */
7552 move_g = IntGroupNew();
7555 for (i = 3; i >= 0; i--) {
7556 Int *nitems, *nitems_dst;
7557 Int **items, **items_dst;
7558 Int nsize; /* Number of Ints in one element */
7559 unsigned char *counts;
7560 del_g = IntGroupNew();
7563 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
7565 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
7567 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
7569 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
7571 nitems = NULL; items = NULL; nsize = 0; break; /* Not reached */
7574 nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
7575 items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
7580 counts = (unsigned char *)calloc(1, *nitems);
7581 /* Find the entries that should be moved to dst */
7583 for (j = 0; j < *nitems * nsize; j++) {
7584 n1 = old2new[(*items)[j]];
7586 counts[j / nsize]++; /* Count the atom belonging to dst */
7588 for (j = n2 = n3 = 0; j < *nitems; j++) {
7589 if (counts[j] > 0) {
7590 /* Remove from src */
7592 if (IntGroupAdd(del_g, j, 1) != 0)
7594 if (counts[j] == nsize) {
7597 if (IntGroupAdd(move_g, j, 1) != 0)
7603 /* Expand the destination array */
7604 if (items_dst != NULL && n3 > 0) {
7605 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
7607 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
7610 /* Remove from src */
7611 if (moveFlag && forUndo == 0) {
7612 if (nactions != NULL) {
7614 ip = (Int *)malloc(sizeof(Int) * nsize * n2);
7615 for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
7616 memmove(ip + j * nsize, *items + k * nsize, sizeof(Int) * nsize);
7619 act = MolActionNew(gMolActionAddBondsForUndo, n2 * nsize, ip, del_g); break;
7621 act = MolActionNew(gMolActionAddAngles, n2 * nsize, ip, del_g); break;
7623 act = MolActionNew(gMolActionAddDihedrals, n2 * nsize, ip, del_g); break;
7625 act = MolActionNew(gMolActionAddImpropers, n2 * nsize, ip, del_g); break;
7628 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7633 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
7638 /* Renumber the entries */
7640 for (j = 0; j < *nitems * nsize; j++) {
7641 (*items)[j] = old2new[(*items)[j]];
7644 if (items_dst != NULL) {
7645 for (j = 0; j < *nitems_dst * nsize; j++) {
7646 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
7650 IntGroupClear(move_g);
7651 IntGroupRelease(del_g);
7653 IntGroupRelease(move_g);
7655 /* Copy the residues */
7657 /* src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset) */
7658 n1 = src->nresidues - resSeqOffset; /* This will be dst->nresidues (if >0) */
7659 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
7662 memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
7666 /* Copy the parameters to dst */
7667 if (dst != NULL && dst_par_g != NULL && (n2 = IntGroupGetCount(dst_par_g)) > 0) {
7668 IntGroup *dst_new_g = IntGroupNew();
7669 Int dst_par_count[kLastParType - kFirstParType + 1];
7670 if (dst_new_g == NULL)
7672 for (i = 0; i <= kLastParType - kFirstParType; i++)
7673 dst_par_count[i] = 0;
7674 up = (UnionPar *)calloc(sizeof(UnionPar), n2);
7677 if (ParameterCopy(src->par, kFirstParType, up, dst_par_g) < n2)
7679 /* Renumber the explicit atom indices */
7680 for (i = 0; i < nsrc; i++)
7681 old2new[i] -= nsrcnew; /* new indices for atoms in dst; otherwise negative numbers */
7682 for (i = 0; i < n2; i++) {
7683 /* Renumber the indices, and count the number of parameters for each type */
7684 n1 = kFirstParType + IntGroupGetNthPoint(dst_par_g, i) / kParameterIndexOffset;
7685 dst_par_count[n1 - kFirstParType]++;
7686 ParameterRenumberAtoms(n1, up + i, nsrc, old2new);
7688 for (i = 0; i < nsrc; i++)
7689 old2new[i] += nsrcnew;
7690 if (dst->par == NULL)
7691 dst->par = ParameterNew();
7692 for (i = 0; i <= kLastParType - kFirstParType; i++) {
7693 if (dst_par_count[i] > 0)
7694 IntGroupAdd(dst_new_g, i * kParameterIndexOffset, dst_par_count[i]);
7696 if (ParameterInsert(dst->par, kFirstParType, up, dst_new_g) < n2)
7699 IntGroupRelease(dst_new_g);
7701 IntGroupRelease(dst_par_g);
7703 /* Remove the unused parameter. Note: the parameters that are in remove_par_g and not in
7704 dst_par_g will disappear. To support undo, these parameters should be taken care separately. */
7705 if (forUndo == 0 && remove_par_g != NULL && (n2 = IntGroupGetCount(remove_par_g)) > 0) {
7706 UnionPar *up = (UnionPar *)malloc(sizeof(UnionPar) * n2);
7707 ParameterDelete(src->par, kFirstParType, up, remove_par_g);
7708 if (nactions != NULL) {
7709 act = MolActionNew(gMolActionAddParameters, kFirstParType, remove_par_g, n2, up);
7710 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7715 IntGroupRelease(remove_par_g);
7717 /* Renumber the parameter records remaining in the src */
7719 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7720 n2 = ParameterGetCountForType(src->par, n1);
7721 for (i = 0; i < n2; i++) {
7722 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7723 ParameterRenumberAtoms(n1, up, nsrc, old2new);
7729 IntGroupRelease(remain_g);
7730 MoleculeCleanUpResidueTable(src);
7732 MoleculeCleanUpResidueTable(dst);
7735 src->nframes = -1; /* Should be recalculated later */
7737 dst->nframes = -1; /* Should be recalculated later */
7743 MoleculeIncrementModifyCount(src);
7744 src->needsMDRebuild = 1;
7745 __MoleculeUnlock(src);
7750 __MoleculeUnlock(src);
7751 /* Panic("Low memory while removing atoms"); */
7755 /* Separate molecule into two parts. The atoms specified by 'where' are moved
7756 from src to a new molecule, which is returned as *dstp. Dstp can be NULL,
7757 in which case the moved atoms are discarded. */
7759 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7761 return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1, nactions, actions, forUndo);
7764 /* Extract atoms from a given molecule into two parts. The atoms specified by
7765 'where' are copied from src to a new molecule, which is returned as *dstp.
7766 If dummyFlag is non-zero, then the atoms that are not included in the group
7767 but are connected to any atoms in the group are converted to "dummy" atoms
7768 (i.e. with element "Du" and names beginning with an underscore) and included
7769 in the new molecule object. */
7771 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
7775 /* Extract the fragment */
7776 retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0, NULL, NULL, 0);
7782 /* Search bonds crossing the molecule border */
7783 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
7785 IntGroupIterator iter;
7788 IntGroupIteratorInit(ig, &iter);
7789 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7790 /* The atoms at the border */
7793 n1 = src->bonds[i*2];
7794 n2 = src->bonds[i*2+1];
7795 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
7799 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
7800 continue; /* Actually this is an internal error */
7802 /* n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule */
7803 /* Create a new dummy atom with the same segment/residue info with n1
7804 and the same position as n2 */
7805 ap = ATOM_AT_INDEX(src->atoms, n1);
7806 memset(&a, 0, gSizeOfAtomRecord);
7807 a.segSeq = ap->segSeq;
7808 memmove(a.segName, ap->segName, 4);
7809 a.resSeq = ap->resSeq;
7810 memmove(a.resName, ap->resName, 4);
7811 ElementToString(0, a.element); /* "Du" */
7812 snprintf(a.aname, 4, "_%d", idx++);
7813 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
7814 /* Add the dummy atom to the new molecule; nn[1] is the index
7815 of the new dummy atom in the new molecule */
7816 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
7817 /* Connect nn1 and nn2 */
7818 nn[2] = kInvalidIndex;
7819 MoleculeAddBonds(*dstp, 1, nn, NULL, 1);
7821 IntGroupIteratorRelease(&iter);
7822 IntGroupRelease(ig);
7830 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds, IntGroup *where, Int autoGenerate)
7832 Int nangles, ndihedrals;
7833 Int *angles, *dihedrals;
7834 Int i, j, k, kk, n1, n2;
7837 Atom *ap1, *ap2, *ap3;
7839 if (mp == NULL || bonds == NULL || nbonds <= 0)
7841 if (mp->noModifyTopology)
7842 return -4; /* Prohibited operation */
7844 /* Note: Duplicates and validity are not checked (the caller must do that) */
7849 if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, n1 + nbonds - 1, NULL) == NULL
7850 || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nbonds, sizeof(Int) * 2, where) != 0) {
7851 __MoleculeUnlock(mp);
7852 return -4; /* Out of memory */
7855 angles = dihedrals = NULL;
7856 nangles = ndihedrals = 0;
7858 /* Add connects[], and angles/dihedrals (if autoGenerate is true) */
7859 for (i = 0; i < nbonds; i++) {
7861 /* One entry at time */
7862 /* (Otherwise, duplicate entries of angles and dihedrals result) */
7864 n2 = bonds[i * 2 + 1];
7866 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
7867 AtomConnectInsertEntry(&ap1->connect, -1, n2);
7868 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
7869 AtomConnectInsertEntry(&ap2->connect, -1, n1);
7871 /* Add angles and dihedrals */
7873 AtomConnect *ac1, *ac2;
7874 if (ap1->anchor == NULL || ap2->anchor == NULL) {
7875 /* N1-N2-{XY} or N2-N1-{XY} angles (X: connected atom, Y: constitute atom of pi-anchor) */
7876 for (j = 0; j < 4; j++) {
7878 case 0: temp[0] = n1; temp[1] = n2; ac1 = &ap2->connect; break; /* N1-N2-X */
7879 case 1: if (ap2->anchor == NULL) continue; else ac1 = &ap2->anchor->connect; break; /* N1-N2-Y */
7880 case 2: temp[0] = n2; temp[1] = n1; ac1 = &ap1->connect; break; /* N2-N1-X */
7881 case 3: if (ap1->anchor == NULL) continue; else ac1 = &ap1->anchor->connect; break; /* N2-N1-Y */
7883 cp1 = AtomConnectData(ac1);
7884 for (k = 0; k < ac1->count; k++) {
7886 if (temp[2] == temp[0])
7888 ap3 = ATOM_AT_INDEX(mp->atoms, temp[2]);
7889 if (ap3->anchor != NULL) {
7890 /* Avoid X-anchor-anchor angle (anchor-X-anchor is allowed) */
7891 if ((j < 2 && ap2->anchor != NULL) || (j >= 2 && ap1->anchor != NULL))
7894 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7896 /* Dihedrals N1-N2-X-X or N2-N1-X-X */
7897 if (j == 1 || j == 3)
7899 cp2 = AtomConnectData(&ap3->connect);
7900 for (kk = 0; kk < ap3->connect.count; kk++) {
7902 if (temp[3] == temp[0] || temp[3] == temp[1])
7904 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7910 /* X-N1-N2-X angles */
7911 if (ap1->anchor == NULL)
7912 ac1 = &ap1->connect;
7913 else ac1 = &ap1->anchor->connect;
7914 if (ap2->anchor == NULL)
7915 ac2 = &ap2->connect;
7916 else ac2 = &ap2->anchor->connect;
7919 cp1 = AtomConnectData(ac1);
7920 cp2 = AtomConnectData(ac2);
7921 for (j = 0; j < ac1->count; j++) {
7923 if (temp[0] == temp[2])
7925 if (ATOM_AT_INDEX(mp->atoms, temp[0])->anchor != NULL)
7927 for (k = 0; k < ac2->count; k++) {
7929 if (temp[3] == temp[0] || temp[3] == temp[1])
7931 if (ATOM_AT_INDEX(mp->atoms, temp[3])->anchor != NULL)
7933 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7940 if (angles != NULL) {
7941 temp[0] = kInvalidIndex;
7942 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
7944 MoleculeAddAngles(mp, angles, NULL);
7947 if (dihedrals != NULL) {
7948 temp[0] = kInvalidIndex;
7949 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
7951 MoleculeAddDihedrals(mp, dihedrals, NULL);
7955 MoleculeIncrementModifyCount(mp);
7956 mp->needsMDRebuild = 1;
7957 __MoleculeUnlock(mp);
7962 __MoleculeUnlock(mp);
7963 Panic("Low memory while adding bonds");
7964 return -1; /* Not reached */
7968 /* The deleted angles and dihedrals are stored in outRemoval. */
7969 /* (*outRemoval) is an array of integers, containing:
7970 [0..na*3-1]: the angle indices
7971 [na*3..na*3+nd*4-1]: the dihedral indices
7972 [na*3+nd*4..na*3+nd*4+ni*4-1]: the improper indices
7973 *outRemovedPos is an intgroup denoting the positions of the removed angles/dihedrals/impropers.
7974 the angle indices are included as they are,
7975 the dihedral indices are offset by ATOMS_MAX_NUMBER,
7976 the improper indices are offset by ATOMS_MAX_NUMBER*2.
7977 Note: the removed bond indices are not returned, because the caller should already know them. */
7979 MoleculeDeleteBonds(Molecule *mp, Int *bonds, IntGroup *where, Int **outRemoved, IntGroup **outRemovedPos)
7981 Int i, j, n1, n2, nw;
7982 Int *ip, *jp, na, nd, ni;
7983 IntGroup *ag, *dg, *ig;
7985 IntGroupIterator iter;
7989 if (mp->noModifyTopology)
7990 return -4; /* Prohibited operation */
7994 /* Update connects[] */
7995 IntGroupIteratorInit(where, &iter);
7996 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7997 n1 = mp->bonds[i * 2];
7998 n2 = mp->bonds[i * 2 + 1];
7999 ap = ATOM_AT_INDEX(mp->atoms, n1);
8000 ip = AtomConnectData(&ap->connect);
8001 for (j = 0; j < ap->connect.count; j++) {
8003 AtomConnectDeleteEntry(&ap->connect, j);
8007 ap = ATOM_AT_INDEX(mp->atoms, n2);
8008 ip = AtomConnectData(&ap->connect);
8009 for (j = 0; j < ap->connect.count; j++) {
8011 AtomConnectDeleteEntry(&ap->connect, j);
8017 /* Remove bonds, angles, dihedrals, impropers */
8018 ag = dg = ig = NULL;
8021 nw = IntGroupGetCount(where);
8022 jp = (Int *)malloc(sizeof(Int) * nw * 2);
8024 IntGroupIteratorReset(&iter);
8025 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8026 jp[j++] = mp->bonds[i * 2];
8027 jp[j++] = mp->bonds[i * 2 + 1];
8029 IntGroupIteratorRelease(&iter);
8031 for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8032 for (j = 0; j < nw; j++) {
8035 if ((ip[0] == n1 && ip[1] == n2)
8036 || (ip[1] == n1 && ip[0] == n2)
8037 || (ip[1] == n1 && ip[2] == n2)
8038 || (ip[2] == n1 && ip[1] == n2)) {
8041 if (IntGroupAdd(ag, i, 1) != 0)
8048 for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8049 for (j = 0; j < nw; j++) {
8052 if ((ip[0] == n1 && ip[1] == n2)
8053 || (ip[1] == n1 && ip[0] == n2)
8054 || (ip[1] == n1 && ip[2] == n2)
8055 || (ip[2] == n1 && ip[1] == n2)
8056 || (ip[2] == n1 && ip[3] == n2)
8057 || (ip[3] == n1 && ip[2] == n2)) {
8060 if (IntGroupAdd(dg, i, 1) != 0)
8067 for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8068 for (j = 0; j < nw; j++) {
8071 if ((ip[0] == n1 && ip[2] == n2)
8072 || (ip[1] == n1 && ip[2] == n2)
8073 || (ip[3] == n1 && ip[2] == n2)
8074 || (ip[0] == n2 && ip[2] == n1)
8075 || (ip[1] == n2 && ip[2] == n1)
8076 || (ip[3] == n2 && ip[2] == n1)) {
8079 if (IntGroupAdd(ig, i, 1) != 0)
8088 if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, bonds, sizeof(Int) * 2, where) != 0)
8090 mp->nbonds -= IntGroupGetCount(where);
8091 if (mp->nbonds == 0) {
8095 if (na == 0 && nd == 0 && ni == 0)
8098 ip = (Int *)malloc(sizeof(Int) * (na * 3 + nd * 4 + ni * 4));
8100 MoleculeDeleteAngles(mp, ip, ag);
8102 MoleculeDeleteDihedrals(mp, ip + na * 3, dg);
8104 MoleculeDeleteImpropers(mp, ip + na * 3 + nd * 4, ig);
8106 IntGroupOffset(dg, ATOMS_MAX_NUMBER);
8107 IntGroupOffset(ig, ATOMS_MAX_NUMBER * 2);
8108 IntGroupAddIntGroup(ag, dg);
8109 IntGroupAddIntGroup(ag, ig);
8110 IntGroupRelease(dg);
8111 IntGroupRelease(ig);
8115 *outRemovedPos = ag;
8117 MoleculeIncrementModifyCount(mp);
8118 mp->needsMDRebuild = 1;
8119 __MoleculeUnlock(mp);
8121 return na * 3 + nd * 4 + ni * 4;
8124 __MoleculeUnlock(mp);
8125 Panic("Low memory while removing bonds");
8126 return -1; /* Not reached */
8130 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
8133 if (mp == NULL || angles == NULL)
8135 if (mp->noModifyTopology)
8136 return -4; /* Prohibited operation */
8140 nc = IntGroupGetCount(where);
8142 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
8148 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
8149 || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
8150 __MoleculeUnlock(mp);
8151 Panic("Low memory while adding angles");
8154 mp->needsMDRebuild = 1;
8155 __MoleculeUnlock(mp);
8160 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
8163 if (mp == NULL || where == NULL)
8165 if (mp->noModifyTopology)
8166 return -4; /* Prohibited operation */
8168 if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
8169 __MoleculeUnlock(mp);
8170 Panic("Low memory while adding angles");
8172 mp->nangles -= (nc = IntGroupGetCount(where));
8173 if (mp->nangles == 0) {
8177 mp->needsMDRebuild = 1;
8178 __MoleculeUnlock(mp);
8183 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
8186 if (mp == NULL || dihedrals == NULL)
8188 if (mp->noModifyTopology)
8189 return -4; /* Prohibited operation */
8191 nc = IntGroupGetCount(where);
8193 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
8199 n1 = mp->ndihedrals;
8201 if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8202 || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
8203 __MoleculeUnlock(mp);
8204 Panic("Low memory while adding dihedrals");
8206 mp->needsMDRebuild = 1;
8207 __MoleculeUnlock(mp);
8212 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
8215 if (mp == NULL || where == NULL)
8217 if (mp->noModifyTopology)
8218 return -4; /* Prohibited operation */
8220 if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
8221 __MoleculeUnlock(mp);
8222 Panic("Low memory while adding dihedrals");
8224 mp->ndihedrals -= (nc = IntGroupGetCount(where));
8225 if (mp->ndihedrals == 0) {
8226 free(mp->dihedrals);
8227 mp->dihedrals = NULL;
8229 mp->needsMDRebuild = 1;
8230 __MoleculeUnlock(mp);
8235 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
8238 if (mp == NULL || impropers == NULL)
8240 if (mp->noModifyTopology)
8241 return -4; /* Prohibited operation */
8243 nc = IntGroupGetCount(where);
8245 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
8251 n1 = mp->nimpropers;
8253 if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8254 || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
8255 __MoleculeUnlock(mp);
8256 Panic("Low memory while adding impropers");
8258 mp->needsMDRebuild = 1;
8259 __MoleculeUnlock(mp);
8264 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
8267 if (mp == NULL || where == NULL)
8269 if (mp->noModifyTopology)
8270 return -4; /* Prohibited operation */
8272 if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
8273 __MoleculeUnlock(mp);
8274 Panic("Low memory while adding impropers");
8276 mp->nimpropers -= (nc = IntGroupGetCount(where));
8277 if (mp->impropers == NULL) {
8278 free(mp->impropers);
8279 mp->impropers = NULL;
8281 __MoleculeUnlock(mp);
8286 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
8289 if (mp == NULL || mp->bonds == NULL)
8291 for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
8292 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
8299 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
8302 if (mp == NULL || mp->angles == NULL)
8304 for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8305 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
8306 (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
8313 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8316 if (mp == NULL || mp->dihedrals == NULL)
8318 for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8319 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
8320 (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
8327 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8330 if (mp == NULL || mp->impropers == NULL)
8332 for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8335 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
8336 (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
8337 (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
8343 /* Remove the bond at bondIndex and create two dummy atoms instead.
8344 The dummy atoms are placed at the end of atoms[], and the residue
8345 numbers are the same as the root atoms (i.e. the atoms to which
8346 the dummy atoms are connected). The indices are returned in
8347 dummyIndices[0,1]. */
8349 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
8351 Int roots[3], newBonds[5];
8357 if (mp == NULL || mp->noModifyTopology)
8359 if (bondIndex < 0 || bondIndex >= mp->nbonds)
8361 roots[0] = mp->bonds[bondIndex * 2];
8362 roots[1] = mp->bonds[bondIndex * 2 + 1];
8363 roots[2] = kInvalidIndex;
8364 rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
8365 rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
8366 VecSub(dr, rootp[0]->r, rootp[1]->r);
8367 for (i = 0; i < 2; i++) {
8370 memmove(nap, rootp[i], sizeof(na));
8371 nap->aname[0] = '*';
8372 strcpy(nap->element, "Du");
8374 nap->charge = nap->weight = 0.0;
8375 nap->atomicNumber = 0;
8376 nap->connect.count = 0;
8377 w = (i == 0 ? 0.4 : -0.4);
8378 VecScaleInc(nap->r, dr, w);
8385 /* Expand atoms array and append the dummy atoms at the end */
8387 natoms = mp->natoms;
8388 if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
8390 memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
8391 dummyIndices[0] = natoms;
8392 dummyIndices[1] = natoms + 1;
8394 /* Remove the old bond and create new bonds */
8395 ig = IntGroupNewWithPoints(bondIndex, 1, -1);
8398 MoleculeDeleteBonds(mp, NULL, ig, NULL, NULL);
8399 IntGroupRelease(ig);
8400 newBonds[0] = roots[0];
8401 newBonds[1] = dummyIndices[0];
8402 newBonds[2] = roots[1];
8403 newBonds[3] = dummyIndices[1];
8404 newBonds[4] = kInvalidIndex;
8406 i = (MoleculeAddBonds(mp, 2, newBonds, NULL, 1) < 0 ? -1 : 0);
8407 mp->needsMDRebuild = 1;
8408 __MoleculeUnlock(mp);
8412 __MoleculeUnlock(mp);
8413 Panic("Low memory during creating dummy atoms");
8417 /* Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
8418 a bond between the two root atoms. The value bondIndex is used as a
8419 hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
8420 the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
8421 is ignored and the new bond is stored at the end of bonds[]. */
8423 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
8430 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
8433 if (mol == NULL || mol->noModifyTopology)
8440 __MoleculeLock(mol);
8441 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
8442 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
8443 mol->needsMDRebuild = 1;
8444 __MoleculeUnlock(mol);
8451 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
8454 if (mol == NULL || mol->noModifyTopology)
8456 n1 = mol->ndihedrals;
8457 np1 = mol->dihedrals;
8458 mol->ndihedrals = 0;
8459 mol->dihedrals = NULL;
8460 if (ndihedrals > 0) {
8461 __MoleculeLock(mol);
8462 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
8463 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
8464 mol->needsMDRebuild = 1;
8465 __MoleculeUnlock(mol);
8467 *outDihedrals = np1;
8472 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
8475 if (mol == NULL || mol->noModifyTopology)
8477 n1 = mol->nimpropers;
8478 np1 = mol->impropers;
8479 mol->nimpropers = 0;
8480 mol->impropers = NULL;
8481 if (nimpropers > 0) {
8482 __MoleculeLock(mol);
8483 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
8484 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
8485 mol->needsMDRebuild = 1;
8486 __MoleculeUnlock(mol);
8488 *outImpropers = np1;
8494 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
8501 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8502 return 0; /* molecule is empty */
8503 if (mol->noModifyTopology)
8507 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
8508 Int *cp = AtomConnectData(&ap->connect);
8509 if (ap->anchor != NULL)
8511 for (j = 0; j < ap->connect.count; j++) {
8513 if (ATOM_AT_INDEX(mol->atoms, j0)->anchor != NULL)
8515 for (k = j + 1; k < ap->connect.count; k++) {
8517 if (ATOM_AT_INDEX(mol->atoms, k0)->anchor != NULL)
8519 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
8520 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
8529 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
8533 if (outAngles != NULL)
8534 *outAngles = angles;
8539 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
8541 Int n1, n2, n3, n4, *ip, *cp2, *cp3;
8546 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8547 return 0; /* molecule is empty */
8550 for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
8551 Int i1, i3, i4, *ip;
8552 if (ap2->anchor != NULL)
8554 cp2 = AtomConnectData(&ap2->connect);
8555 for (i3 = 0; i3 < ap2->connect.count; i3++) {
8559 ap3 = ATOM_AT_INDEX(mol->atoms, n3);
8560 if (ap3->anchor != NULL)
8562 cp3 = AtomConnectData(&ap3->connect);
8563 for (i1 = 0; i1 < ap2->connect.count; i1++) {
8567 if (ATOM_AT_INDEX(mol->atoms, n1)->anchor != NULL)
8569 for (i4 = 0; i4 < ap3->connect.count; i4++) {
8571 if (n2 == n4 || n1 == n4)
8573 if (ATOM_AT_INDEX(mol->atoms, n4)->anchor != NULL)
8575 if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
8576 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
8586 if (ndihedrals > 0) {
8587 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
8591 if (outDihedrals != NULL)
8592 *outDihedrals = dihedrals;
8597 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
8599 Int n1, n2, n3, n4, t1, t2, t3, t4, *ip, *cp;
8600 Parameter *par = mol->par;
8605 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
8606 return 0; /* molecule is empty */
8607 if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
8608 return 0; /* No improper parameters are defined */
8612 for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
8613 Int i1, i2, i4, found, *ip;
8615 cp = AtomConnectData(&ap3->connect);
8616 for (i1 = 0; i1 < ap3->connect.count; i1++) {
8618 t1 = ATOM_AT_INDEX(ap, n1)->type;
8619 for (i2 = i1 + 1; i2 < ap3->connect.count; i2++) {
8621 t2 = ATOM_AT_INDEX(ap, n2)->type;
8622 for (i4 = i2 + 1; i4 < ap3->connect.count; i4++) {
8624 t4 = ATOM_AT_INDEX(ap, n4)->type;
8626 if (ParameterLookupImproperPar(par, t1, t2, t3, t4, n1, n2, n3, n4, 0) != NULL)
8628 else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, -1, -1, -1, -1, 0) != NULL)
8630 if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
8631 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8641 if (nimpropers > 0) {
8642 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
8646 if (outImpropers != NULL)
8647 *outImpropers = impropers;
8651 #pragma mark ====== Residues ======
8654 MoleculeCleanUpResidueTable(Molecule *mp)
8658 if (mp == NULL || mp->natoms == 0)
8662 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8663 if (ap->resSeq >= maxres)
8664 maxres = ap->resSeq + 1;
8665 if (ap->resSeq < mp->nresidues) {
8666 if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
8667 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8669 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
8672 if (maxres < mp->nresidues)
8673 mp->nresidues = maxres;
8674 __MoleculeUnlock(mp);
8677 /* Change the number of residues. If nresidues is greater than the current value,
8678 then the array mp->residues is expanded with null names. If nresidues is smaller
8679 than the current value, mp->nresidues is set to the smallest possible value
8680 that is no smaller than nresidues and larger than any of the resSeq values. */
8682 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
8687 if (mp->nresidues == nresidues)
8689 else if (mp->nresidues < nresidues) {
8692 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
8693 while (n < nresidues)
8694 mp->residues[n++][0] = 0;
8695 __MoleculeUnlock(mp);
8701 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8702 if (ap->resSeq >= n)
8711 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
8713 IntGroupIterator iter;
8714 int withArray, resSeq, maxSeq;
8718 /* If LSB of resSeqs is 1, then a constant value is used for all specified atoms */
8719 if (((int)resSeqs & 1) == 0) {
8724 resSeq = ((int)resSeqs - 1) / 2;
8727 IntGroupIteratorInit(group, &iter);
8729 /* Change resSeqs */
8733 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8734 ap = ATOM_AT_INDEX(mp->atoms, i);
8736 resSeq = resSeqs[j++];
8737 if (resSeq > maxSeq)
8739 ap->resSeq = resSeq;
8741 __MoleculeUnlock(mp);
8743 /* Expand array if necessary */
8744 if (maxSeq >= mp->nresidues)
8745 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8747 /* Synchronize resName and residues[] */
8749 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8750 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8752 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8754 IntGroupIteratorRelease(&iter);
8755 __MoleculeUnlock(mp);
8757 MoleculeIncrementModifyCount(mp);
8763 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
8765 return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(resSeq * 2 + 1));
8768 /* Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
8769 specifies the mp->nresidues after modifying the residue numbers.
8770 If all atoms are modified, then the table of residue names is also shifted. Otherwise,
8771 the table of residue names is not touched. */
8773 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
8775 int i, maxSeq, nmodatoms;
8777 IntGroupIterator iter;
8778 IntGroupIteratorInit(group, &iter);
8781 nresidues = mp->nresidues;
8784 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8785 ap = ATOM_AT_INDEX(mp->atoms, i);
8786 ap->resSeq += offset;
8787 if (ap->resSeq < 0) {
8788 /* Bad argument; undo change and returns this index + 1 */
8790 ap->resSeq -= offset;
8791 while ((i = IntGroupIteratorLast(&iter)) >= 0) {
8792 ap = ATOM_AT_INDEX(mp->atoms, i);
8793 ap->resSeq -= offset;
8795 IntGroupIteratorRelease(&iter);
8796 return bad_index + 1;
8798 if (ap->resSeq > maxSeq)
8799 maxSeq = ap->resSeq;
8802 if (maxSeq >= nresidues)
8803 nresidues = maxSeq + 1;
8804 if (offset < 0 && nmodatoms == mp->natoms) {
8805 /* Shift the residue names downward */
8806 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
8808 __MoleculeUnlock(mp);
8809 MoleculeChangeNumberOfResidues(mp, nresidues);
8810 if (offset > 0 && nmodatoms == mp->natoms) {
8811 /* Shift the residue names upward */
8813 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
8814 __MoleculeUnlock(mp);
8816 IntGroupIteratorRelease(&iter);
8818 MoleculeIncrementModifyCount(mp);
8823 /* Change residue names for the specified residue numbers. Names is an array of
8824 chars containing argc*4 characters, and every 4 characters represent a
8825 residue name; characters '\x01'-'\x1f' are converted to '\0', which allow
8826 names to be handled as a C string. */
8828 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
8833 for (i = 0; i < argc; i++) {
8834 if (maxSeq < resSeqs[i])
8835 maxSeq = resSeqs[i];
8837 if (maxSeq >= mp->nresidues)
8838 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
8840 for (i = 0; i < argc; i++) {
8841 char *p = mp->residues[resSeqs[i]];
8843 strncpy(p, names + i * 4, 4);
8844 for (j = 0; j < 4; j++) {
8845 if (p[j] >= 0 && p[j] < 0x20)
8849 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8850 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
8852 __MoleculeUnlock(mp);
8854 MoleculeIncrementModifyCount(mp);
8859 /* Returns the maximum residue number actually used */
8861 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
8866 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8867 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8869 if (ap->resSeq > maxSeq)
8870 maxSeq = ap->resSeq;
8875 /* Returns the minimum residue number actually used */
8877 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
8881 minSeq = ATOMS_MAX_NUMBER;
8882 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
8883 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
8885 if (ap->resSeq < minSeq)
8886 minSeq = ap->resSeq;
8888 return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
8891 #pragma mark ====== Sort by Residues ======
8894 sAtomSortComparator(const void *a, const void *b)
8896 const Atom *ap, *bp;
8897 ap = *((const Atom **)a);
8898 bp = *((const Atom **)b);
8899 if (ap->resSeq == bp->resSeq) {
8900 /* Retain the original order (i.e. atom with larger pointer address is larger) */
8907 /* Compare the residue sequence. However, residue sequence 0 is always larger. */
8908 if (ap->resSeq == 0)
8910 else if (bp->resSeq == 0)
8912 else if (ap->resSeq < bp->resSeq)
8914 else if (ap->resSeq > bp->resSeq)
8921 sMoleculeReorder(Molecule *mp)
8923 int i, res, prevRes;
8927 if (mp == NULL || mp->natoms <= 1)
8930 /* Sort the atoms, bonds, etc. */
8931 apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
8932 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
8933 newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
8934 if (apArray == NULL || old2new == NULL || newAtoms == NULL)
8935 Panic("Low memory during reordering atoms");
8936 for (i = 0; i < mp->natoms; i++)
8937 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
8939 /* Sort the atoms. Note: apArray is an array of "Pointer to Atom" */
8940 qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
8942 /* Make a table of 'which atom becomes which' */
8943 for (i = 0; i < mp->natoms; i++) {
8944 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
8948 /* Renumber the bonds, etc. */
8949 for (i = 0; i < mp->nbonds * 2; i++) {
8950 mp->bonds[i] = old2new[mp->bonds[i]];
8952 for (i = 0; i < mp->nangles * 3; i++) {
8953 mp->angles[i] = old2new[mp->angles[i]];
8955 for (i = 0; i < mp->ndihedrals * 4; i++) {
8956 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
8958 for (i = 0; i < mp->nimpropers * 4; i++) {
8959 mp->impropers[i] = old2new[mp->impropers[i]];
8961 for (i = 0; i < mp->natoms; i++) {
8963 ip = AtomConnectData(&(apArray[i]->connect));
8964 for (j = 0; j < apArray[i]->connect.count; j++, ip++)
8968 /* Renumber the residues so that the residue numbers are contiguous */
8970 for (i = 0; i < mp->natoms; i++) {
8971 if (apArray[i]->resSeq == 0)
8973 if (apArray[i]->resSeq != prevRes) {
8975 prevRes = apArray[i]->resSeq;
8976 if (prevRes != res) {
8977 strncpy(mp->residues[res], mp->residues[prevRes], 4);
8980 apArray[i]->resSeq = res;
8982 mp->nresidues = res + 1;
8984 /* Sort the atoms and copy back to atoms[] */
8985 for (i = 0; i < mp->natoms; i++) {
8986 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
8988 memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
8990 /* Free the locally allocated storage */
8996 /* Renumber atoms */
8998 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
9000 Int *old2new, i, j, retval;
9004 if (mp->noModifyTopology)
9006 if (old2new_out != NULL)
9007 old2new = old2new_out;
9009 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9010 saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9011 if (old2new == NULL || saveAtoms == NULL)
9012 Panic("Low memory during reordering atoms");
9013 memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
9015 for (i = 0; i < mp->natoms; i++)
9017 for (i = 0; i < isize && i < mp->natoms; i++) {
9019 if (j < 0 || j >= mp->natoms) {
9020 retval = 1; /* Out of range */
9023 if (old2new[j] != -1) {
9024 retval = 2; /* Duplicate entry */
9029 if (i < mp->natoms) {
9030 for (j = 0; j < mp->natoms; j++) {
9031 if (old2new[j] != -1)
9036 if (i != mp->natoms) {
9037 retval = 3; /* Internal inconsistency */
9041 /* Renumber the bonds, etc. */
9042 for (i = 0; i < mp->nbonds * 2; i++) {
9043 mp->bonds[i] = old2new[mp->bonds[i]];
9045 for (i = 0; i < mp->nangles * 3; i++) {
9046 mp->angles[i] = old2new[mp->angles[i]];
9048 for (i = 0; i < mp->ndihedrals * 4; i++) {
9049 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9051 for (i = 0; i < mp->nimpropers * 4; i++) {
9052 mp->impropers[i] = old2new[mp->impropers[i]];
9054 /* Renumber the connection table and pi anchor table */
9055 for (i = 0; i < mp->natoms; i++) {
9056 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
9057 Int *ip = AtomConnectData(&ap->connect);
9058 for (j = 0; j < ap->connect.count; j++, ip++)
9060 if (ap->anchor != NULL) {
9061 ip = AtomConnectData(&ap->anchor->connect);
9062 for (j = 0; j < ap->anchor->connect.count; j++, ip++)
9067 if (mp->par != NULL) {
9068 /* Renumber the parameters */
9070 for (j = kFirstParType; j <= kLastParType; j++) {
9071 n = ParameterGetCountForType(mp->par, j);
9072 for (i = 0; i < n; i++) {
9073 UnionPar *up = ParameterGetUnionParFromTypeAndIndex(mp->par, j, i);
9075 ParameterRenumberAtoms(j, up, mp->natoms, old2new);
9080 /* Renumber the atoms */
9081 for (i = 0; i < mp->natoms; i++)
9082 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
9085 MoleculeIncrementModifyCount(mp);
9086 mp->needsMDRebuild = 1;
9089 __MoleculeUnlock(mp);
9091 if (old2new_out == NULL)
9096 #pragma mark ====== Coordinate Transform ======
9099 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
9104 Transform rtr, symtr;
9105 if (mp == NULL || tr == NULL)
9107 TransformInvert(rtr, tr);
9109 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9110 if (group == NULL || IntGroupLookup(group, i, NULL) != 0) {
9111 TransformVec(&ap->r, tr, &ap->r);
9112 if (!SYMOP_ALIVE(ap->symop))
9114 /* Transform symop */
9115 if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9117 TransformMul(symtr, tr, symtr);
9118 if (group == NULL || IntGroupLookup(group, ap->symbase, NULL) != 0)
9119 TransformMul(symtr, symtr, rtr);
9121 if (!SYMOP_ALIVE(ap->symop))
9123 /* Transform symop if the base atom is transformed */
9124 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
9126 if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9128 TransformMul(symtr, symtr, rtr);
9130 if (MoleculeGetSymopForTransform(mp, symtr, &new_symop, 1) != 0)
9132 ap->symop = new_symop;
9134 mp->needsMDCopyCoordinates = 1;
9135 __MoleculeUnlock(mp);
9136 sMoleculeNotifyChangeAppearance(mp);
9141 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
9145 if (mp == NULL || tr == NULL)
9148 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9149 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9151 TransformVec(&ap->r, tr, &ap->r);
9153 mp->needsMDCopyCoordinates = 1;
9154 __MoleculeUnlock(mp);
9155 sMoleculeNotifyChangeAppearance(mp);
9160 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
9163 if (mp == NULL || vp == NULL)
9165 memset(tr, 0, sizeof(tr));
9166 tr[0] = tr[4] = tr[8] = 1.0;
9170 MoleculeTransform(mp, tr, group);
9174 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
9177 TransformForRotation(tr, axis, angle, center);
9178 MoleculeTransform(mp, tr, group);
9182 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
9187 if (mp == NULL || center == NULL)
9189 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9190 return 2; /* Empty molecule */
9192 center->x = center->y = center->z = 0.0;
9193 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9194 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9196 VecScaleInc(*center, ap->r, ap->weight);
9200 return 3; /* Atomic weights are not defined? */
9202 VecScaleSelf(*center, w);
9207 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
9214 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9215 return 2; /* Empty molecule */
9216 vmin.x = vmin.y = vmin.z = 1e50;
9217 vmax.x = vmax.y = vmax.z = -1e50;
9218 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9219 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9221 if (vmin.x > ap->r.x)
9223 if (vmin.y > ap->r.y)
9225 if (vmin.z > ap->r.z)
9227 if (vmax.x < ap->r.x)
9229 if (vmax.y < ap->r.y)
9231 if (vmax.z < ap->r.z)
9241 #pragma mark ====== Measurements ======
9244 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
9247 /* if (mp->is_xtal_coord) {
9248 TransformVec(&r1, mp->cell->tr, vp1);
9249 TransformVec(&r2, mp->cell->tr, vp2);
9255 return VecLength(r1);
9259 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
9263 /* if (mp->is_xtal_coord) {
9264 TransformVec(&r1, mp->cell->tr, vp1);
9265 TransformVec(&r2, mp->cell->tr, vp2);
9266 TransformVec(&r3, mp->cell->tr, vp3);
9274 w = VecLength(r1) * VecLength(r3);
9277 return acos(VecDot(r1, r3) / w) * kRad2Deg;
9281 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
9283 Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
9285 /* if (mp->is_xtal_coord) {
9286 TransformVec(&r1, mp->cell->tr, vp1);
9287 TransformVec(&r2, mp->cell->tr, vp2);
9288 TransformVec(&r3, mp->cell->tr, vp3);
9289 TransformVec(&r4, mp->cell->tr, vp4);
9296 VecSub(r21, r1, r2);
9297 VecSub(r32, r2, r3);
9298 VecSub(r43, r3, r4);
9299 VecCross(v1, r21, r32);
9300 VecCross(v2, r32, r43);
9301 VecCross(v3, r32, v1);
9305 if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
9311 VecScaleSelf(v1, w1);
9312 VecScaleSelf(v2, w2);
9313 VecScaleSelf(v3, w3);
9314 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
9318 #pragma mark ====== XtalCell Parameters ======
9321 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
9323 if (mp->cell != NULL) {
9324 TransformVec(dst, mp->cell->tr, src);
9329 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
9331 if (mp->cell != NULL) {
9332 TransformVec(dst, mp->cell->rtr, src);
9337 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
9339 static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
9341 Vector *vp1, *vp2, *vp3;
9346 for (n1 = 0; n1 < 3; n1++) {
9347 if (cp->flags[n1] != 0)
9351 /* All directions are non-periodic */
9352 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
9353 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
9357 vp1 = &(cp->axes[n1]);
9358 vp2 = &(cp->axes[n2]);
9359 vp3 = &(cp->axes[n3]);
9360 cp->tr[n1*3] = vp1->x;
9361 cp->tr[n1*3+1] = vp1->y;
9362 cp->tr[n1*3+2] = vp1->z;
9363 cp->tr[9] = cp->origin.x;
9364 cp->tr[10] = cp->origin.y;
9365 cp->tr[11] = cp->origin.z;
9366 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
9367 /* 1-dimensional or 2-dimensional system */
9368 /* Create "dummy" axes, so that transforms between internal and cartesian coordinates are
9369 possible with a single matrix */
9370 if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
9372 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
9373 VecCross(v1, *vp1, xvec);
9374 VecCross(v2, *vp1, yvec);
9375 if (VecLength2(v1) < VecLength2(v2))
9377 VecCross(v2, *vp1, v1);
9378 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
9379 return -1; /* Non-regular transform */
9380 } else if (cp->flags[n2] == 0) {
9382 VecCross(v1, v2, *vp1);
9383 if (NormalizeVec(&v1, &v1))
9384 return -1; /* Non-regular transform */
9387 VecCross(v2, *vp1, v1);
9388 if (NormalizeVec(&v2, &v2))
9389 return -1; /* Non-regular transform */
9391 cp->tr[n2*3] = v1.x;
9392 cp->tr[n2*3+1] = v1.y;
9393 cp->tr[n2*3+2] = v1.z;
9394 cp->tr[n3*3] = v2.x;
9395 cp->tr[n3*3+1] = v2.y;
9396 cp->tr[n3*3+2] = v2.z;
9398 VecCross(v1, *vp1, *vp2);
9399 if (fabs(VecDot(v1, *vp3)) < 1e-7)
9400 return -1; /* Non-regular transform */
9401 cp->tr[n2*3] = vp2->x;
9402 cp->tr[n2*3+1] = vp2->y;
9403 cp->tr[n2*3+2] = vp2->z;
9404 cp->tr[n3*3] = vp3->x;
9405 cp->tr[n3*3+1] = vp3->y;
9406 cp->tr[n3*3+2] = vp3->z;
9409 if (TransformInvert(cp->rtr, cp->tr))
9410 return -1; /* Non-regular transform */
9412 /* Calculate the reciprocal cell parameters */
9413 cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[3] * cp->rtr[3] + cp->rtr[6] * cp->rtr[6]);
9414 cp->rcell[1] = sqrt(cp->rtr[1] * cp->rtr[1] + cp->rtr[4] * cp->rtr[4] + cp->rtr[7] * cp->rtr[7]);
9415 cp->rcell[2] = sqrt(cp->rtr[2] * cp->rtr[2] + cp->rtr[5] * cp->rtr[5] + cp->rtr[8] * cp->rtr[8]);
9416 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;
9417 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;
9418 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;
9421 /* Calculate a, b, c, alpha, beta, gamma */
9422 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[1] * cp->tr[1] + cp->tr[2] * cp->tr[2]);
9423 cp->cell[1] = sqrt(cp->tr[3] * cp->tr[3] + cp->tr[4] * cp->tr[4] + cp->tr[5] * cp->tr[5]);
9424 cp->cell[2] = sqrt(cp->tr[6] * cp->tr[6] + cp->tr[7] * cp->tr[7] + cp->tr[8] * cp->tr[8]);
9425 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;
9426 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;
9427 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;
9433 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
9442 memset(&cmat, 0, sizeof(Transform));
9443 if (mp->cell != NULL)
9444 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
9446 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
9448 if (mp->cell != NULL) {
9450 mp->needsMDRebuild = 1;
9456 cp = (XtalCell *)calloc(sizeof(XtalCell), 1);
9458 Panic("Low memory during setting cell parameters");
9460 mp->needsMDRebuild = 1;
9462 /* alpha, beta, gamma are in degree */
9466 cp->cell[3] = alpha;
9468 cp->cell[5] = gamma;
9469 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
9470 /* c unique (hexagonal etc.) */
9471 Double cosa, cosb, sinb, cosg;
9472 cosa = cos(alpha * kDeg2Rad);
9473 cosb = cos(beta * kDeg2Rad);
9474 sinb = sin(beta * kDeg2Rad);
9475 cosg = cos(gamma * kDeg2Rad);
9476 cp->axes[0].x = a * sinb;
9478 cp->axes[0].z = a * cosb;
9479 cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
9480 cp->axes[1].z = b * cosa;
9481 cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
9487 Double cosg, sing, cosa, cosb;
9488 cosa = cos(alpha * kDeg2Rad);
9489 cosb = cos(beta * kDeg2Rad);
9490 cosg = cos(gamma * kDeg2Rad);
9491 sing = sin(gamma * kDeg2Rad);
9492 cp->axes[0].x = a * sing;
9493 cp->axes[0].y = a * cosg;
9498 cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
9499 cp->axes[2].y = c * cosa;
9500 cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
9502 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
9503 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
9504 MoleculeCalculateCellFromAxes(cp, 0);
9505 TransformMul(cmat, cp->tr, cmat);
9508 /* Update the coordinates (if requested) */
9509 if (convertCoordinates) {
9510 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9511 TransformVec(&(ap->r), cmat, &(ap->r));
9515 /* Update the anisotropic parameters */
9516 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9517 Aniso *anp = ap->aniso;
9519 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
9522 __MoleculeUnlock(mp);
9523 sMoleculeNotifyChangeAppearance(mp);
9527 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x13, Double x23, const Double *sigmaptr)
9531 const Double log2 = 0.693147180559945;
9532 const Double pi22 = 19.7392088021787; /* 2*pi**2 */
9538 if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
9540 anp = mp->atoms[n1].aniso;
9543 anp = (Aniso *)calloc(sizeof(Aniso), 1);
9545 __MoleculeUnlock(mp);
9546 Panic("Low memory during setting anisotropic atom parameters");
9548 mp->atoms[n1].aniso = anp;
9551 case 1: d = 1; dx = 0.5; break;
9552 case 2: d = log2; dx = log2; break;
9553 case 3: d = log2; dx = log2 * 0.5; break;
9554 case 4: u = 1; d = 0.25; dx = 0.25; break;
9555 case 5: u = 1; d = 0.25; dx = 0.125; break;
9556 case 8: u = 1; d = pi22; dx = pi22; break;
9557 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
9558 case 10: d = pi22; dx = pi22; break;
9559 default: d = dx = 1; break;
9561 anp->bij[0] = x11 * d;
9562 anp->bij[1] = x22 * d;
9563 anp->bij[2] = x33 * d;
9564 anp->bij[3] = x12 * dx;
9565 anp->bij[4] = x13 * dx;
9566 anp->bij[5] = x23 * dx;
9567 if (sigmaptr != NULL) {
9569 anp->bsig[0] = sigmaptr[0] * d;
9570 anp->bsig[1] = sigmaptr[1] * d;
9571 anp->bsig[2] = sigmaptr[2] * d;
9572 anp->bsig[3] = sigmaptr[3] * dx;
9573 anp->bsig[4] = sigmaptr[4] * dx;
9574 anp->bsig[5] = sigmaptr[5] * dx;
9577 anp->bsig[0] = anp->bsig[1] = anp->bsig[2] = anp->bsig[3] = anp->bsig[4] = anp->bsig[5] = 0.0;
9580 if (cp != NULL && u == 1) {
9581 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
9582 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
9583 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
9584 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
9585 anp->bij[4] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[3] * kDeg2Rad); */
9586 anp->bij[5] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[4] * kDeg2Rad); */
9587 if (sigmaptr != NULL) {
9588 anp->bsig[0] *= cp->rcell[0] * cp->rcell[0];
9589 anp->bsig[1] *= cp->rcell[1] * cp->rcell[1];
9590 anp->bsig[2] *= cp->rcell[2] * cp->rcell[2];
9591 anp->bsig[3] *= cp->rcell[0] * cp->rcell[1];
9592 anp->bsig[4] *= cp->rcell[2] * cp->rcell[0];
9593 anp->bsig[5] *= cp->rcell[1] * cp->rcell[2];
9597 /* Calculate the principal axes (in Cartesian coordinates) */
9598 /* The principal axes are the eigenvectors of matrix At(B^-1)A, where
9599 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
9600 in which x and z are the crystal-space and cartesian coordinates. */
9601 m1[0] = anp->bij[0] / pi22;
9602 m1[4] = anp->bij[1] / pi22;
9603 m1[8] = anp->bij[2] / pi22;
9604 m1[1] = m1[3] = anp->bij[3] / pi22;
9605 m1[2] = m1[6] = anp->bij[4] / pi22;
9606 m1[5] = m1[7] = anp->bij[5] / pi22;
9607 MatrixInvert(m1, m1);
9609 memmove(m2, cp->rtr, sizeof(Mat33));
9610 MatrixMul(m1, m1, m2);
9611 MatrixTranspose(m2, m2);
9612 MatrixMul(m1, m2, m1);
9614 MatrixSymDiagonalize(m1, val, axis);
9615 for (u = 0; u < 3; u++) {
9617 fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
9620 val[u] = 1 / sqrt(val[u]);
9622 anp->pmat[u*3] = axis[u].x * val[u];
9623 anp->pmat[u*3+1] = axis[u].y * val[u];
9624 anp->pmat[u*3+2] = axis[u].z * val[u];
9626 __MoleculeUnlock(mp);
9629 /* Set the anisotropic parameter for atom idx according to the symop. If symop is not alive, nothing is done. */
9631 MoleculeSetAnisoBySymop(Molecule *mp, int idx)
9635 if (mp == NULL || idx < 0 || idx >= mp->natoms)
9637 ap = ATOM_AT_INDEX(mp->atoms, idx);
9638 if (!SYMOP_ALIVE(ap->symop))
9640 ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
9641 if (ap2->aniso == NULL) {
9642 if (ap->aniso != NULL) {
9648 if (ap->aniso == NULL)
9649 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
9650 if (ap->symop.sym == 0 || ap->symop.sym >= mp->nsyms) {
9651 /* Just copy the aniso parameters */
9652 memmove(ap->aniso, ap2->aniso, sizeof(Aniso));
9655 memmove(t1, SYMMETRY_AT_INDEX(mp->syms, ap->symop.sym), sizeof(Transform));
9656 t1[9] = t1[10] = t1[11] = 0.0;
9657 memset(t2, 0, sizeof(Transform));
9658 t2[0] = ap2->aniso->bij[0];
9659 t2[4] = ap2->aniso->bij[1];
9660 t2[8] = ap2->aniso->bij[2];
9661 t2[1] = t2[3] = ap2->aniso->bij[3];
9662 t2[2] = t2[6] = ap2->aniso->bij[4];
9663 t2[5] = t2[7] = ap2->aniso->bij[5];
9664 TransformMul(t2, t1, t2);
9665 TransformInvert(t1, t1);
9666 TransformMul(t2, t2, t1);
9667 MoleculeSetAniso(mp, idx, 0, t2[0], t2[4], t2[8], t2[1], t2[2], t2[5], (ap2->aniso->has_bsig ? ap2->aniso->bsig : NULL));
9671 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic, int convertCoordinates)
9673 static Vector zeroVec = {0, 0, 0};
9680 if (mp->cell != NULL)
9681 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
9683 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
9685 if (mp->cell != NULL) {
9687 mp->needsMDRebuild = 1;
9692 memset(&b, 0, sizeof(b));
9693 b.axes[0] = (ax != NULL ? *ax : zeroVec);
9694 b.axes[1] = (ay != NULL ? *ay : zeroVec);
9695 b.axes[2] = (az != NULL ? *az : zeroVec);
9697 memmove(b.flags, periodic, 3);
9698 if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
9701 if (mp->cell == NULL) {
9702 mp->needsMDRebuild = 1;
9704 if (mp->cell->has_sigma) {
9705 /* Keep the sigma */
9707 memmove(b.cellsigma, mp->cell->cellsigma, sizeof(mp->cell->cellsigma));
9709 if ((b.flags[0] != mp->cell->flags[0]) || (b.flags[1] != mp->cell->flags[1]) || (b.flags[2] != mp->cell->flags[2])) {
9710 mp->needsMDRebuild = 1;
9714 mp->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
9715 if (mp->cell != NULL) {
9716 memmove(mp->cell, &b, sizeof(XtalCell));
9717 TransformMul(cmat, b.tr, cmat);
9718 /* Update the coordinates (if requested) */
9719 if (convertCoordinates) {
9720 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9721 TransformVec(&(ap->r), cmat, &(ap->r));
9725 /* Update the anisotropic parameters */
9726 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9727 Aniso *anp = ap->aniso;
9729 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
9733 } else n = -2; /* Out of memory */
9734 __MoleculeUnlock(mp);
9735 sMoleculeNotifyChangeAppearance(mp);
9739 #pragma mark ====== Fragment manipulation ======
9742 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
9746 if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
9748 IntGroupAdd(result, idx, 1);
9749 ap = ATOM_AT_INDEX(mp->atoms, idx);
9750 cp = AtomConnectData(&ap->connect);
9751 for (i = 0; i < ap->connect.count; i++) {
9753 if (IntGroupLookup(result, idx2, NULL))
9755 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, idx2)->anchor != NULL)
9756 continue; /* bond between two pi_anchors is ignored */
9757 sMoleculeFragmentSub(mp, idx2, result, exatoms);
9759 if (ap->anchor != NULL) {
9760 cp = AtomConnectData(&ap->anchor->connect);
9761 for (i = 0; i < ap->anchor->connect.count; i++) {
9763 if (IntGroupLookup(result, idx2, NULL))
9765 sMoleculeFragmentSub(mp, idx2, result, exatoms);
9770 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
9771 not containing the atoms in exatoms */
9773 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
9776 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9778 result = IntGroupNew();
9779 sMoleculeFragmentSub(mp, n1, result, exatoms);
9783 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
9784 not containing the atoms n2, n3, ... (terminated by -1) */
9786 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
9789 IntGroup *exatoms, *result;
9790 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
9792 exatoms = IntGroupNew();
9793 for (i = 0; i < argc; i++)
9794 IntGroupAdd(exatoms, argv[i], 1);
9795 result = IntGroupNew();
9796 sMoleculeFragmentSub(mp, n1, result, exatoms);
9797 IntGroupRelease(exatoms);
9801 /* The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
9802 not containing the atoms in exatoms */
9804 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
9806 IntGroupIterator iter;
9809 if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
9811 IntGroupIteratorInit(inatoms, &iter);
9812 result = IntGroupNew();
9813 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9814 sMoleculeFragmentSub(mp, i, result, exatoms);
9816 IntGroupIteratorRelease(&iter);
9820 /* Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
9821 group is bound to the rest of the molecule via only one bond.
9822 If the result is true, then the atoms belonging to the (only) bond are returned
9823 in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
9824 and n2 can be NULL, if those informations are not needed. */
9826 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
9828 Int i, i1, i2, j, k, bond_count, nval1, nval2, *cp;
9830 if (mp == NULL || mp->natoms == 0 || group == NULL)
9831 return 0; /* Invalid arguments */
9833 for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
9834 i2 = IntGroupGetEndPoint(group, i);
9835 for (j = i1; j < i2; j++) {
9836 if (j < 0 || j >= mp->natoms)
9837 return 0; /* Invalid atom group */
9838 ap = ATOM_AT_INDEX(mp->atoms, j);
9839 cp = AtomConnectData(&ap->connect);
9840 for (k = 0; k < ap->connect.count; k++) {
9841 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, cp[k])->anchor != NULL)
9842 continue; /* Ignore bond between two pi_anchors */
9843 if (IntGroupLookup(group, cp[k], NULL) == 0) {
9848 return 0; /* Too many bonds */
9851 if (ap->anchor != NULL) {
9852 cp = AtomConnectData(&ap->anchor->connect);
9853 for (k = 0; k < ap->anchor->connect.count; k++) {
9854 if (IntGroupLookup(group, cp[k], NULL) == 0) {
9859 return 0; /* Too many bonds */
9865 if (bond_count == 1) {
9876 /* Returns non-zero if the given group is 'rotatable' in the molecule. The group
9877 is said to be 'rotatable' when either of the following conditions are met; (1)
9878 the group is detachable, or (2) the group consists of two bonded atoms that define
9879 a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
9880 (either a new IntGroup or 'group' with incremented reference count; thus the caller
9881 is responsible for releasing the returned value). */
9883 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
9886 if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
9887 if (rotGroup != NULL) {
9888 IntGroupRetain(group);
9893 if (group != NULL && IntGroupGetCount(group) == 2) {
9894 i1 = IntGroupGetNthPoint(group, 0);
9895 i2 = IntGroupGetNthPoint(group, 1);
9896 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
9897 IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
9900 i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
9902 IntGroupRelease(frag);
9903 if (rotGroup != NULL)
9907 if (rotGroup != NULL)
9909 else if (frag != NULL)
9910 IntGroupRelease(frag);
9917 #pragma mark ====== Multiple frame ======
9920 MoleculeGetNumberOfFrames(Molecule *mp)
9924 if (mp->nframes <= 0) {
9928 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9929 if (ap->nframes > n)
9940 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
9942 int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
9945 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
9948 n_old = MoleculeGetNumberOfFrames(mp);
9949 n_new = n_old + count;
9950 last_inserted = IntGroupGetNthPoint(group, count - 1);
9951 if (n_new <= last_inserted) {
9952 exframes = last_inserted - n_new + 1; /* number of extra frames that will be silently inserted */
9954 } else exframes = 0;
9956 tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4); /* "*4" for handling cells */
9962 /* Copy back the current coordinates */
9963 /* No change in the current coordinates, but the frame buffer is updated */
9964 MoleculeSelectFrame(mp, mp->cframe, 1);
9966 /* Expand ap->frames for all atoms */
9967 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9968 if (ap->frames == NULL)
9969 vp = (Vector *)calloc(sizeof(Vector), n_new);
9971 vp = (Vector *)realloc(ap->frames, sizeof(Vector) * n_new);
9973 __MoleculeUnlock(mp);
9976 for (j = ap->nframes; j < n_new; j++)
9980 if (mp->cell != NULL) {
9981 j = mp->nframe_cells;
9982 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, n_new - 1, NULL);
9983 for (i = j; i < n_new; i++) {
9984 /* Set the current cell parameters to the expanded frames */
9985 mp->frame_cells[i * 4] = mp->cell->axes[0];
9986 mp->frame_cells[i * 4 + 1] = mp->cell->axes[1];
9987 mp->frame_cells[i * 4 + 2] = mp->cell->axes[2];
9988 mp->frame_cells[i * 4 + 3] = mp->cell->origin;
9992 /* group = [n0..n1-1, n2..n3-1, ...] */
9994 /* tempv[0..n0-1] <- ap[0..n0-1], s += n0,
9995 tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
9996 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
9997 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
9999 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
10000 At last, s will become n_old and t will become count. */
10001 for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10002 int s, t, ns, ne, mult;
10005 if (i == mp->natoms) {
10006 if (mp->cell == NULL || mp->frame_cells == NULL)
10008 vp = mp->frame_cells;
10015 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10017 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
10020 ne = IntGroupGetEndPoint(group, j);
10022 if (i == mp->natoms) {
10023 if (inFrameCell != NULL) {
10024 tempv[ns * 4] = inFrameCell[t * 4];
10025 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
10026 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
10027 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
10029 tempv[ns * 4] = mp->cell->axes[0];
10030 tempv[ns * 4 + 1] = mp->cell->axes[1];
10031 tempv[ns * 4 + 2] = mp->cell->axes[2];
10032 tempv[ns * 4 + 3] = mp->cell->origin;
10035 if (inFrame != NULL)
10036 tempv[ns] = inFrame[natoms * t + i];
10045 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
10048 if (i < mp->natoms)
10049 ap->nframes = n_new;
10050 memmove(vp, tempv, sizeof(Vector) * mult * n_new);
10053 mp->nframes = n_new;
10054 MoleculeSelectFrame(mp, last_inserted, 0);
10055 MoleculeIncrementModifyCount(mp);
10056 __MoleculeUnlock(mp);
10061 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
10063 int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
10064 Vector *tempv, *vp;
10066 IntGroup *group, *group2;
10068 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
10071 /* outFrame[] should have enough size for Vector * natoms * group.count */
10072 memset(outFrame, 0, sizeof(Vector) * natoms * count);
10073 if (mp->cell != NULL && mp->frame_cells != NULL)
10074 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
10076 n_old = MoleculeGetNumberOfFrames(mp);
10078 return -2; /* Cannot delete last frame */
10080 group = IntGroupNew();
10081 group2 = IntGroupNewWithPoints(0, n_old, -1);
10082 IntGroupIntersect(inGroup, group2, group);
10083 IntGroupRelease(group2);
10084 count = IntGroupGetCount(group);
10085 n_new = n_old - count;
10087 IntGroupRelease(group);
10088 return -2; /* Trying to delete too many frames */
10090 tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4); /* "*4" for handling cells */
10091 if (tempv == NULL) {
10092 IntGroupRelease(group);
10096 __MoleculeLock(mp);
10098 /* Copy back the current coordinates */
10099 /* No change in the current coordinates, but the frame buffer is updated */
10100 MoleculeSelectFrame(mp, mp->cframe, 1);
10102 /* Determine which frame should be selected after removal is completed */
10105 if (IntGroupLookup(group, mp->cframe, &i)) {
10106 /* cframe will be removed */
10107 n1 = IntGroupGetStartPoint(group, i) - 1;
10109 n1 = IntGroupGetEndPoint(group, i);
10110 } else n1 = mp->cframe;
10111 /* Change to that frame */
10112 MoleculeSelectFrame(mp, n1, 0);
10113 group2 = IntGroupNewFromIntGroup(group);
10114 IntGroupReverse(group2, 0, n_old);
10115 new_cframe = IntGroupLookupPoint(group2, n1);
10116 if (new_cframe < 0)
10117 return -3; /* This cannot happen */
10118 IntGroupRelease(group2);
10121 /* group = [n0..n1-1, n2..n3-1, ...] */
10123 /* tempv[0..n0-1] -> ap[0..n0-1], s += n0,
10124 tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
10125 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
10126 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
10128 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
10129 At last, s will become n_new and t will become count. */
10131 for (i = 0, ap = mp->atoms; i <= mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10132 int s, t, j, ns, ne;
10134 /* if i == mp->natoms, mp->frame_cells is handled */
10135 if (i == mp->natoms) {
10136 if (mp->cell == NULL || mp->frame_cells == NULL)
10139 vp = mp->frame_cells;
10145 ap->frames = vp = (Vector *)calloc(sizeof(Vector), n_old);
10147 __MoleculeUnlock(mp);
10151 old_count = ap->nframes;
10154 /* Copy vp to tempv */
10155 memset(tempv, 0, sizeof(Vector) * mult * n_old);
10156 memmove(tempv, vp, sizeof(Vector) * mult * (old_count > n_old ? n_old : old_count));
10157 ne = ns = s = t = 0;
10158 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10162 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (ns - ne));
10165 ne = IntGroupGetEndPoint(group, j);
10169 if (i < mp->natoms)
10170 outFrame[natoms * t + i] = tempv[ns];
10171 else if (outFrameCell != NULL) {
10172 outFrameCell[t * 4] = tempv[ns * 4];
10173 outFrameCell[t * 4 + 1] = tempv[ns * 4 + 1];
10174 outFrameCell[t * 4 + 2] = tempv[ns * 4 + 2];
10175 outFrameCell[t * 4 + 3] = tempv[ns * 4 + 3];
10182 memmove(vp + s * mult, tempv + ne * mult, sizeof(Vector) * mult * (n_old - ne));
10185 if (i < mp->natoms)
10190 if (i < mp->natoms) {
10195 free(mp->frame_cells);
10196 mp->frame_cells = NULL;
10197 mp->nframe_cells = 0;
10200 if (i < mp->natoms)
10201 ap->frames = (Vector *)realloc(ap->frames, sizeof(Vector) * s);
10203 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, s - 1, NULL);
10204 mp->nframe_cells = s;
10209 mp->nframes = nframes;
10211 /* Select the "last" frame; do not "copy back" the coordinates to the frame table */
10212 /* i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
10213 MoleculeSelectFrame(mp, new_cframe, 0);
10215 IntGroupRelease(group);
10217 MoleculeIncrementModifyCount(mp);
10218 __MoleculeUnlock(mp);
10223 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
10225 int i, cframe, nframes, modified;
10227 cframe = mp->cframe;
10228 nframes = MoleculeGetNumberOfFrames(mp);
10230 frame = mp->cframe;
10231 if (mp == NULL || mp->natoms == 0 || frame < 0 || frame >= nframes)
10234 __MoleculeLock(mp);
10235 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10236 if (copyback && cframe >= 0 && cframe < ap->nframes) {
10237 /* Write the current coordinate back to the frame array */
10238 ap->frames[cframe] = ap->r;
10240 if (frame != cframe && frame >= 0 && frame < ap->nframes) {
10241 /* Read the coordinate from the frame array */
10242 ap->r = ap->frames[frame];
10247 if (mp->cell != NULL && mp->frame_cells != NULL) {
10248 /* Write the current cell back to the frame_cells array */
10249 if (copyback && cframe >= 0) {
10250 Vector *vp = (Vector *)AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, cframe, NULL);
10251 vp[0] = mp->cell->axes[0];
10252 vp[1] = mp->cell->axes[1];
10253 vp[2] = mp->cell->axes[2];
10254 vp[3] = mp->cell->origin;
10256 /* Set the cell from the frame array */
10257 if (frame != cframe && frame >= 0 && frame < mp->nframe_cells) {
10258 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);
10260 MoleculeAmendBySymmetry(mp, NULL, NULL, NULL);
10263 mp->cframe = frame;
10265 mp->needsMDCopyCoordinates = 1;
10266 __MoleculeUnlock(mp);
10267 sMoleculeNotifyChangeAppearance(mp);
10271 /* If molecule is multi-frame, then flush the current information to the frame buffer.
10272 Returns the number of frames. */
10274 MoleculeFlushFrames(Molecule *mp)
10276 int nframes = MoleculeGetNumberOfFrames(mp);
10278 MoleculeSelectFrame(mp, mp->cframe, 1);
10282 #pragma mark ====== Pi Atoms ======
10285 MoleculeCalculatePiAnchorPosition(Molecule *mol, int idx)
10289 if (mol == NULL || idx < 0 || idx >= mol->natoms)
10291 ap = ATOM_AT_INDEX(mol->atoms, idx);
10292 if (ap->anchor == NULL)
10294 ip = AtomConnectData(&ap->anchor->connect);
10295 n = ap->anchor->connect.count;
10297 for (i = 0; i < ap->anchor->connect.count; i++) {
10298 ap2 = ATOM_AT_INDEX(mol->atoms, ip[i]);
10299 VecScaleInc(ap->r, ap2->r, ap->anchor->coeffs[i]);
10304 MoleculeSetPiAnchorList(Molecule *mol, Int idx, Int nentries, Int *entries, Double *weights, Int *nUndoActions, struct MolAction ***undoActions)
10307 Int *ip, i, j, n, *np;
10309 if (mol == NULL || idx < 0 || idx >= mol->natoms || nentries <= 1)
10310 return -1; /* Invalid argument */
10311 if (weights != NULL) {
10313 for (i = 0; i < nentries; i++) {
10314 if (weights[i] <= 0.0) {
10315 return 10; /* Weights must be positive */
10320 } else d = 1.0 / nentries;
10321 ap = ATOM_AT_INDEX(mol->atoms, idx);
10322 if (ap->anchor != NULL) {
10323 /* Already an anchor: check if bonds/angles/dihedrals have entries related to this anchor */
10324 IntGroup *bg, *ag, *dg, *ig;
10325 Int *ibuf, ibufsize;
10327 bg = ag = dg = ig = NULL;
10328 ip = AtomConnectData(&ap->anchor->connect);
10329 for (i = 0; i < ap->anchor->connect.count; i++) {
10331 for (j = 0; j < nentries; j++) {
10332 if (n == entries[j])
10335 if (j == nentries) {
10336 /* This entry will disappear: if any bond/angle/dihedral has idx-n pair, that should be removed. */
10337 for (j = 0, np = mol->bonds; j < mol->nbonds; j++, np += 2) {
10338 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[0])) {
10340 bg = IntGroupNew();
10341 IntGroupAdd(bg, j, 1);
10344 for (j = 0, np = mol->angles; j < mol->nangles; j++, np += 3) {
10345 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) ||
10346 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1])) {
10348 ag = IntGroupNew();
10349 IntGroupAdd(ag, j, 1);
10352 for (j = 0, np = mol->dihedrals; j < mol->ndihedrals; j++, np += 4) {
10353 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) || (idx == np[2] && n == np[3]) ||
10354 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[3] && n == np[2])) {
10356 dg = IntGroupNew();
10357 IntGroupAdd(dg, j, 1);
10360 for (j = 0, np = mol->impropers; j < mol->nimpropers; j++, np += 4) {
10361 if ((idx == np[0] && n == np[2]) || (idx == np[1] && n == np[2]) || (idx == np[3] && n == np[2]) ||
10362 (idx == np[2] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[2] && n == np[3])) {
10364 ig = IntGroupNew();
10365 IntGroupAdd(ig, j, 1);
10373 /* Delete impropers (with undo info) */
10374 i = IntGroupGetCount(ig);
10375 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
10376 MoleculeDeleteImpropers(mol, ibuf, ig);
10377 if (nUndoActions != NULL && undoActions != NULL) {
10378 act = MolActionNew(gMolActionAddImpropers, i * 4, ibuf, ig);
10379 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10381 IntGroupRelease(ig);
10384 /* Delete dihedrals (with undo info) */
10385 i = IntGroupGetCount(dg);
10386 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
10387 MoleculeDeleteDihedrals(mol, ibuf, dg);
10388 if (nUndoActions != NULL && undoActions != NULL) {
10389 act = MolActionNew(gMolActionAddDihedrals, i * 4, ibuf, dg);
10390 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10392 IntGroupRelease(dg);
10395 /* Delete angles (with undo info) */
10396 i = IntGroupGetCount(ag);
10397 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 3 - 1, NULL);
10398 MoleculeDeleteAngles(mol, ibuf, ag);
10399 if (nUndoActions != NULL && undoActions != NULL) {
10400 act = MolActionNew(gMolActionAddAngles, i * 3, ibuf, ag);
10401 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10403 IntGroupRelease(ag);
10406 /* Delete bonds (with undo info) */
10407 i = IntGroupGetCount(bg);
10408 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 2 - 1, NULL);
10409 MoleculeDeleteBonds(mol, ibuf, bg, NULL, NULL);
10410 if (nUndoActions != NULL && undoActions != NULL) {
10411 act = MolActionNew(gMolActionAddBondsForUndo, i * 2, ibuf, bg);
10412 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
10414 IntGroupRelease(bg);
10417 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
10419 AtomConnectResize(&ap->anchor->connect, nentries);
10420 memmove(AtomConnectData(&ap->anchor->connect), entries, sizeof(Int) * nentries);
10421 AssignArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), nentries - 1, NULL);
10422 if (weights != NULL) {
10423 memmove(ap->anchor->coeffs, weights, sizeof(Double) * nentries);
10424 for (i = 0; i < nentries; i++)
10425 ap->anchor->coeffs[i] *= d; /* Normalize weight */
10427 for (i = 0; i < nentries; i++)
10428 ap->anchor->coeffs[i] = d;
10430 MoleculeCalculatePiAnchorPosition(mol, idx);
10434 #pragma mark ====== MO calculation ======
10436 /* Calculate an MO value for a single point. */
10437 /* Index is the MO number (1-based) */
10438 /* tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom. */
10440 sCalcMOPoint(const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
10444 Double val, tval, *cnp, *tmpp, *mobasep, *mop;
10446 /* Cache dr and |dr|^2 */
10447 for (i = 0; i < bset->natoms; i++) {
10448 Vector r = bset->pos[i];
10449 tmp[i * 4] = r.x = vp->x - r.x;
10450 tmp[i * 4 + 1] = r.y = vp->y - r.y;
10451 tmp[i * 4 + 2] = r.z = vp->z - r.z;
10452 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
10454 /* Iterate over all shells */
10456 mobasep = bset->mo + (index - 1) * bset->ncomps;
10457 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
10458 pp = bset->priminfos + sp->p_idx;
10459 cnp = bset->cns + sp->cn_idx;
10460 tmpp = tmp + sp->a_idx * 4;
10461 mop = mobasep + sp->m_idx;
10465 for (j = 0; j < sp->nprim; j++) {
10466 tval += *cnp++ * exp(-pp->A * tmpp[3]);
10469 val += mop[0] * tval;
10475 for (j = 0; j < sp->nprim; j++) {
10476 tval = exp(-pp->A * tmpp[3]);
10477 x += *cnp++ * tval;
10478 y += *cnp++ * tval;
10479 z += *cnp++ * tval;
10482 x *= mop[0] * tmpp[0];
10483 y *= mop[1] * tmpp[1];
10484 z *= mop[2] * tmpp[2];
10488 case kGTOType_SP: {
10491 for (j = 0; j < sp->nprim; j++) {
10492 tval = exp(-pp->A * tmpp[3]);
10493 t += *cnp++ * tval;
10494 x += *cnp++ * tval;
10495 y += *cnp++ * tval;
10496 z += *cnp++ * tval;
10500 x *= mop[1] * tmpp[0];
10501 y *= mop[2] * tmpp[1];
10502 z *= mop[3] * tmpp[2];
10503 val += t + x + y + z;
10507 Double xx, yy, zz, xy, xz, yz;
10508 xx = yy = zz = xy = xz = yz = 0;
10509 for (j = 0; j < sp->nprim; j++) {
10510 tval = exp(-pp->A * tmpp[3]);
10511 xx += *cnp++ * tval;
10512 yy += *cnp++ * tval;
10513 zz += *cnp++ * tval;
10514 xy += *cnp++ * tval;
10515 xz += *cnp++ * tval;
10516 yz += *cnp++ * tval;
10519 xx *= mop[0] * tmpp[0] * tmpp[0];
10520 yy *= mop[1] * tmpp[1] * tmpp[1];
10521 zz *= mop[2] * tmpp[2] * tmpp[2];
10522 xy *= mop[3] * tmpp[0] * tmpp[1];
10523 xz *= mop[4] * tmpp[0] * tmpp[2];
10524 yz *= mop[5] * tmpp[1] * tmpp[2];
10525 val += xx + yy + zz + xy + xz + yz;
10528 case kGTOType_D5: {
10529 Double d0, d1p, d1n, d2p, d2n;
10530 d0 = d1p = d1n = d2p = d2n = 0;
10531 for (j = 0; j < sp->nprim; j++) {
10532 tval = exp(-pp->A * tmpp[3]);
10533 d0 += *cnp++ * tval;
10534 d1p += *cnp++ * tval;
10535 d1n += *cnp++ * tval;
10536 d2p += *cnp++ * tval;
10537 d2n += *cnp++ * tval;
10540 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
10541 d1p *= mop[1] * tmpp[0] * tmpp[2];
10542 d1n *= mop[2] * tmpp[1] * tmpp[2];
10543 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
10544 d2n *= mop[4] * tmpp[0] * tmpp[1];
10545 val += d0 + d1p + d1n + d2p + d2n;
10548 /* TODO: Support F/F7 and G/G9 type orbitals */
10554 /* Calculate one MO. The input vectors should be in bohr unit (angstrom * 1.889725989 = kAngstrom2Bohr). */
10555 /* mono is the MO number (1-based) */
10557 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)
10559 int ix, iy, iz, n, nn;
10562 if (mp == NULL || mp->bset == NULL)
10564 if (mp->bset->cns == NULL) {
10565 if (sSetupGaussianCoefficients(mp->bset) != 0)
10568 cp = (Cube *)calloc(sizeof(Cube), 1);
10572 cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
10573 if (cp->dp == NULL) {
10586 /* TODO: use multithread */
10587 tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms * 4);
10594 for (ix = 0; ix < nx; ix++) {
10596 for (iy = 0; iy < ny; iy++) {
10597 for (iz = 0; iz < nz; iz++) {
10598 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
10599 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
10600 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
10601 cp->dp[n++] = sCalcMOPoint(mp->bset, mono, &p, tmp);
10603 if (callback != NULL && n - nn > 100) {
10605 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
10609 return -2; /* User interrupt */
10616 AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
10617 return mp->bset->ncubes - 1;
10621 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
10624 Vector rmin, rmax, *vp;
10625 Double dr, dx, dy, dz;
10626 if (mp == NULL || mp->bset == NULL || mp->bset->natoms == 0)
10630 rmin.x = rmin.y = rmin.z = 1e10;
10631 rmax.x = rmax.y = rmax.z = -1e10;
10632 for (i = 0, vp = mp->bset->pos; i < mp->bset->natoms; i++, vp++) {
10633 dr = RadiusForAtomicNumber(ATOM_AT_INDEX(mp->atoms, i)->atomicNumber);
10636 dr = dr * kAngstrom2Bohr * 3.0 + 2.0;
10637 if (rmin.x > vp->x - dr)
10638 rmin.x = vp->x - dr;
10639 if (rmin.y > vp->y - dr)
10640 rmin.y = vp->y - dr;
10641 if (rmin.z > vp->z - dr)
10642 rmin.z = vp->z - dr;
10643 if (rmax.x < vp->x + dr)
10644 rmax.x = vp->x + dr;
10645 if (rmax.y < vp->y + dr)
10646 rmax.y = vp->y + dr;
10647 if (rmax.z < vp->z + dr)
10648 rmax.z = vp->z + dr;
10650 dx = rmax.x - rmin.x;
10651 dy = rmax.y - rmin.y;
10652 dz = rmax.z - rmin.z;
10653 dr = pow(dx * dy * dz / npoints, 1.0/3.0);
10654 *nx = floor(dx / dr + 0.5);
10655 *ny = floor(dy / dr + 0.5);
10656 *nz = floor(dz / dr + 0.5);
10664 xp->x = yp->y = zp->z = dr;
10665 xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
10670 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
10672 if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
10674 return mp->bset->cubes[index];
10678 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
10681 if (mp == NULL || mp->bset == NULL)
10683 for (i = 0; i < mp->bset->ncubes; i++) {
10684 if (mp->bset->cubes[i]->idn == mono)
10691 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
10694 if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
10696 CubeRelease(mp->bset->cubes[index]);
10698 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
10699 if (--(mp->bset->ncubes) == 0) {
10700 free(mp->bset->cubes);
10701 mp->bset->cubes = NULL;
10703 return mp->bset->ncubes;
10707 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
10712 if (mp == NULL || mp->bset == NULL)
10713 return -1; /* Molecule or the basis set information is empty */
10714 cp = MoleculeGetCubeAtIndex(mp, index);
10716 return -2; /* MO not yet calculated */
10717 fp = fopen(fname, "wb");
10719 return -3; /* Cannot create file */
10721 /* Comment lines */
10722 fprintf(fp, "%s MO=%d\n", comment, cp->idn);
10723 fprintf(fp, " MO coefficients\n");
10725 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms), cp->origin.x, cp->origin.y, cp->origin.z);
10726 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx, cp->dx.x, cp->dx.y, cp->dx.z);
10727 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny, cp->dy.x, cp->dy.y, cp->dy.z);
10728 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz, cp->dz.x, cp->dz.y, cp->dz.z);
10730 /* Atomic information */
10731 for (i = 0; i < mp->bset->natoms; i++) {
10732 Vector *vp = mp->bset->pos + i;
10733 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
10734 /* The second number should actually be the effective charge */
10735 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber, vp->x, vp->y, vp->z);
10737 fprintf(fp, "%5d%5d\n", 1, 1);
10740 for (i = n = 0; i < cp->nx; i++) {
10741 for (j = 0; j < cp->ny; j++) {
10742 for (k = 0; k < cp->nz; k++) {
10743 fprintf(fp, " %12.5e", cp->dp[n++]);
10744 if (k == cp->nz - 1 || k % 6 == 5)