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.
26 #include "MD/MDCore.h"
27 #include "MD/MDPressure.h"
29 static Molecule *sMoleculeRoot = NULL;
30 static int sMoleculeUntitledCount = 0;
32 Int gSizeOfAtomRecord = sizeof(Atom);
34 /* These are the pasteboard data type. Since the internal representation of the
35 pasteboard data includes binary data that may be dependent on the software version,
36 the revision number is appended to these strings on startup (See MyApp::OnInit()) */
37 char *gMoleculePasteboardType = "Molecule";
38 char *gParameterPasteboardType = "Parameter";
40 #pragma mark ====== Utility function ======
43 strlen_limit(const char *s, int limit)
46 for (len = 0; *s != 0 && (limit < 0 || len < limit); s++, len++);
50 #pragma mark ====== Atom handling ======
53 s_AtomDuplicate(Atom *dst, const Atom *src, Int copy_frame)
56 dst = (Atom *)malloc(gSizeOfAtomRecord);
60 memmove(dst, src, gSizeOfAtomRecord);
61 if (src->aniso != NULL) {
62 dst->aniso = (Aniso *)malloc(sizeof(Aniso));
63 if (dst->aniso != NULL)
64 memmove(dst->aniso, src->aniso, sizeof(Aniso));
66 if (src->frames != NULL && copy_frame) {
67 dst->frames = (Vector *)malloc(sizeof(Vector) * src->nframes);
68 if (dst->frames != NULL) {
69 memmove(dst->frames, src->frames, sizeof(Vector) * src->nframes);
70 dst->nframes = src->nframes;
75 if (src->connect.count > ATOM_CONNECT_LIMIT) {
76 dst->connect.u.ptr = NULL;
77 dst->connect.count = 0;
78 NewArray(&(dst->connect.u.ptr), &(dst->connect.count), sizeof(Int), src->connect.count);
79 memmove(dst->connect.u.ptr, src->connect.u.ptr, sizeof(Int) * src->connect.count);
81 if (src->anchor != NULL) {
82 dst->anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
83 if (dst->anchor != NULL)
84 memmove(dst->anchor, src->anchor, sizeof(PiAnchor));
85 if (dst->anchor->connect.count > ATOM_CONNECT_LIMIT) {
86 dst->anchor->connect.u.ptr = NULL;
87 dst->anchor->connect.count = 0;
88 NewArray(&(dst->anchor->connect.u.ptr), &(dst->anchor->connect.count), sizeof(Int), src->anchor->connect.count);
89 memmove(dst->anchor->connect.u.ptr, src->anchor->connect.u.ptr, sizeof(Int) * src->anchor->connect.count);
91 if (dst->anchor->ncoeffs > 0) {
92 NewArray(&(dst->anchor->coeffs), &(dst->anchor->ncoeffs), sizeof(Double), src->anchor->ncoeffs);
93 memmove(dst->anchor->coeffs, src->anchor->coeffs, sizeof(Double) * src->anchor->ncoeffs);
100 AtomDuplicate(Atom *dst, const Atom *src)
102 return s_AtomDuplicate(dst, src, 1);
106 AtomDuplicateNoFrame(Atom *dst, const Atom *src)
108 return s_AtomDuplicate(dst, src, 0);
114 if (ap->aniso != NULL) {
118 if (ap->frames != NULL) {
123 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
124 ap->connect.count = 0;
125 free(ap->connect.u.ptr);
126 ap->connect.u.ptr = NULL;
131 CubeRelease(Cube *cp)
141 BasisSetRelease(BasisSet *bset)
146 if (bset->shells != NULL)
148 if (bset->priminfos != NULL)
149 free(bset->priminfos);
150 if (bset->mo != NULL)
152 if (bset->cns != NULL)
154 if (bset->moenergies != NULL)
155 free(bset->moenergies);
156 if (bset->scfdensities != NULL)
157 free(bset->scfdensities);
158 /* if (bset->pos != NULL)
160 if (bset->nuccharges != NULL)
161 free(bset->nuccharges);
162 if (bset->cubes != NULL) {
163 for (i = 0; i < bset->ncubes; i++) {
164 CubeRelease(bset->cubes[i]);
172 AtomConnectData(AtomConnect *ac)
176 return ATOM_CONNECT_PTR(ac);
180 AtomConnectResize(AtomConnect *ac, Int nconnects)
185 if (nconnects <= ATOM_CONNECT_LIMIT) {
186 if (ac->count > ATOM_CONNECT_LIMIT) {
188 memmove(ac->u.data, p, sizeof(Int) * nconnects);
192 if (ac->count <= ATOM_CONNECT_LIMIT) {
195 NewArray(&p, &(ac->count), sizeof(Int), nconnects);
196 memmove(p, ac->u.data, sizeof(Int) * ac->count);
198 } else if (ac->count < nconnects) {
200 AssignArray(&(ac->u.ptr), &(ac->count), sizeof(Int), nconnects - 1, NULL);
203 ac->count = nconnects;
207 AtomConnectInsertEntry(AtomConnect *ac, Int idx, Int connect)
215 /* Insert after the last component that is smaller than connect
216 (i.e. keep them sorted) */
217 p = ATOM_CONNECT_PTR(ac);
218 for (idx = 0; idx < ac->count; idx++) {
219 if (p[idx] >= connect)
223 AtomConnectResize(ac, ac->count + 1);
224 n = ac->count - idx - 1; /* Number of entries to be moved towards the end */
225 p = ATOM_CONNECT_PTR(ac);
227 memmove(p + idx + 1, p + idx, sizeof(Int) * n);
233 AtomConnectDeleteEntry(AtomConnect *ac, Int idx)
238 if (idx < 0 || idx >= ac->count)
240 n = ac->count - idx - 1; /* Number of entries to be moved towards the top */
241 p = ATOM_CONNECT_PTR(ac);
243 memmove(p + idx, p + idx + 1, sizeof(Int) * n);
245 AtomConnectResize(ac, ac->count - 1);
249 AtomConnectHasEntry(AtomConnect *ac, Int ent)
254 p = ATOM_CONNECT_PTR(ac);
255 for (n = 0; n < ac->count; n++) {
262 #pragma mark ====== Accessor types ======
265 MolEnumerableNew(Molecule *mol, int kind)
267 MolEnumerable *mseq = (MolEnumerable *)calloc(sizeof(MolEnumerable), 1);
269 mseq->mol = MoleculeRetain(mol);
276 MolEnumerableRelease(MolEnumerable *mseq)
279 MoleculeRelease(mseq->mol);
285 AtomRefNew(Molecule *mol, int idx)
287 AtomRef *aref = (AtomRef *)calloc(sizeof(AtomRef), 1);
289 aref->mol = MoleculeRetain(mol);
296 AtomRefRelease(AtomRef *aref)
299 MoleculeRelease(aref->mol);
304 #pragma mark ====== Creation of molecules ======
310 Molecule *mp = (Molecule *)calloc(sizeof(Molecule), 1);
312 Panic("Cannot allocate new molecule record");
313 snprintf(name, sizeof name, "Untitled %d", sMoleculeUntitledCount++);
314 ObjectInit((Object *)mp, (Object **)&sMoleculeRoot, name);
315 mp->mview = MainView_new();
321 MoleculeNewWithName(const char *name)
323 Molecule *mp = MoleculeNew();
324 MoleculeSetName(mp, name);
329 MoleculeInitWithAtoms(Molecule *mp, const Atom *atoms, int natoms)
336 if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
337 Panic("Cannot allocate memory for atoms");
338 for (i = 0; i < natoms; i++)
339 AtomDuplicate(mp->atoms + i, atoms + i);
340 mp->nframes = -1; /* Should be recalculated later */
345 MoleculeInitWithMolecule(Molecule *mp2, Molecule *mp)
348 MoleculeFlushFrames(mp);
349 MoleculeInitWithAtoms(mp2, mp->atoms, mp->natoms);
350 if (mp->nbonds > 0) {
351 if (NewArray(&mp2->bonds, &mp2->nbonds, sizeof(Int)*2, mp->nbonds) == NULL)
353 memmove(mp2->bonds, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
355 if (mp->nangles > 0) {
356 if (NewArray(&mp2->angles, &mp2->nangles, sizeof(Int)*3, mp->nangles) == NULL)
358 memmove(mp2->angles, mp->angles, sizeof(Int) * 3 * mp->nangles);
360 if (mp->ndihedrals > 0) {
361 if (NewArray(&mp2->dihedrals, &mp2->ndihedrals, sizeof(Int)*4, mp->ndihedrals) == NULL)
363 memmove(mp2->dihedrals, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
365 if (mp->nimpropers > 0) {
366 if (NewArray(&mp2->impropers, &mp2->nimpropers, sizeof(Int)*4, mp->nimpropers) == NULL)
368 memmove(mp2->impropers, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
370 if (mp->nresidues > 0) {
371 if (NewArray(&mp2->residues, &mp2->nresidues, sizeof(mp->residues[0]), mp->nresidues) == NULL)
373 memmove(mp2->residues, mp->residues, sizeof(mp->residues[0]) * mp->nresidues);
375 if (mp->cell != NULL) {
376 mp2->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
377 memmove(mp2->cell, mp->cell, sizeof(XtalCell));
380 NewArray(&(mp2->syms), &(mp2->nsyms), sizeof(Transform), mp->nsyms);
381 memmove(mp2->syms, mp->syms, sizeof(Transform) * mp2->nsyms);
384 /* mp2->useFlexibleCell = mp->useFlexibleCell; */
385 if (mp->nframe_cells > 0) {
386 if (NewArray(&mp2->frame_cells, &mp2->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells) == NULL)
388 memmove(mp2->frame_cells, mp->frame_cells, sizeof(Vector) * 4 * mp->nframe_cells);
391 if (mp->nmolprops > 0) {
392 if (NewArray(&mp2->molprops, &mp2->nmolprops, sizeof(MolProp), mp->nmolprops) == NULL)
394 n = MoleculeGetNumberOfFrames(mp);
395 for (i = 0; i < mp2->nmolprops; i++) {
396 mp2->molprops[i].propname = strdup(mp->molprops[i].propname);
397 mp2->molprops[i].propvals = (Double *)malloc(sizeof(Double) * n);
398 memcpy(mp2->molprops[i].propvals, mp->molprops[i].propvals, sizeof(Double) * n);
402 /* FIXME: should bset (basis set info) and elpot be duplicated or not? */
405 mp2->par = ParameterDuplicate(mp->par);
406 if (mp->arena != NULL) {
408 md_arena_init_from_arena(mp2->arena, mp->arena);
413 Panic("Cannot allocate memory for duplicate molecule");
414 return NULL; /* Not reached */
417 /* Assign a unique name to this parameter record */
419 MoleculeSetName(Molecule *mp, const char *name)
421 ObjectSetName((Object *)mp, name, (Object *)sMoleculeRoot);
425 MoleculeGetName(Molecule *mp)
427 return ObjectGetName((Object *)mp);
431 MoleculeWithName(const char *name)
433 return (Molecule *)ObjectWithName(name, (Object *)sMoleculeRoot);
437 MoleculeSetPath(Molecule *mol, const char *fname)
440 if (mol == NULL || fname == NULL)
442 if (fname[0] == '/' || (isalpha(fname[0]) && fname[1] == ':')) {
446 cwd = getcwd(NULL, 0);
447 asprintf(&buf, "%s/%s", cwd, fname);
450 if (mol->path != NULL) {
451 if (strcmp(mol->path, buf) == 0) {
456 free((void *)(mol->path));
459 if (mol->arena != NULL) {
460 md_close_output_files(mol->arena);
465 MoleculeGetPath(Molecule *mol)
473 MoleculeRetain(Molecule *mp)
475 ObjectIncrRefCount((Object *)mp);
476 MoleculeRetainExternalObj(mp);
481 MoleculeClear(Molecule *mp)
486 if (mp->arena != NULL) {
487 md_arena_set_molecule(mp->arena, NULL);
490 if (mp->par != NULL) {
491 ParameterRelease(mp->par);
494 if (mp->atoms != NULL) {
495 for (i = 0; i < mp->natoms; i++)
496 AtomClean(mp->atoms + i);
501 if (mp->bonds != NULL) {
506 if (mp->angles != NULL) {
511 if (mp->dihedrals != NULL) {
513 mp->dihedrals = NULL;
516 if (mp->impropers != NULL) {
518 mp->impropers = NULL;
521 if (mp->residues != NULL) {
526 if (mp->cell != NULL) {
530 if (mp->syms != NULL) {
535 if (mp->selection != NULL) {
536 IntGroupRelease(mp->selection);
537 mp->selection = NULL;
539 if (mp->frame_cells != NULL) {
540 free(mp->frame_cells);
541 mp->frame_cells = NULL;
542 mp->nframe_cells = 0;
544 if (mp->bset != NULL) {
545 BasisSetRelease(mp->bset);
548 if (mp->mcube != NULL) {
549 MoleculeDeallocateMCube(mp->mcube);
552 if (mp->molprops != NULL) {
553 for (i = 0; i < mp->nmolprops; i++) {
554 free(mp->molprops[i].propname);
555 free(mp->molprops[i].propvals);
561 if (mp->par != NULL) {
562 ParameterRelease(mp->par);
565 if (mp->elpots != NULL) {
570 if (mp->path != NULL) {
571 free((void *)mp->path);
577 MoleculeRelease(Molecule *mp)
581 MoleculeReleaseExternalObj(mp);
582 if (ObjectDecrRefCount((Object *)mp) == 0) {
584 mp->mview->mol = NULL;
585 MainView_release(mp->mview);
586 ObjectDealloc((Object *)mp, (Object **)&sMoleculeRoot);
591 MoleculeExchange(Molecule *mp1, Molecule *mp2)
594 struct MainView *mview1, *mview2;
595 struct MDArena *arena1, *arena2;
596 /* mview and arena must be kept as they are */
601 /* 'natoms' is the first member to be copied */
602 int ofs = offsetof(Molecule, natoms);
603 memmove((char *)(&mp_temp) + ofs, (char *)mp1 + ofs, sizeof(Molecule) - ofs);
604 memmove((char *)mp1 + ofs, (char *)mp2 + ofs, sizeof(Molecule) - ofs);
605 memmove((char *)mp2 + ofs, (char *)(&mp_temp) + ofs, sizeof(Molecule) - ofs);
610 /* if (mp1->arena != NULL && mp1->arena->mol == mp2)
611 mp1->arena->mol = mp1;
612 if (mp1->arena != NULL && mp1->arena->xmol == mp2)
613 mp1->arena->xmol = mp1;
614 if (mp2->arena != NULL && mp2->arena->mol == mp1)
615 mp2->arena->mol = mp2;
616 if (mp2->arena != NULL && mp2->arena->xmol == mp1)
617 mp2->arena->xmol = mp2; */
620 #pragma mark ====== Mutex ======
623 MoleculeLock(Molecule *mol)
625 if (mol == NULL || mol->mutex == NULL)
627 MoleculeCallback_lockMutex(mol->mutex);
631 MoleculeUnlock(Molecule *mol)
633 if (mol == NULL || mol->mutex == NULL)
635 MoleculeCallback_unlockMutex(mol->mutex);
638 #pragma mark ====== Modify count ======
641 MoleculeIncrementModifyCount(Molecule *mp)
644 if (++(mp->modifyCount) == 1)
645 MoleculeCallback_notifyModification(mp, 0);
646 /* fprintf(stderr, "MoleculeIncrementModifyCount: %d\n", mp->modifyCount); */
651 MoleculeClearModifyCount(Molecule *mp)
655 /* fprintf(stderr, "MoleculeClearModifyCount: %d\n", mp->modifyCount); */
659 #pragma mark ====== File handling functions ======
662 guessMoleculeType(const char *fname)
666 const char *retval = NULL;
667 fp = fopen(fname, "rb");
669 memset(buf, 0, sizeof buf);
670 if (fread(buf, 1, sizeof buf - 1, fp) > 0) {
671 if (strncmp(buf, "PSF", 3) == 0)
673 else if (((p = strstr(buf, "ATOM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r'))
674 || ((p = strstr(buf, "HETATM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r')))
677 retval = "???"; /* unknown */
685 guessElement(Atom *ap)
687 int atomicNumber = -1;
688 if (ap->atomicNumber > 0)
689 atomicNumber = ap->atomicNumber;
691 atomicNumber = GuessAtomicNumber(ap->element, ap->weight);
692 if (atomicNumber <= 0 && ap->aname[0] != 0)
693 atomicNumber = GuessAtomicNumber(ap->aname, ap->weight);
695 if (atomicNumber >= 0) {
696 ap->atomicNumber = atomicNumber;
698 ap->weight = WeightForAtomicNumber(atomicNumber);
699 if (ap->element[0] == 0)
700 ElementToString(atomicNumber, ap->element);
706 sReadLineWithInterrupt(char *buf, int size, FILE *stream, int *lineNumber)
708 static int lastLineNumber = 0;
709 if (lineNumber != NULL) {
710 if (*lineNumber == 0)
712 else if (*lineNumber >= lastLineNumber + 1000) {
713 if (MyAppCallback_checkInterrupt() != 0)
714 return -1; /* User interrupt */
715 lastLineNumber = *lineNumber;
718 return ReadLine(buf, size, stream, lineNumber);
722 s_append_asprintf(char **buf, const char *fmt, ...)
728 vasprintf(&s, fmt, va);
729 len = (*buf == NULL ? 0 : strlen(*buf));
734 *buf = malloc(len + 1);
737 *buf = realloc(*buf, len + 1);
745 MoleculeLoadFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
748 if (ftype == NULL || *ftype == 0) {
750 cp = strrchr(fname, '.');
754 cp = guessMoleculeType(fname);
755 if (strcmp(cp, "???") != 0)
759 if (strcasecmp(ftype, "psf") == 0) {
760 retval = MoleculeLoadPsfFile(mp, fname, errbuf);
761 } else if (strcasecmp(ftype, "pdb") == 0) {
762 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
763 } else if (strcasecmp(ftype, "tep") == 0) {
764 retval = MoleculeLoadTepFile(mp, fname, errbuf);
765 } else if (strcasecmp(ftype, "res") == 0 || strcasecmp(ftype, "ins") == 0) {
766 retval = MoleculeLoadShelxFile(mp, fname, errbuf);
767 } else if (strcasecmp(ftype, "fchk") == 0 || strcasecmp(ftype, "fch") == 0) {
768 retval = MoleculeLoadGaussianFchkFile(mp, fname, errbuf);
770 s_append_asprintf(errbuf, "Unknown format %s", ftype);
773 /* if (retval != 0) {
774 retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
777 MoleculeSetPath(mp, fname);
782 MoleculeLoadMbsfFile(Molecule *mp, const char *fname, char **errbuf)
786 int i, j, k, err, fn, nframes, nwarnings;
792 double mview_dbuf[10];
795 char *bufp, *valp, *comp;
800 const int kUndefined = -10000000;
804 if (mp->natoms != 0 || mp->par != NULL || mp->arena != NULL) {
805 s_append_asprintf(errbuf, "The molecule must be empty");
808 fp = fopen(fname, "rb");
810 s_append_asprintf(errbuf, "Cannot open file");
813 for (i = 0; i < 10; i++)
814 mview_dbuf[i] = kUndefined;
815 for (i = 0; i < 18; i++)
816 mview_ibuf[i] = kUndefined;
821 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
822 if (strncmp(buf, "!:", 2) != 0)
823 continue; /* Skip until section header is found */
825 strsep(&bufp, " \t\n");
826 if (strcmp(buf, "!:atoms") == 0) {
827 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
832 /* idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge */
833 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) {
834 s_append_asprintf(errbuf, "line %d: coordinates cannot be read for atom %d", lineNumber, mp->natoms + 1);
837 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
838 strncpy(ap->segName, cbuf[0], 4);
839 ap->resSeq = ibuf[1];
840 strncpy(ap->resName, cbuf[1], 4);
841 strncpy(ap->aname, cbuf[2], 4);
842 ap->type = AtomTypeEncodeToUInt(cbuf[3]);
843 ap->charge = dbuf[0];
844 ap->weight = dbuf[1];
845 strncpy(ap->element, cbuf[4], 2);
846 ap->atomicNumber = ibuf[2];
847 ap->occupancy = dbuf[2];
848 ap->tempFactor = dbuf[3];
849 ap->intCharge = ibuf[3];
852 } else if (strcmp(buf, "!:atoms_symop") == 0) {
854 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
859 /* idx symop symbase */
860 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
861 s_append_asprintf(errbuf, "line %d: symmetry operations cannot be read for atom %d", lineNumber, i + 1);
864 if (i >= mp->natoms) {
865 s_append_asprintf(errbuf, "line %d: too many atomic symmetry info\n", lineNumber);
868 ap = ATOM_AT_INDEX(mp->atoms, i);
869 ap->symop.sym = ibuf[1] / 1000000;
870 ap->symop.dx = (ibuf[1] % 1000000) / 10000;
871 ap->symop.dy = (ibuf[1] % 10000) / 100;
872 ap->symop.dz = ibuf[1] % 100;
873 ap->symbase = ibuf[2];
877 } else if (strcmp(buf, "!:atoms_fix") == 0) {
879 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
884 /* idx fix_force fix_pos */
885 if (sscanf(buf, "%d %lf %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]) < 5) {
886 s_append_asprintf(errbuf, "line %d: fix atom info cannot be read for atom %d", lineNumber, i + 1);
889 if (i >= mp->natoms) {
890 s_append_asprintf(errbuf, "line %d: too many fix atom info\n", lineNumber);
893 ap = ATOM_AT_INDEX(mp->atoms, i);
894 ap->fix_force = dbuf[0];
895 ap->fix_pos.x = dbuf[1];
896 ap->fix_pos.y = dbuf[2];
897 ap->fix_pos.z = dbuf[3];
901 } else if (strcmp(buf, "!:uff_types") == 0) {
903 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
909 if (sscanf(buf, "%d %6s", &ibuf[0], cbuf[0]) < 2) {
910 s_append_asprintf(errbuf, "line %d: uff type info cannot be read for atom %d", lineNumber, i + 1);
913 if (i >= mp->natoms) {
914 s_append_asprintf(errbuf, "line %d: too many uff type info\n", lineNumber);
917 ap = ATOM_AT_INDEX(mp->atoms, i);
918 strncpy(ap->uff_type, cbuf[0], 5);
922 } else if (strcmp(buf, "!:mm_exclude") == 0) {
924 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
929 /* idx mm_exclude periodic_exclude */
930 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
931 s_append_asprintf(errbuf, "line %d: mm_exclude flags cannot be read for atom %d", lineNumber, i + 1);
934 if (i >= mp->natoms) {
935 s_append_asprintf(errbuf, "line %d: too many mm_exclude flags\n", lineNumber);
938 ap = ATOM_AT_INDEX(mp->atoms, i);
939 ap->mm_exclude = (ibuf[1] != 0);
940 ap->periodic_exclude = (ibuf[2] != 0);
944 } else if (strcmp(buf, "!:pi_anchor") == 0) {
945 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
951 if ((j = sscanf(buf, "%d %d", &ibuf[0], &ibuf[1])) < 2) {
952 s_append_asprintf(errbuf, "line %d: bad format for pi_anchor", lineNumber);
956 ap = ATOM_AT_INDEX(mp->atoms, i);
957 if (ap->anchor != NULL) {
958 s_append_asprintf(errbuf, "line %d: warning: duplicate pi_anchor entry", lineNumber);
959 AtomConnectResize(&ap->anchor->connect, 0);
960 free(ap->anchor->coeffs);
963 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
964 if (ibuf[1] < 2 || ibuf[1] >= mp->natoms) {
965 s_append_asprintf(errbuf, "line %d: bad number of components for pi_anchor", lineNumber);
968 AtomConnectResize(&ap->anchor->connect, ibuf[1]);
969 ip = AtomConnectData(&ap->anchor->connect);
970 NewArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), ibuf[1]);
972 for (i = 0; i < j; i++) {
973 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
974 s_append_asprintf(errbuf, "line %d: unexpected end of file while reading pi_anchors", lineNumber);
977 if (sscanf(buf, "%d %lf", &ibuf[0], &dbuf[0]) < 2) {
978 s_append_asprintf(errbuf, "line %d: bad format for pi_anchor", lineNumber);
981 if (ibuf[0] < 0 || ibuf[0] >= mp->natoms) {
982 s_append_asprintf(errbuf, "line %d: atom index out of range", lineNumber);
985 if (dbuf[0] <= 0.0) {
986 s_append_asprintf(errbuf, "line %d: the pi anchor weights should be positive", lineNumber);
990 ap->anchor->coeffs[i] = dbuf[0];
994 } else if (strcmp(buf, "!:positions") == 0) {
996 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1002 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) {
1003 s_append_asprintf(errbuf, "line %d: atom position cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
1006 if (j > 4 && nframes != 0) {
1007 s_append_asprintf(errbuf, "line %d: atom position sigma can only be given for frame 0", lineNumber);
1010 if (j > 4 && j != 7) {
1011 s_append_asprintf(errbuf, "line %d: atom position sigma cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
1014 if (i >= mp->natoms) {
1015 s_append_asprintf(errbuf, "line %d: too many atom position records\n", lineNumber);
1021 ap = ATOM_AT_INDEX(mp->atoms, i);
1023 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes, &v);
1025 ap->frames[0] = ap->r;
1029 ap->sigma.x = dbuf[3];
1030 ap->sigma.y = dbuf[4];
1031 ap->sigma.z = dbuf[5];
1037 mp->nframes = nframes;
1038 mp->cframe = nframes - 1;
1040 mp->nframes = mp->cframe = 0;
1043 } else if (strcmp(buf, "!:bonds") == 0) {
1044 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1049 /* from1 to1 from2 to2 from3 to3 from4 to4 */
1050 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]);
1051 if (i < 2 || i % 2 != 0) {
1052 s_append_asprintf(errbuf, "line %d: bad bond format", lineNumber);
1055 for (j = 0; j < i; j += 2) {
1057 iibuf[1] = ibuf[j + 1];
1058 if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[0] == iibuf[1]) {
1059 s_append_asprintf(errbuf, "line %d: warning: bad bond specification (%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1]);
1061 } else if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[0])->connect), iibuf[1])) {
1062 s_append_asprintf(errbuf, "line %d: warning: bond %d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1]);
1065 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, iibuf);
1066 AtomConnectInsertEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[0])->connect), -1, iibuf[1]);
1067 AtomConnectInsertEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[1])->connect), -1, iibuf[0]);
1072 } else if (strcmp(buf, "!:bond_orders") == 0) {
1073 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1079 i = sscanf(buf, "%lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]);
1081 s_append_asprintf(errbuf, "line %d: bad bond order format", lineNumber);
1084 for (j = 0; j < i; j++) {
1085 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbondOrders, &dbuf[j]);
1088 if (mp->nbondOrders > mp->nbonds) {
1089 s_append_asprintf(errbuf, "line %d: warning: the number of bond order info (%d) exceeds number of bonds (%d) - ignoring excess info\n", lineNumber, mp->nbondOrders, mp->nbonds);
1091 mp->nbondOrders = mp->nbonds;
1092 } else if (mp->nbondOrders < mp->nbonds) {
1093 s_append_asprintf(errbuf, "line %d: warning: the number of bond order info (%d) is less than number of bonds (%d)\n", lineNumber, mp->nbondOrders, mp->nbonds);
1095 j = mp->nbondOrders;
1096 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbonds - 1, NULL);
1097 for (i = j; i < mp->nbonds; i++)
1098 mp->bondOrders[i] = 0.0;
1102 } else if (strcmp(buf, "!:angles") == 0) {
1103 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1108 /* a1 b1 c1 a2 b2 c2 a3 b3 c3 */
1109 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]);
1110 if (i == 0 || i % 3 != 0) {
1111 s_append_asprintf(errbuf, "line %d: bad angle format", lineNumber);
1114 for (j = 0; j < i; j += 3) {
1116 iibuf[1] = ibuf[j + 1];
1117 iibuf[2] = ibuf[j + 2];
1118 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]) {
1119 s_append_asprintf(errbuf, "line %d: warning: bad angle specification (%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1121 } else if (MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[2]) == 0) {
1122 s_append_asprintf(errbuf, "line %d: warning: angle with non-bonded atoms (%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1124 } else if (MoleculeLookupAngle(mp, iibuf[0], iibuf[1], iibuf[2]) >= 0) {
1125 s_append_asprintf(errbuf, "line %d: warning: angle %d-%d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1128 AssignArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, mp->nangles, iibuf);
1133 } else if (strcmp(buf, "!:dihedrals") == 0) {
1134 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1139 /* a1 b1 c1 d1 a2 b2 c2 d2 */
1140 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]);
1141 if (i == 0 || i % 4 != 0) {
1142 s_append_asprintf(errbuf, "line %d: bad dihedral format", lineNumber);
1145 for (j = 0; j < i; j += 4) {
1147 iibuf[1] = ibuf[j + 1];
1148 iibuf[2] = ibuf[j + 2];
1149 iibuf[3] = ibuf[j + 3];
1150 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]) {
1151 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]);
1153 } else if (MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[2]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[3]) == 0) {
1154 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]);
1156 } else if (MoleculeLookupDihedral(mp, iibuf[0], iibuf[1], iibuf[2], iibuf[3]) >= 0) {
1157 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]);
1160 AssignArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, mp->ndihedrals, iibuf);
1165 } else if (strcmp(buf, "!:impropers") == 0) {
1166 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1171 /* a1 b1 c1 d1 a2 b2 c2 d2 */
1172 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]);
1173 if (i == 0 || i % 4 != 0) {
1174 s_append_asprintf(errbuf, "line %d: bad improper format", lineNumber);
1177 for (j = 0; j < i; j += 4) {
1179 iibuf[1] = ibuf[j + 1];
1180 iibuf[2] = ibuf[j + 2];
1181 iibuf[3] = ibuf[j + 3];
1182 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]) {
1183 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]);
1185 } else if (MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[1]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[3]) == 0) {
1186 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]);
1188 } else if (MoleculeLookupImproper(mp, iibuf[0], iibuf[1], iibuf[2], iibuf[3]) >= 0) {
1189 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]);
1192 AssignArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, mp->nimpropers, iibuf);
1197 } else if (strcmp(buf, "!:xtalcell") == 0 && mp->cell == NULL) {
1198 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1203 /* a b c alpha beta gamma [sigmaflag] */
1204 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) {
1205 s_append_asprintf(errbuf, "line %d: bad xtalcell format", lineNumber);
1208 MoleculeSetCell(mp, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], 0);
1209 if (j == 7 && ibuf[0] != 0) {
1210 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1211 s_append_asprintf(errbuf, "line %d: sigma for xtalcell are missing", lineNumber);
1214 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1215 s_append_asprintf(errbuf,"line %d: bad xtalcell sigma format", lineNumber);
1218 if (mp->cell != NULL) {
1219 mp->cell->has_sigma = 1;
1220 for (i = 0; i < 6; i++) {
1221 mp->cell->cellsigma[i] = dbuf[i];
1224 s_append_asprintf(errbuf, "line %d: cell sigma are given while cell is not given", lineNumber);
1229 } else if (strcmp(buf, "!:symmetry_operations") == 0) {
1231 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1237 /* a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3 */
1238 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1239 s_append_asprintf(errbuf, "line %d: bad symmetry_operation format", lineNumber);
1244 tr[i + 3] = dbuf[1];
1245 tr[i + 6] = dbuf[2];
1253 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1258 } else if (strcmp(buf, "!:anisotropic_thermal_parameters") == 0) {
1260 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1265 /* b11 b22 b33 b12 b13 b23 [has_sigma] */
1266 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) {
1267 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters cannot be read for atom %d", lineNumber, i + 1);
1270 if (i >= mp->natoms) {
1271 s_append_asprintf(errbuf, "line %d: too many anisotropic thermal parameters\n", lineNumber);
1274 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) {
1277 MoleculeSetAniso(mp, i, 0, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], NULL);
1279 if (j == 7 && ibuf[0] != 0) {
1280 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1281 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma missing", lineNumber);
1284 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1285 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma cannot be read for atom %d", lineNumber, i + 1);
1288 ap = ATOM_AT_INDEX(mp->atoms, i);
1289 if (ap->aniso == NULL) {
1290 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma are given while the parameters are not given", lineNumber);
1293 ap->aniso->has_bsig = 1;
1294 for (j = 0; j < 6; j++)
1295 ap->aniso->bsig[j] = dbuf[j];
1300 } else if (strcmp(buf, "!:periodic_box") == 0) {
1304 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1309 /* 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] */
1311 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1312 s_append_asprintf(errbuf, "line %d: bad periodic_box format", lineNumber);
1321 if ((j = sscanf(buf, "%d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3])) < 3) {
1322 s_append_asprintf(errbuf, "line %d: bad periodic_box format", lineNumber);
1325 if (j == 4 && ibuf[3] != 0)
1327 cbuf[0][0] = ibuf[0];
1328 cbuf[0][1] = ibuf[1];
1329 cbuf[0][2] = ibuf[2];
1330 MoleculeSetPeriodicBox(mp, vs, vs + 1, vs + 2, vs + 3, cbuf[0], 0);
1332 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1333 s_append_asprintf(errbuf, "line %d: sigma for cell parameters are missing", lineNumber);
1336 if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1337 s_append_asprintf(errbuf, "line %d: bad periodic_box sigma format", lineNumber);
1340 if (mp->cell != NULL) {
1341 mp->cell->has_sigma = 1;
1342 for (i = 0; i < 6; i++) {
1343 mp->cell->cellsigma[i] = dbuf[i];
1346 s_append_asprintf(errbuf, "line %d: cell sigma are given while cell is not given", lineNumber);
1352 } else if (strcmp(buf, "!:frame_periodic_boxes") == 0) {
1355 /* mp->useFlexibleCell = 1; *//* The presence of this block causes asserting this flag */
1356 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1361 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1362 s_append_asprintf(errbuf, "line %d: bad frame_periodic_box format", lineNumber);
1370 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells, vs);
1374 if (mp->cframe < mp->nframe_cells) {
1375 /* mp->cframe should already have been set when positions are read */
1376 Vector *vp = &mp->frame_cells[mp->cframe * 4];
1377 static char defaultFlags[] = {1, 1, 1};
1378 char *flags = (mp->cell != NULL ? mp->cell->flags : defaultFlags);
1379 MoleculeSetPeriodicBox(mp, vp, vp + 1, vp + 2, vp + 3, flags, 0);
1382 } else if (strcmp(buf, "!:md_parameters") == 0) {
1384 if (mp->arena == NULL)
1385 mp->arena = md_arena_new(NULL);
1387 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1393 comp = strsep(&bufp, " \t");
1395 while (*bufp == ' ' || *bufp == '\t')
1397 valp = strsep(&bufp, "\n");
1399 if (strcmp(comp, "alchem_flags") == 0) {
1400 j = (valp == NULL ? 0 : atoi(valp));
1402 valp = (char *)malloc(j);
1404 while ((k = fgetc(fp)) >= 0) {
1406 if (k < '0' || k > '9') {
1407 s_append_asprintf(errbuf, "line %d: too few flags in alchem_flags block", lineNumber + 1);
1411 ReadLine(buf, sizeof buf, fp, &lineNumber);
1413 while (*bufp != 0) {
1414 if (*bufp >= '0' && *bufp <= '2') {
1416 s_append_asprintf(errbuf, "line %d: too many flags in alchem_flags block", lineNumber);
1420 valp[i++] = *bufp - '0';
1421 } else if (*bufp != ' ' && *bufp != '\t' && *bufp != '\n') {
1422 s_append_asprintf(errbuf, "line %d: strange character (0x%02x) in alchem_flags block", lineNumber, (int)*bufp);
1431 md_set_alchemical_flags(arena, j, valp);
1436 /* In the following, the redundant "!= NULL" is to suppress suprious warning */
1437 if ((strcmp(comp, "log_file") == 0 && (pp = &arena->log_result_name) != NULL)
1438 || (strcmp(comp, "coord_file") == 0 && (pp = &arena->coord_result_name) != NULL)
1439 || (strcmp(comp, "vel_file") == 0 && (pp = &arena->vel_result_name) != NULL)
1440 || (strcmp(comp, "force_file") == 0 && (pp = &arena->force_result_name) != NULL)
1441 || (strcmp(comp, "debug_file") == 0 && (pp = &arena->debug_result_name) != NULL)) {
1442 if (*valp == 0 || strstr(valp, "(null)") == valp)
1445 valp = strdup(valp);
1447 char *valp1 = strchr(valp, '\n');
1453 } else if ((strcmp(comp, "debug_output_level") == 0 && (ip = &arena->debug_output_level) != NULL)
1454 || (strcmp(comp, "coord_output_freq") == 0 && (ip = &arena->coord_output_freq) != NULL)
1455 || (strcmp(comp, "energy_output_freq") == 0 && (ip = &arena->energy_output_freq) != NULL)
1456 || (strcmp(comp, "coord_frame") == 0 && (ip = &arena->coord_result_frame) != NULL)
1457 || (strcmp(comp, "andersen_freq") == 0 && (ip = &arena->andersen_thermo_freq) != NULL)
1458 || (strcmp(comp, "random_seed") == 0 && (ip = &arena->random_seed) != NULL)
1459 || (strcmp(comp, "use_xplor_shift") == 0 && (ip = &arena->use_xplor_shift) != NULL)
1460 || (strcmp(comp, "relocate_center") == 0 && (ip = &arena->relocate_center) != NULL)
1461 || (strcmp(comp, "surface_potential_freq") == 0 && (ip = &arena->surface_potential_freq) != NULL)
1462 || (strcmp(comp, "use_graphite") == 0 && (ip = &arena->use_graphite) != NULL)) {
1463 *ip = (valp == NULL ? 0 : atoi(valp));
1464 } else if ((strcmp(comp, "timestep") == 0 && (dp = &arena->timestep) != NULL)
1465 || (strcmp(comp, "cutoff") == 0 && (dp = &arena->cutoff) != NULL)
1466 || (strcmp(comp, "electro_cutoff") == 0 && (dp = &arena->electro_cutoff) != NULL)
1467 || (strcmp(comp, "pairlist_distance") == 0 && (dp = &arena->pairlist_distance) != NULL)
1468 || (strcmp(comp, "switch_distance") == 0 && (dp = &arena->switch_distance) != NULL)
1469 || (strcmp(comp, "temperature") == 0 && (dp = &arena->temperature) != NULL)
1470 || (strcmp(comp, "andersen_coupling") == 0 && (dp = &arena->andersen_thermo_coupling) != NULL)
1471 || (strcmp(comp, "dielectric") == 0 && (dp = &arena->dielectric) != NULL)
1472 || (strcmp(comp, "gradient_convergence") == 0 && (dp = &arena->gradient_convergence) != NULL)
1473 || (strcmp(comp, "coordinate_convergence") == 0 && (dp = &arena->coordinate_convergence) != NULL)
1474 || (strcmp(comp, "scale14_vdw") == 0 && (dp = &arena->scale14_vdw) != NULL)
1475 || (strcmp(comp, "scale14_elect") == 0 && (dp = &arena->scale14_elect) != NULL)
1476 || (strcmp(comp, "surface_probe_radius") == 0 && (dp = &arena->probe_radius) != NULL)
1477 || (strcmp(comp, "surface_tension") == 0 && (dp = &arena->surface_tension) != NULL)
1478 || (strcmp(comp, "alchemical_lambda") == 0 && (dp = &arena->alchem_lambda) != NULL)
1479 || (strcmp(comp, "alchemical_delta_lambda") == 0 && (dp = &arena->alchem_dlambda) != NULL)) {
1480 *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1484 } else if (strcmp(buf, "!:pressure_control_parameters") == 0) {
1485 MDPressureArena *pressure;
1486 if (mp->arena == NULL)
1487 mp->arena = md_arena_new(mp);
1488 if (mp->arena->pressure == NULL)
1489 mp->arena->pressure = pressure_new();
1490 pressure = mp->arena->pressure;
1491 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1497 comp = strsep(&bufp, " \t");
1499 while (*bufp == ' ' || *bufp == '\t')
1501 valp = strsep(&bufp, "\n");
1503 if (strcmp(comp, "pressure") == 0) {
1504 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) {
1505 s_append_asprintf(errbuf, "line %d: bad format", lineNumber);
1508 for (i = 0; i < 9; i++)
1509 pressure->apply[i] = dbuf[i];
1510 } else if (strcmp(comp, "pressure_cell_flexibility") == 0) {
1511 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) {
1512 s_append_asprintf(errbuf, "line %d: bad format", lineNumber);
1515 for (i = 0; i < 8; i++)
1516 pressure->cell_flexibility[i] = dbuf[i];
1517 } else if ((strcmp(comp, "pressure_freq") == 0 && (ip = &pressure->freq) != NULL)) {
1518 *ip = (valp == NULL ? 0 : atoi(valp));
1519 } else if ((strcmp(comp, "pressure_coupling") == 0 && (dp = &pressure->coupling) != NULL)
1520 || (strcmp(comp, "pressure_fluctuate_cell_origin") == 0 && (dp = &pressure->fluctuate_cell_origin) != NULL)
1521 || (strcmp(comp, "pressure_fluctuate_cell_orientation") == 0 && (dp = &pressure->fluctuate_cell_orientation) != NULL)) {
1522 *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1526 } else if (strcmp(buf, "!:velocity") == 0) {
1528 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1534 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1535 s_append_asprintf(errbuf, "line %d: atom velocity cannot be read for atom %d", lineNumber, i + 1);
1538 if (i >= mp->natoms) {
1539 s_append_asprintf(errbuf, "line %d: too many atom velocity records\n", lineNumber);
1542 ap = ATOM_AT_INDEX(mp->atoms, i);
1549 } else if (strcmp(buf, "!:force") == 0) {
1551 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1557 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1558 s_append_asprintf(errbuf, "line %d: atom force cannot be read for atom %d", lineNumber, i + 1);
1561 if (i >= mp->natoms) {
1562 s_append_asprintf(errbuf, "line %d: too many atom force records\n", lineNumber);
1565 ap = ATOM_AT_INDEX(mp->atoms, i);
1572 } else if (strcmp(buf, "!:parameter") == 0 || strcmp(buf, "!:parameters") == 0) {
1573 Parameter *par = mp->par;
1575 mp->par = ParameterNew();
1580 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1585 j = ParameterReadFromString(par, buf, &bufp, fname, lineNumber, 0);
1587 s_append_asprintf(errbuf, "%s", bufp);
1594 s_append_asprintf(errbuf, "%s", bufp);
1598 } else if (strcmp(buf, "!:trackball") == 0) {
1600 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1605 if (mp->mview == NULL || mp->mview->track == NULL)
1606 continue; /* Skip (this should not happen though) */
1607 /* scale; trx try trz; theta_deg x y z */
1608 if ((i == 0 && sscanf(buf, "%lf", &dbuf[0]) < 1)
1609 || (i == 1 && sscanf(buf, "%lf %lf %lf",
1610 &dbuf[1], &dbuf[2], &dbuf[3]) < 3)
1611 || (i == 2 && sscanf(buf, "%lf %lf %lf %lf",
1612 &dbuf[4], &dbuf[5], &dbuf[6], &dbuf[7]) < 4)) {
1613 s_append_asprintf(errbuf, "line %d: bad trackball format", lineNumber);
1617 TrackballSetScale(mp->mview->track, dbuf[0]);
1619 TrackballSetTranslate(mp->mview->track, dbuf + 1);
1621 TrackballSetRotate(mp->mview->track, dbuf + 4);
1625 } else if (strcmp(buf, "!:view") == 0) {
1626 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1631 if (mp->mview == NULL)
1632 continue; /* Skip (this should not happen, though) */
1634 comp = strsep(&bufp, " \t");
1636 while (*bufp == ' ' || *bufp == '\t')
1638 valp = strsep(&bufp, "\n");
1640 if (strcmp(comp, "show_unit_cell") == 0)
1641 mp->mview->showUnitCell = atoi(valp);
1642 else if (strcmp(comp, "show_periodic_box") == 0)
1643 mp->mview->showPeriodicBox = atoi(valp);
1644 else if (strcmp(comp, "show_expanded_atoms") == 0)
1645 mp->mview->showExpandedAtoms = atoi(valp);
1646 else if (strcmp(comp, "show_ellipsoids") == 0)
1647 mp->mview->showEllipsoids = atoi(valp);
1648 else if (strcmp(comp, "show_hydrogens") == 0)
1649 mp->mview->showHydrogens = atoi(valp);
1650 else if (strcmp(comp, "show_dummy_atoms") == 0)
1651 mp->mview->showDummyAtoms = atoi(valp);
1652 else if (strcmp(comp, "show_rotation_center") == 0)
1653 mp->mview->showRotationCenter = atoi(valp);
1654 else if (strcmp(comp, "show_graphite_flag") == 0)
1655 mp->mview->showGraphiteFlag = atoi(valp);
1656 else if (strcmp(comp, "show_periodic_image_flag") == 0)
1657 mp->mview->showPeriodicImageFlag = atoi(valp);
1658 else if (strcmp(comp, "show_graphite") == 0)
1659 mp->mview->showGraphite = atoi(valp);
1660 else if (strcmp(comp, "show_expanded_atoms") == 0)
1661 mp->mview->showExpandedAtoms = atoi(valp);
1662 else if (strcmp(comp, "atom_resolution") == 0 && (i = atoi(valp)) >= 6)
1663 mp->mview->atomResolution = i;
1664 else if (strcmp(comp, "bond_resolution") == 0 && (i = atoi(valp)) >= 4)
1665 mp->mview->bondResolution = i;
1666 else if (strcmp(comp, "atom_radius") == 0)
1667 mp->mview->atomRadius = strtod(valp, NULL);
1668 else if (strcmp(comp, "bond_radius") == 0)
1669 mp->mview->bondRadius = strtod(valp, NULL);
1670 else if (strcmp(comp, "show_periodic_image") == 0) {
1671 sscanf(valp, "%d %d %d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3], &ibuf[4], &ibuf[5]);
1672 for (i = 0; i < 6; i++)
1673 mp->mview->showPeriodicImage[i] = ibuf[i];
1677 } else if (strcmp(buf, "!:property") == 0) {
1681 while (*bufp != 0 && *bufp != '\n' && bufp < (buf + sizeof buf - 3)) {
1684 dec[i + 1] = bufp[2];
1686 dec[i++] = strtol(dec, NULL, 16);
1697 i = MoleculeCreateProperty(mp, dec);
1699 s_append_asprintf(errbuf, "line %d: warning: duplicate molecular property %s - ignored\n", lineNumber, dec);
1704 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1710 s_append_asprintf(errbuf, "line %d: warning: too many molecular property %s - ignored\n", lineNumber, dec);
1714 dbuf[0] = strtod(buf, NULL);
1715 mp->molprops[i].propvals[j] = dbuf[0];
1719 } else if (strcmp(buf, "!:gaussian_primitives") == 0) {
1720 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1725 /* sym nprims a_idx */
1726 if (sscanf(buf, "%6s %d %d", cbuf[0], &ibuf[0], &ibuf[1]) < 3) {
1727 s_append_asprintf(errbuf, "line %d: the gaussian primitive info cannot be read", lineNumber);
1730 if (strcasecmp(cbuf[0], "S") == 0) {
1732 } else if (strcasecmp(cbuf[0], "P") == 0) {
1734 } else if (strcasecmp(cbuf[0], "SP") == 0) {
1736 } else if (strcasecmp(cbuf[0], "D") == 0) {
1738 } else if (strcasecmp(cbuf[0], "D5") == 0) {
1740 } else if (strcasecmp(cbuf[0], "F") == 0) {
1742 } else if (strcasecmp(cbuf[0], "F7") == 0) {
1744 } else if (strcasecmp(cbuf[0], "G") == 0) {
1746 } else if (strcasecmp(cbuf[0], "G9") == 0) {
1749 s_append_asprintf(errbuf, "line %d: the gaussian primitive type %s is unknown", lineNumber, cbuf[0]);
1753 s_append_asprintf(errbuf, "line %d: the number of primitive (%d) must be positive", lineNumber, ibuf[0]);
1756 if (ibuf[1] < 0 || ibuf[1] >= mp->natoms) {
1757 s_append_asprintf(errbuf, "line %d: the atom index (%d) is out of range", lineNumber, ibuf[1]);
1760 MoleculeAddGaussianOrbitalShell(mp, ibuf[1], ibuf[2], ibuf[0], 0);
1762 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1767 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1768 s_append_asprintf(errbuf, "line %d: cannot read gaussian primitive coefficients", lineNumber);
1771 MoleculeAddGaussianPrimitiveCoefficients(mp, dbuf[0], dbuf[1], dbuf[2]);
1779 } else if (strcmp(buf, "!:mo_info") == 0) {
1780 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1785 if (sscanf(buf, "%6s %d %d", cbuf[0], &ibuf[0], &ibuf[1]) < 3) {
1786 s_append_asprintf(errbuf, "line %d: the MO info cannot be correctly read", lineNumber);
1789 if (strcasecmp(cbuf[0], "RHF") == 0) {
1791 } else if (strcasecmp(cbuf[0], "ROHF") == 0) {
1793 } else if (strcasecmp(cbuf[0], "UHF") == 0) {
1796 s_append_asprintf(errbuf, "line %d: unknown HF type: %s", lineNumber, cbuf[0]);
1799 if (ibuf[0] < 0 || ibuf[1] < 0) {
1800 s_append_asprintf(errbuf, "line %d: incorrect number of electrons", lineNumber);
1803 MoleculeSetMOInfo(mp, ibuf[2], ibuf[0], ibuf[1]);
1806 } else if (strcmp(buf, "!:mo_coefficients") == 0) {
1807 if (mp->bset == NULL || mp->bset->nshells == 0) {
1808 s_append_asprintf(errbuf, "line %d: the :gaussian_primitive section must come before :mo_coefficients", lineNumber);
1811 /* Count the number of components */
1812 dp = (Double *)malloc(sizeof(Double) * mp->bset->ncomps);
1814 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1819 if (sscanf(buf, "MO %d %lf", &ibuf[0], &dbuf[6]) < 2) {
1820 s_append_asprintf(errbuf, "line %d: cannot read the MO index or energy", lineNumber);
1824 s_append_asprintf(errbuf, "line %d: the MO index (%d) must be in ascending order", lineNumber, ibuf[0]);
1828 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1829 j = sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]);
1831 s_append_asprintf(errbuf, "line %d: cannot read the MO coefficients", lineNumber);
1834 for (k = 0; k < j; k++, i++) {
1835 if (i >= mp->bset->ncomps) {
1836 s_append_asprintf(errbuf, "line %d: too many MO coefficients", lineNumber);
1841 if (i >= mp->bset->ncomps)
1844 i = MoleculeSetMOCoefficients(mp, ibuf[0], dbuf[6], mp->bset->ncomps, dp);
1846 s_append_asprintf(errbuf, "line %d: cannot set MO coefficients", lineNumber);
1849 i = ibuf[0] + 1; /* For next entry */
1852 } else if (strcmp(buf, "!:graphics") == 0) {
1853 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1854 MainViewGraphic *gp = NULL;
1859 if (mp->mview == NULL)
1860 continue; /* Skip */
1862 if (strcmp(buf, "line\n") == 0) {
1863 ibuf[0] = kMainViewGraphicLine;
1864 } else if (strcmp(buf, "poly\n") == 0) {
1865 ibuf[0] = kMainViewGraphicPoly;
1866 } else if (strcmp(buf, "cylinder\n") == 0) {
1867 ibuf[0] = kMainViewGraphicCylinder;
1868 } else if (strcmp(buf, "cone\n") == 0) {
1869 ibuf[0] = kMainViewGraphicCone;
1870 } else if (strcmp(buf, "ellipsoid\n") == 0) {
1871 ibuf[0] = kMainViewGraphicEllipsoid;
1873 continue; /* Skip */
1875 gp = (MainViewGraphic *)calloc(sizeof(MainViewGraphic), 1);
1878 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1884 if (sscanf(buf, "%d %d", &ibuf[0], &ibuf[1]) < 2) {
1885 s_append_asprintf(errbuf, "line %d: the closed/visible flags cannot be read for graphic object", lineNumber);
1888 gp->closed = ibuf[0];
1889 gp->visible = ibuf[1];
1890 } else if (i == 1) {
1891 if (sscanf(buf, "%lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]) < 4) {
1892 s_append_asprintf(errbuf, "line %d: the color cannot be read for graphic object", lineNumber);
1895 for (j = 0; j < 4; j++)
1896 gp->rgba[j] = dbuf[j];
1897 } else if (i == 2) {
1900 s_append_asprintf(errbuf, "line %d: the number of control points must be non-negative", lineNumber);
1904 NewArray(&gp->points, &gp->npoints, sizeof(GLfloat) * 3, j);
1905 } else if (i >= 3 && i < gp->npoints + 3) {
1906 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1907 s_append_asprintf(errbuf, "line %d: the control point cannot be read for graphic object", lineNumber);
1911 gp->points[j++] = dbuf[0];
1912 gp->points[j++] = dbuf[1];
1913 gp->points[j] = dbuf[2];
1914 } else if (i == gp->npoints + 3) {
1917 s_append_asprintf(errbuf, "line %d: the number of normals must be non-negative", lineNumber);
1921 NewArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, j);
1922 } else if (i >= gp->npoints + 4 && i < gp->npoints + gp->nnormals + 4) {
1923 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1924 s_append_asprintf(errbuf, "line %d: the normal vector cannot be read for graphic object", lineNumber);
1927 j = (i - gp->npoints - 4) * 3;
1928 gp->normals[j++] = dbuf[0];
1929 gp->normals[j++] = dbuf[1];
1930 gp->normals[j] = dbuf[2];
1934 MainView_insertGraphic(mp->mview, -1, gp);
1936 if (buf[0] == '\n' || buf[0] == 0)
1941 } else if (strncmp(buf, "!:@", 3) == 0) {
1942 /* Plug-in implemented in the ruby world */
1944 char *stringBuf, *returnString;
1946 NewArray(&stringBuf, &stringLen, sizeof(char), i + 1);
1947 strcpy(stringBuf, buf);
1949 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1950 /* The comment lines are _not_ skipped */
1954 AssignArray(&stringBuf, &stringLen, sizeof(char), i + j, NULL);
1955 strncpy(stringBuf + i, buf, j);
1958 if (MolActionCreateAndPerform(mp, SCRIPT_ACTION("si;s"),
1959 "proc { |i| loadmbsf_plugin(i) rescue \"line #{i}: #{$i.to_s}\" }",
1960 stringBuf, k, &returnString) != 0) {
1961 s_append_asprintf(errbuf, "line %d: cannot invoke Ruby plugin", lineNumber);
1963 } else if (returnString[0] != 0) {
1964 s_append_asprintf(errbuf, "%s", returnString);
1970 /* Unknown sections are silently ignored */
1973 MoleculeCleanUpResidueTable(mp);
1974 if (mp->arena != NULL)
1975 md_arena_set_molecule(mp->arena, mp);
1979 /* if (mp->mview != NULL) {
1980 if (mview_ibuf[0] != kUndefined)
1981 mp->mview->showUnitCell = mview_ibuf[0];
1982 if (mview_ibuf[1] != kUndefined)
1983 mp->mview->showPeriodicBox = mview_ibuf[1];
1984 if (mview_ibuf[2] != kUndefined)
1985 mp->mview->showExpandedAtoms = mview_ibuf[2];
1986 if (mview_ibuf[3] != kUndefined)
1987 mp->mview->showEllipsoids = mview_ibuf[3];
1988 if (mview_ibuf[4] != kUndefined)
1989 mp->mview->showHydrogens = mview_ibuf[4];
1990 if (mview_ibuf[5] != kUndefined)
1991 mp->mview->showDummyAtoms = mview_ibuf[5];
1992 if (mview_ibuf[6] != kUndefined)
1993 mp->mview->showRotationCenter = mview_ibuf[6];
1994 if (mview_ibuf[7] != kUndefined)
1995 mp->mview->showGraphiteFlag = mview_ibuf[7];
1996 if (mview_ibuf[8] != kUndefined)
1997 mp->mview->showPeriodicImageFlag = mview_ibuf[8];
1998 if (mview_ibuf[9] != kUndefined)
1999 mp->mview->showGraphite = mview_ibuf[9];
2000 if (mview_ibuf[10] != kUndefined && mview_ibuf[10] >= 6)
2001 mp->mview->atomResolution = mview_ibuf[10];
2002 if (mview_ibuf[11] != kUndefined && mview_ibuf[11] >= 4)
2003 mp->mview->bondResolution = mview_ibuf[11];
2004 for (i = 0; i < 6; i++) {
2005 if (mview_ibuf[12 + i] != kUndefined)
2006 mp->mview->showPeriodicImage[i] = mview_ibuf[12 + i];
2008 if (mview_dbuf[8] != kUndefined)
2009 mp->mview->atomRadius = mview_dbuf[8];
2010 if (mview_dbuf[9] != kUndefined)
2011 mp->mview->bondRadius = mview_dbuf[9];
2012 if (mp->mview->track != NULL) {
2013 if (mview_dbuf[0] != kUndefined)
2014 TrackballSetScale(mp->mview->track, mview_dbuf[0]);
2015 if (mview_dbuf[1] != kUndefined)
2016 TrackballSetTranslate(mp->mview->track, mview_dbuf + 1);
2017 if (mview_dbuf[4] != kUndefined)
2018 TrackballSetRotate(mp->mview->track, mview_dbuf + 4);
2027 /* The content of mp may be broken, so make it empty */
2033 MoleculeLoadPsfFile(Molecule *mp, const char *fname, char **errbuf)
2042 Vector *frames = NULL;
2048 else MoleculeClear(mp);
2049 fp = fopen(fname, "rb");
2051 s_append_asprintf(errbuf, "Cannot open file");
2054 /* flockfile(fp); */
2057 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2058 if (strncmp(buf, "PSF", 3) == 0) {
2062 for (p = buf; *p != 0 && isspace(*p); p++) {}
2068 if (strstr(buf, "!COORD") != NULL) {
2069 /* Extended psf file with coordinates */
2071 /* Allocate a temporary storage for frames */
2072 size_t size = sizeof(Vector) * mp->natoms * fn;
2074 frames = (Vector *)malloc(size);
2076 frames = (Vector *)realloc(frames, size);
2080 /* Read coordinates */
2081 for (i = 0; i < mp->natoms; i++) {
2084 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2086 s_append_asprintf(errbuf, "line %d: premature end of file while reading coordinates (frame %d)", lineNumber, fn);
2089 if (sscanf(buf, "%lg %lg %lg", dval, dval + 1, dval + 2) != 3) {
2091 s_append_asprintf(errbuf, "line %d: coordinates cannot be read for atom %d", lineNumber, i + 1);
2098 ATOM_AT_INDEX(mp->atoms, i)->r = r;
2100 frames[mp->natoms * (fn - 1) + i] = r;
2109 ReadFormat(buf, "I8", &natoms);
2112 if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
2115 for (i = 0; i < natoms; i++) {
2117 char segName[5], resName[4], atomName[5], atomType[3], element[3];
2120 memset(&w, 0, sizeof(w));
2121 ap = ATOM_AT_INDEX(mp->atoms, i);
2122 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2124 s_append_asprintf(errbuf, "line %d: premature end of file while reading atoms", lineNumber);
2127 ReadFormat(buf, "I8 x1 S4 I5 x1 S3 x2 S4 x1 S4 F16 F10",
2128 &w.serial, w.segName, &ap->resSeq, w.resName, w.atomName,
2129 w.atomType, &ap->charge, &ap->weight);
2130 strncpy(ap->segName, w.segName, 4);
2131 strncpy(ap->resName, w.resName, 3);
2132 strncpy(ap->aname, w.atomName, 4);
2133 ap->type = AtomTypeEncodeToUInt(w.atomType);
2134 /* $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl */
2135 ap->atomicNumber = GuessAtomicNumber(w.atomName, ap->weight);
2136 ElementToString(ap->atomicNumber, w.element);
2137 strncpy(ap->element, w.element, 2);
2138 /* w.element[0] = 0;
2139 for (p = w.atomName; *p != 0; p++) {
2140 if (isalpha(*p) && *p != '_') {
2141 w.element[0] = toupper(*p);
2142 if (isalpha(p[1]) && p[1] != '_') {
2143 w.element[1] = toupper(p[1]);
2151 strncpy(ap->element, w.element, 2);
2152 ap->atomicNumber = ElementToInt(w.element); */
2153 if (w.resName[0] == 0)
2154 strncpy(ap->resName, "XXX", 3);
2155 if (ap->resSeq > mp->nresidues)
2156 mp->nresidues = ap->resSeq;
2158 if (mp->residues != NULL)
2160 if (NewArray(&mp->residues, &mp->nresidues, sizeof(char (*)[4]), mp->nresidues + 1) == 0)
2162 for (i = 0; i < mp->natoms; i++) {
2163 j = mp->atoms[i].resSeq;
2164 if (mp->residues[j][0] == 0)
2165 strncpy(mp->residues[j], mp->atoms[i].resName, 4);
2168 } else if (section == 3) {
2172 ReadFormat(buf, "I8", &nbonds);
2175 if (NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, nbonds) == NULL)
2178 for (i = 0; i < nbonds; i += 4) {
2179 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2180 s_append_asprintf(errbuf, "line %d: premature end of file while reading bonds", lineNumber);
2184 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
2185 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
2186 for (j = 0; j < 4 && i + j < nbonds; j++) {
2189 b1 = ibuf[j * 2] - 1; /* Internal atom number is 0-based */
2190 b2 = ibuf[j * 2 + 1] - 1;
2191 if (b1 < 0 || b1 >= mp->natoms || b2 < 0 || b2 >= mp->natoms) {
2192 s_append_asprintf(errbuf, "line %d: The bond %d-%d includes non-existent atom", lineNumber, b1+1, b2+1);
2198 ap = ATOM_AT_INDEX(mp->atoms, b1);
2199 AtomConnectInsertEntry(&ap->connect, -1, b2);
2200 ap = ATOM_AT_INDEX(mp->atoms, b2);
2201 AtomConnectInsertEntry(&ap->connect, -1, b1);
2205 } else if (section == 4) {
2209 ReadFormat(buf, "I8", &nangles);
2212 if (NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, nangles) == NULL)
2215 for (i = 0; i < nangles; i += 3) {
2216 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2217 s_append_asprintf(errbuf, "line %d: premature end of file while reading angles", lineNumber);
2221 ReadFormat(buf, "I8I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
2222 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7, ibuf + 8);
2223 for (j = 0; j < 3 && i + j < nangles; j++) {
2225 a1 = ibuf[j * 3] - 1; /* Internal atom number is 0-based */
2226 a2 = ibuf[j * 3 + 1] - 1;
2227 a3 = ibuf[j * 3 + 2] - 1;
2228 if (a1 < 0 || a1 >= mp->natoms || a2 < 0 || a2 >= mp->natoms || a3 < 0 || a3 >= mp->natoms) {
2229 s_append_asprintf(errbuf, "line %d: The angle %d-%d-%d includes non-existent atom", lineNumber, a1+1, a2+1, a3+1);
2239 } else if (section == 5 || section == 6) {
2240 /* Dihedrals and Impropers */
2243 ReadFormat(buf, "I8", &ndihedrals);
2244 if (ndihedrals == 0)
2247 if (NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, ndihedrals) == NULL)
2251 if (NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, ndihedrals) == NULL)
2255 for (i = 0; i < ndihedrals; i += 2) {
2256 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2258 s_append_asprintf(errbuf, "line %d: premature end of file while reading %s", lineNumber, (section == 5 ? "dihedral" : "improper"));
2262 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3, ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
2263 for (j = 0; j < 2 && i + j < ndihedrals; j++) {
2265 d1 = ibuf[j * 4] - 1; /* Internal atom number is 0-based */
2266 d2 = ibuf[j * 4 + 1] - 1;
2267 d3 = ibuf[j * 4 + 2] - 1;
2268 d4 = ibuf[j * 4 + 3] - 1;
2269 if (d1 < 0 || d1 >= mp->natoms || d2 < 0 || d2 >= mp->natoms || d3 < 0 || d3 >= mp->natoms || d4 < 0 || d4 >= mp->natoms) {
2270 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);
2284 /* Create frames for each atom if necessary */
2286 for (i = 0; i < mp->natoms; i++) {
2287 ap = ATOM_AT_INDEX(mp->atoms, i);
2288 NewArray(&ap->frames, &ap->nframes, sizeof(Vector), fn);
2289 if (ap->frames == NULL)
2291 for (j = 0; j < fn; j++)
2292 ap->frames[j] = frames[mp->natoms * j + i];
2299 /* funlockfile(fp); */
2301 mp->nframes = -1; /* Should be recalculated later */
2304 else if (section == -1)
2308 Panic("low memory while reading structure file %s", fname);
2309 return 1; /* not reached */
2312 /* ("-x", "y", "-z+0.5") -> (-1,0,0,0,0,1,0,0,0,0,-1,0.5) */
2314 sMoleculeSymopStringsToTransform(char **symops, Transform tr)
2318 memset(tr, 0, sizeof(Transform));
2319 for (i = 0; i < 3; i++) {
2323 while (*symop != 0) {
2325 while (isspace(*symop))
2327 if (*symop == 0 || *symop == '\r' || *symop == 'n')
2329 if (*symop == '-') {
2332 } else if (*symop == '+') {
2336 while (isspace(*symop))
2338 if (*symop == '.' || isdigit(*symop)) {
2339 /* Numerical offset */
2340 double d = strtod(symop, &symop);
2341 if (*symop == '/') {
2342 double dd = strtod(symop + 1, &symop);
2346 return 1; /* Bad format */
2349 } else if (*symop == 'x' || *symop == 'X') {
2352 } else if (*symop == 'y' || *symop == 'Y') {
2355 } else if (*symop == 'z' || *symop == 'Z') {
2358 } else return 1; /* Bad format */
2359 } /* end while (*symop != 0) */
2365 sMoleculeGenerateSymopWithTransform(Molecule *mp, Transform gtr, int num)
2371 for (i = 0; i < num; i++) {
2372 memmove(tr, mp->syms[i], sizeof(Transform));
2373 TransformMul(tr, gtr, tr);
2374 for (j = 9; j < 12; j++) {
2377 else if (tr[j] <= 0.0)
2380 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
2387 char *p = buf + strlen(buf) - 1;
2388 if (p >= buf && (*p == '\n' || *p == '\r')) {
2390 if (--p >= buf && (*p == '\n' || *p == '\r')) {
2398 MoleculeLoadTepFile(Molecule *mp, const char *fname, char **errbuf)
2411 fp = fopen(fname, "rb");
2413 s_append_asprintf(errbuf, "Cannot open file");
2417 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2418 if (section == -1) {
2425 ReadFormat(buf, "I1F8F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5);
2427 MoleculeSetCell(mp, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], 0);
2434 if (cellType == 0) {
2435 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);
2449 char *symops[3], *brks;
2451 memset(tr, 0, sizeof(Transform));
2452 ReadFormat(buf, "I1", ibuf);
2453 symops[0] = strtok_r(buf + 1, ", ", &brks);
2454 symops[1] = strtok_r(NULL, ", ", &brks);
2455 symops[2] = strtok_r(NULL, ", ", &brks);
2456 if (sMoleculeSymopStringsToTransform(symops, tr)) {
2457 s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2461 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2467 if (section == 2) { /* Atoms */
2471 int atomIndex = mp->natoms;
2472 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2473 memset(ap, 0, gSizeOfAtomRecord);
2474 ReadFormat(buf, "S6x3x9x9F9F9F9F9", name, fbuf, fbuf+1, fbuf+2, fbuf+3);
2475 strncpy(ap->aname, name, 4);
2479 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2480 /* ap->atomicNumber = AtomNameToElement(ap->name);
2481 ElementToString(ap->atomicNumber, ap->element); */
2482 /* sAtomSetElement(ap, -1, ap->name); */
2485 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2486 s_append_asprintf(errbuf, "unexpected end of file");
2489 ReadFormat(buf, "I1F8F9F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2491 if ((atomType >= 0 && atomType <= 5) || (atomType >= 8 && atomType <= 10)) {
2492 /* Anisotropic thermal parameters */
2493 MoleculeSetAniso(mp, atomIndex, atomType, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[5], fbuf[4], NULL);
2501 MoleculeGuessBonds(mp, 0.0, &nbonds, &bonds);
2503 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2506 mp->nframes = -1; /* Should be recalculated later */
2509 Panic("low memory while reading structure file %s", fname);
2510 return -1; /* not reached */
2514 MoleculeLoadShelxFile(Molecule *mp, const char *fname, char **errbuf)
2522 int currentResSeq = 0;
2523 char currentResName[6];
2530 char (*sfacs)[4] = NULL;
2535 currentResName[0] = 0;
2536 fp = fopen(fname, "rb");
2538 s_append_asprintf(errbuf, "Cannot open file");
2542 tr[0] = tr[4] = tr[8] = 1;
2543 tr[1] = tr[2] = tr[3] = tr[5] = tr[6] = tr[7] = tr[9] = tr[10] = tr[11] = 0;
2544 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), 0, tr) == 0)
2546 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2547 if (strncmp(buf, "CELL", 4) == 0) {
2549 sscanf(buf + 4, " %f %f %f %f %f %f %f", fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2550 MoleculeSetCell(mp, fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], fbuf[6], 0);
2552 } else if (strncmp(buf, "SFAC", 4) == 0) {
2554 for (p1 = strtok_r(buf + 4, " ", &p2); p1 != NULL; p1 = strtok_r(NULL, " ", &p2)) {
2555 char *pp = (char *)AssignArray(&sfacs, &nsfacs, 4, nsfacs, NULL);
2562 } else if (strncmp(buf, "LATT", 4) == 0) {
2563 sscanf(buf + 4, " %d", &latticeType);
2565 } else if (strncmp(buf, "SYMM", 4) == 0) {
2566 char *symops[3], *brks;
2567 memset(tr, 0, sizeof(Transform));
2568 // ReadFormat(buf + 4, "I1", ibuf);
2570 symops[0] = strtok_r(buf + 4, ",", &brks);
2571 symops[1] = strtok_r(NULL, ",", &brks);
2572 symops[2] = strtok_r(NULL, ",", &brks);
2573 if (sMoleculeSymopStringsToTransform(symops, tr)) {
2574 s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2577 if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2580 } else if (strncmp(buf, "RESI", 4) == 0) {
2581 for (p1 = buf + 4; isspace(*p1); p1++);
2583 for (p2 = p1 + 1; isalnum(*p2); p2++);
2585 strncpy(currentResName, p1, 4);
2586 currentResName[4] = 0;
2588 } else currentResName[0] = 0;
2589 sscanf(buf + 4, " %d", ¤tResSeq);
2592 /* Atom name: [A-Za-z]{1,2}[0-9]* */
2593 for (p1 = buf; p1 < buf + 2 && (isalpha(*p1) && *p1 != '_'); p1++);
2595 while (isdigit(*p1))
2598 if (p1 > buf && p1 <= buf + 4 && isspace(*p1)) {
2602 int atomIndex = mp->natoms;
2603 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2604 memset(ap, 0, gSizeOfAtomRecord);
2605 strncpy(ap->aname, buf, 4);
2606 n = sscanf(p1, " %d %f %f %f %f %f %f %2s", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, cont);
2607 if (n == 8 && strcmp(cont, "=") == 0) {
2608 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2609 s_append_asprintf(errbuf, "line %d: unexpected end of file within the atom cards", lineNumber);
2612 sscanf(buf, " %f %f %f %f", fbuf+6, fbuf+7, fbuf+8, fbuf+9);
2614 } else n = 5; /* Iso */
2618 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2619 ap->occupancy = fbuf[3];
2620 if (ap->aname[0] != 'Q' && ibuf[0] >= 1 && ibuf[0] <= nsfacs) {
2621 strncpy(ap->element, sfacs[ibuf[0] - 1], 2);
2623 /* sAtomSetElement(ap, -1, sfacs[ibuf[0] - 1]); */
2624 /* strncpy(ap->element, sfacs[ibuf[0] - 1], 4);
2625 ap->atomicNumber = ElementToInt(ap->element); */
2627 sAtomSetElement(ap, -1, ap->name); */
2628 /* ap->atomicNumber = AtomNameToElement(ap->name);
2629 ElementToString(ap->atomicNumber, ap->element); */
2632 if (n == 10 || fbuf[4] >= 0.0) {
2634 /* Read in the standard deviations */
2635 ReadLine(buf, sizeof buf, fp, &lineNumber);
2636 for (i = 0; i < 9; i++) {
2640 dbuf[i] = strtod(buf + j, NULL);
2643 ap->sigma.x = dbuf[0];
2644 ap->sigma.y = dbuf[1];
2645 ap->sigma.z = dbuf[2];
2648 ap->tempFactor = fbuf[4] * 78.9568352087147; /* 8*pi*pi */
2650 MoleculeSetAniso(mp, atomIndex, 8, fbuf[4], fbuf[5], fbuf[6], fbuf[9], fbuf[7], fbuf[8], dbuf);
2651 ap->resSeq = currentResSeq;
2652 strncpy(ap->resName, currentResName, 4);
2659 /* Add symmetry operations according to the lattice type */
2660 switch (latticeType < 0 ? -latticeType : latticeType) {
2661 static Transform tr_i = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0.5};
2662 static Transform tr_c = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0};
2663 static Transform tr_a = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.5};
2664 static Transform tr_b = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5};
2665 static Transform tr_r1 = {0, 1, 0, -1, -1, 0, 0, 0, 1, 0, 0, 0};
2666 static Transform tr_r2 = {-1, -1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0};
2670 sMoleculeGenerateSymopWithTransform(mp, tr_i, 0);
2672 case 3: /* Rhombohedral obverse on hexagonal axes */
2674 sMoleculeGenerateSymopWithTransform(mp, tr_r1, n);
2675 sMoleculeGenerateSymopWithTransform(mp, tr_r2, n);
2679 sMoleculeGenerateSymopWithTransform(mp, tr_a, n);
2680 sMoleculeGenerateSymopWithTransform(mp, tr_b, n);
2681 sMoleculeGenerateSymopWithTransform(mp, tr_c, n);
2684 sMoleculeGenerateSymopWithTransform(mp, tr_a, 0);
2687 sMoleculeGenerateSymopWithTransform(mp, tr_b, 0);
2690 sMoleculeGenerateSymopWithTransform(mp, tr_c, 0);
2694 if (latticeType > 0) {
2695 static Transform tr_inv = {-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0};
2696 sMoleculeGenerateSymopWithTransform(mp, tr_inv, 0);
2699 MoleculeGuessBonds(mp, 0.0, &nbonds, &bonds);
2701 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2704 mp->nframes = -1; /* Should be recalculated later */
2707 Panic("low memory while reading structure file %s", fname);
2708 return -1; /* not reached */
2711 /* Add one gaussian orbital shell information (not undoable) */
2713 MoleculeAddGaussianOrbitalShell(Molecule *mol, Int a_idx, Int sym, Int nprims, Int add_exp)
2718 return -1; /* Molecule is empty */
2721 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2723 return -2; /* Low memory */
2725 shellp = AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), bset->nshells, NULL);
2727 return -2; /* Low memory */
2729 case 0: shellp->sym = kGTOType_S; shellp->ncomp = 1; break;
2730 case 1: shellp->sym = kGTOType_P; shellp->ncomp = 3; break;
2731 case -1: shellp->sym = kGTOType_SP; shellp->ncomp = 4; break;
2732 case 2: shellp->sym = kGTOType_D; shellp->ncomp = 6; break;
2733 case -2: shellp->sym = kGTOType_D5; shellp->ncomp = 5; break;
2734 case 3: shellp->sym = kGTOType_F; shellp->ncomp = 10; break;
2735 case -3: shellp->sym = kGTOType_F7; shellp->ncomp = 7; break;
2736 case 4: shellp->sym = kGTOType_G; shellp->ncomp = 15; break;
2737 case -4: shellp->sym = kGTOType_G9; shellp->ncomp = 9; break;
2739 return -3; /* Unsupported shell type */
2741 shellp->nprim = nprims;
2742 shellp->a_idx = a_idx;
2743 if (bset->shells < shellp) {
2744 shellp->m_idx = shellp[-1].m_idx + shellp[-1].ncomp;
2745 shellp->p_idx = shellp[-1].p_idx + shellp[-1].nprim;
2750 shellp->add_exp = add_exp;
2751 /* Update the number of components (if not yet determined) */
2752 if (bset->ncomps < shellp->m_idx + shellp->ncomp)
2753 bset->ncomps = shellp->m_idx + shellp->ncomp;
2757 /* Add a set of gaussian primitive coefficients (not undoable) */
2759 MoleculeAddGaussianPrimitiveCoefficients(Molecule *mol, Double exponent, Double contraction, Double contraction_sp)
2764 return -1; /* Molecule is empty */
2767 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2769 return -2; /* Low memory */
2771 primp = AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), bset->npriminfos, NULL);
2773 return -2; /* Low memory */
2774 primp->A = exponent;
2775 primp->C = contraction;
2776 primp->Csp = contraction_sp;
2780 /* Get the shell information from the component index */
2781 /* The outLabel must have space for at least 23 non-Null characters */
2783 MoleculeGetGaussianComponentInfo(Molecule *mol, Int comp_idx, Int *outAtomIdx, char *outLabel, Int *outShellIdx)
2788 if (mol == NULL || (bset = mol->bset) == NULL)
2789 return -1; /* No basis set info */
2790 if (comp_idx < 0 || comp_idx >= bset->ncomps)
2791 return -2; /* Component index out of range */
2792 for (si = 0, shellp = bset->shells; si < bset->nshells; si++, shellp++) {
2793 if (comp_idx >= shellp->ncomp) {
2794 comp_idx -= shellp->ncomp;
2797 static const char *type_p = "xyz";
2798 static const char *type_d = "xxyyzzxyxzyz";
2799 static const char *type_d5[] = {"xy","yz","zz", "xz", "xx-yy"};
2800 static const char *type_f = "xxxyyyzzzxxyxxzxyyyyzxzzyzzxyz";
2801 static const char *type_f7[] = {"x3-3xy2", "x2z-y2z", "x(5z2-r2)", "z(5z2-3r2)", "y(5z2-r2)", "xyz", "3x2y-y3"};
2802 static const char *type_g[] = {"x4", "y4", "z4", "x3y", "x3z", "xy3", "y3z", "xz3", "yz3", "x2y2", "x2z2", "y2z2", "x2yz", "x2yz", "xyz2"};
2803 static const char *type_g9[] = {"x4+y4-6x2y2", "xz(x2-3y2)", "(x2-y2)(7z2-r2)", "xz(7z2-3r2)", "35z4-30z2r2+3r4", "yz(7z2-3r2)", "xy(7z2-r2)", "yz(3x2-y2)", "xy(x2-y2)"};
2804 *outAtomIdx = shellp->a_idx;
2806 switch (shellp->sym) {
2808 strcpy(outLabel, "S");
2812 outLabel[1] = type_p[comp_idx];
2817 strcpy(outLabel, "S");
2820 outLabel[1] = type_p[comp_idx - 1];
2826 strncpy(outLabel + 1, type_d + comp_idx * 2, 2);
2831 strcpy(outLabel + 1, type_d5[comp_idx]);
2835 strncpy(outLabel + 1, type_f + comp_idx * 3, 3);
2840 strcpy(outLabel + 1, type_f7[comp_idx]);
2844 strcpy(outLabel + 1, type_g[comp_idx]);
2848 strcpy(outLabel + 1, type_g9[comp_idx]);
2851 return -3; /* Unsupported orbital type (internal error) */
2856 return -4; /* comp_idx out of range? (internal error) */
2859 /* Set MO coefficients for idx-th MO (1-based) */
2861 MoleculeSetMOCoefficients(Molecule *mol, Int idx, Double energy, Int ncomps, Double *coeffs)
2866 return -1; /* Molecule is empty */
2869 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2871 return -2; /* Low memory */
2873 if (bset->nmos == 0) {
2874 if (bset->nshells > 0) {
2875 /* Shell info is already set: calculate the number of MOs from there */
2876 for (i = n = 0; i < bset->nshells; i++)
2877 n += bset->shells[i].ncomp;
2879 } else if (ncomps > 0) {
2880 bset->ncomps = ncomps;
2882 if (bset->rflag == 0)
2883 bset->nmos = bset->ncomps * 2;
2885 bset->nmos = bset->ncomps;
2886 if (bset->nmos <= 0)
2887 return -3; /* Bad or inconsistent number of MOs */
2888 bset->mo = (Double *)calloc(sizeof(Double), (bset->nmos + 1) * bset->ncomps);
2889 bset->moenergies = (Double *)calloc(sizeof(Double), bset->nmos + 1);
2890 if (bset->mo == NULL || bset->moenergies == NULL) {
2891 if (bset->mo != NULL)
2893 if (bset->moenergies != NULL)
2894 free(bset->moenergies);
2896 bset->moenergies = NULL;
2898 return -2; /* Low memory */
2902 idx = -idx + bset->ncomps;
2903 if (idx < 0 || idx > bset->nmos)
2904 return -4; /* Bad MO index */
2906 idx = bset->nmos; /* Arbitrary vector */
2909 if (energy != -1000000)
2910 bset->moenergies[idx] = energy;
2911 if (ncomps < bset->ncomps)
2912 return -5; /* Insufficient number of data provided */
2913 memmove(bset->mo + (idx * bset->ncomps), coeffs, sizeof(Double) * bset->ncomps);
2914 if (bset->cns != NULL) {
2915 /* Clear the cached values */
2923 /* Get MO coefficients for idx-th MO (1-based) */
2924 /* Caution: *ncoeffs and *coeffs should be valid _before_ calling this function, i.e. */
2925 /* *ncoeffs = 0 && *coeffs = NULL or *coeffs is a valid memory pointer and *ncoeffs */
2926 /* properly designates the memory size as an array of Doubles. */
2928 MoleculeGetMOCoefficients(Molecule *mol, Int idx, Double *energy, Int *ncoeffs, Double **coeffs)
2932 return -1; /* Molecule is empty */
2934 if (bset == NULL || bset->ncomps <= 0)
2935 return -2; /* No basis set info */
2937 idx = -idx + bset->ncomps;
2938 if (idx < 0 || idx > bset->nmos)
2939 return -3; /* MO index out of range */
2941 idx = bset->nmos; /* Arbitrary vector */
2945 *energy = bset->moenergies[idx];
2946 if (ncoeffs != NULL && coeffs != NULL) {
2947 if (*ncoeffs < bset->ncomps || *coeffs == NULL) {
2948 if (*coeffs != NULL)
2949 free(*coeffs); /* Caution: possible cause of SIGBUS if *coeff is not initialized properly */
2950 *coeffs = (Double *)calloc(sizeof(Double), bset->ncomps);
2951 *ncoeffs = bset->ncomps;
2953 memmove(*coeffs, bset->mo + (idx * bset->ncomps), sizeof(Double) * bset->ncomps);
2958 /* Set Basic MO Info. rflag: 0, UHF; 1, RHF; 2, ROHF; -1, clear
2959 ne_alpha: number of alpha electrons, ne_beta: number of beta electrons */
2961 MoleculeSetMOInfo(Molecule *mol, Int rflag, Int ne_alpha, Int ne_beta)
2964 if (mol == NULL || mol->natoms == 0)
2965 return -1; /* Molecule is empty */
2967 if (mol->bset != NULL) {
2968 BasisSetRelease(mol->bset);
2975 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2977 return -2; /* Low memory */
2979 bset->natoms_bs = mol->natoms;
2980 bset->ne_alpha = ne_alpha;
2981 bset->ne_beta = ne_beta;
2982 bset->rflag = rflag;
2987 sSeparateTokens(char *inString, char **outPtr, int size)
2991 for (i = 0; i < size; i++) {
2992 p = strtok((i == 0 ? inString : NULL), " \r\n");
3003 sReadNumberArray(void *basep, Int *countp, Int size, Int num, FILE *fp, int *lnp)
3007 *((void **)basep) = NULL;
3009 if (AssignArray(basep, countp, size, num - 1, NULL) == NULL)
3010 return 4; /* Out of memory */
3012 while (ReadLine(buf, sizeof buf, fp, lnp) > 0) {
3013 char *tokens[16], *p;
3014 sSeparateTokens(buf, tokens, 16);
3015 for (i = 0; i < 16; i++) {
3016 if (tokens[i] == NULL)
3018 if (size == sizeof(Int)) {
3019 (*((Int **)basep))[n] = strtol(tokens[i], &p, 0);
3020 } else if (size == sizeof(Double)) {
3021 (*((Double **)basep))[n] = strtod(tokens[i], &p);
3022 } else return -1; /* Internal error */
3023 if (tokens[i] == p || *p != 0)
3024 return 1; /* Non-digit character */
3026 if (i < 15 && tokens[i + 1] != NULL)
3027 return 2; /* Too many data */
3028 return 0; /* All data are successfully read */
3032 return 3; /* Unexpected EOF */
3036 sSetupGaussianCoefficients(BasisSet *bset)
3043 /* Cache the contraction coefficients for efficient calculation */
3044 /* Sum up the number of components for all primitives */
3045 for (i = k = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3047 k += sp->nprim * sp->ncomp;
3049 /* Allocate memory for the cached values */
3050 if (AssignArray(&bset->cns, &bset->ncns, sizeof(Double), k - 1, NULL) == NULL)
3052 /* Iterate over all primitives */
3054 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3055 for (j = 0, pp = bset->priminfos + sp->p_idx; j < sp->nprim; j++, pp++) {
3058 // (8 alpha^3/pi^3)^0.25 exp(-alpha r^2)
3059 *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
3062 // (128 alpha^5/pi^3)^0.25 [x|y|z]exp(-alpha r^2)
3063 d = pp->C * pow(pp->A, 1.25) * 1.425410941;
3069 *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
3070 d = pp->Csp * pow(pp->A, 1.25) * 1.425410941;
3076 // xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2)
3077 // xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
3078 d = pp->C * pow(pp->A, 1.75);
3079 dp[0] = dp[1] = dp[2] = d * 1.645922781;
3080 dp[3] = dp[4] = dp[5] = d * 2.850821881;
3084 // 3zz-rr: (128 alpha^7/9pi^3)^0.25 (3zz-rr)exp(-alpha r^2)
3085 // xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
3086 // xx-yy: (128 alpha^7/pi^3)^0.25 (xx-yy)exp(-alpha r^2)
3087 d = pp->C * pow(pp->A, 1.75);
3088 dp[0] = d * 0.822961390;
3089 dp[1] = dp[2] = dp[4] = d * 2.850821881;
3090 dp[3] = d * 1.425410941;
3093 /* TODO: Support F/F7 and G/G9 type orbitals */
3101 MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char **errbuf)
3106 int natoms, nbasis, i, j, k, n, mxbond, retval, ncomps, nprims, nelec;
3120 bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
3124 fp = fopen(fname, "rb");
3126 s_append_asprintf(errbuf, "Cannot open file");
3130 natoms = nbasis = -1;
3138 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3141 if (lineNumber == 2) {
3144 bset->rflag = 0; /* UHF */
3145 else if (buf[11] == 'O')
3146 bset->rflag = 2; /* ROHF */
3147 else bset->rflag = 1; /* RHF */
3150 while (p > buf && *p == ' ')
3153 sSeparateTokens(buf + 42, tokens, 16);
3154 if (strcmp(buf, "Number of atoms") == 0) {
3155 if (tokens[1] == NULL || (natoms = atoi(tokens[1])) <= 0) {
3156 s_append_asprintf(errbuf, "Line %d: strange number of atoms: %s", lineNumber, tokens[1]);
3160 bset->natoms_bs = natoms;
3161 /* Allocate atom records (all are empty for now) */
3162 AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms - 1, NULL);
3163 /* Also allocate atom position array for MO calculations */
3164 /* AssignArray(&bset->pos, &bset->natoms, sizeof(Vector), natoms - 1, NULL); */
3165 /* Also allocate nuclear charge array */
3166 bset->nuccharges = (Double *)calloc(sizeof(Double), natoms);
3167 } else if (strcmp(buf, "Number of electrons") == 0) {
3168 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
3169 s_append_asprintf(errbuf, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
3174 } else if (strcmp(buf, "Number of alpha electrons") == 0) {
3175 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
3176 s_append_asprintf(errbuf, "Line %d: strange number of alpha electrons: %s", lineNumber, tokens[1]);
3181 } else if (strcmp(buf, "Number of beta electrons") == 0) {
3182 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
3183 s_append_asprintf(errbuf, "Line %d: strange number of beta electrons: %s", lineNumber, tokens[1]);
3188 if (bset->ne_alpha + bset->ne_beta != nelec) {
3189 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);
3193 } else if (strcmp(buf, "Number of basis functions") == 0) {
3194 if (tokens[1] == NULL || (nbasis = atoi(tokens[1])) <= 0) {
3195 s_append_asprintf(errbuf, "Line %d: strange number of basis functions: %s", lineNumber, tokens[1]);
3199 } else if (strcmp(buf, "Atomic numbers") == 0) {
3200 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
3201 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
3205 if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms, fp, &lineNumber) != 0) {
3206 s_append_asprintf(errbuf, "Line %d: cannot read atomic numbers", lineNumber);
3210 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
3211 ap->atomicNumber = iary[i];
3212 bset->nuccharges[i] = iary[i];
3213 ElementToString(ap->atomicNumber, ap->element);
3214 memmove(ap->aname, ap->element, 4);
3215 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
3220 } else if (strcmp(buf, "Nuclear charges") == 0) {
3221 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
3222 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
3226 if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms, fp, &lineNumber) != 0) {
3227 s_append_asprintf(errbuf, "Line %d: cannot read nuclear charges", lineNumber);
3231 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
3232 bset->nuccharges[i] = dary[i];
3236 } else if (strcmp(buf, "Current cartesian coordinates") == 0) {
3237 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * 3) {
3238 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of cartesian coordinates: %s", lineNumber, tokens[2]);
3242 if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms * 3, fp, &lineNumber) != 0) {
3243 s_append_asprintf(errbuf, "Line %d: cannot read cartesian coordinates", lineNumber);
3247 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
3248 ap->r.x = dary[i * 3] * kBohr2Angstrom;
3249 ap->r.y = dary[i * 3 + 1] * kBohr2Angstrom;
3250 ap->r.z = dary[i * 3 + 2] * kBohr2Angstrom;
3254 } else if (strcmp(buf, "MxBond") == 0) {
3255 if (tokens[1] == NULL || (mxbond = atoi(tokens[1])) <= 0) {
3256 s_append_asprintf(errbuf, "Line %d: strange number of bonds per atom: %s", lineNumber, tokens[1]);
3260 } else if (strcmp(buf, "IBond") == 0) {
3262 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * mxbond) {
3263 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of bonds: %s", lineNumber, tokens[2]);
3267 if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms * mxbond, fp, &lineNumber) != 0) {
3268 s_append_asprintf(errbuf, "Line %d: cannot read bond information", lineNumber);
3272 bonds = (Int *)malloc(sizeof(Int) * (mxbond * 2 + 1));
3273 for (i = 0; i < natoms; i++) {
3274 for (j = k = 0; j < mxbond; j++) {
3275 n = iary[i * mxbond + j] - 1;
3277 /* Connect atom i and atom n */
3283 bonds[k] = kInvalidIndex;
3284 MoleculeAddBonds(mp, k / 2, bonds, NULL, 1);
3290 } else if (strcmp(buf, "Shell types") == 0) {
3291 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0) {
3292 s_append_asprintf(errbuf, "Line %d: wrong number of shell types: %s", lineNumber, tokens[2]);
3296 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
3297 s_append_asprintf(errbuf, "Line %d: cannot read shell types", lineNumber);
3301 /* Allocate ShellInfo table and store shell type information */
3302 AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), nary - 1, NULL);
3303 for (i = n = 0, sp = bset->shells; i < nary; i++, sp++) {
3305 case 0: sp->sym = kGTOType_S; sp->ncomp = 1; break;
3306 case 1: sp->sym = kGTOType_P; sp->ncomp = 3; break;
3307 case -1: sp->sym = kGTOType_SP; sp->ncomp = 4; break;
3308 case 2: sp->sym = kGTOType_D; sp->ncomp = 6; break;
3309 case -2: sp->sym = kGTOType_D5; sp->ncomp = 5; break;
3310 case 3: sp->sym = kGTOType_F; sp->ncomp = 10; break;
3311 case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break;
3312 case 4: sp->sym = kGTOType_G; sp->ncomp = 15; break;
3313 case -4: sp->sym = kGTOType_G9; sp->ncomp = 9; break;
3315 s_append_asprintf(errbuf, "Line %d: unsupported shell type %d", lineNumber, iary[i]);
3322 bset->ncomps = ncomps = n;
3325 } else if (strcmp(buf, "Number of primitives per shell") == 0) {
3326 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
3327 s_append_asprintf(errbuf, "Line %d: wrong size of the primitive table: %s", lineNumber, tokens[2]);
3331 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
3332 s_append_asprintf(errbuf, "Line %d: cannot read primitive table", lineNumber);
3336 for (i = n = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3337 sp->nprim = iary[i];
3344 } else if (strcmp(buf, "Shell to atom map") == 0) {
3345 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
3346 s_append_asprintf(errbuf, "Line %d: wrong size of the shell-to-atom map: %s", lineNumber, tokens[2]);
3350 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
3351 s_append_asprintf(errbuf, "Line %d: cannot read shell-to-atom table", lineNumber);
3355 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3356 sp->a_idx = iary[i] - 1;
3360 } else if (strcmp(buf, "Primitive exponents") == 0) {
3361 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nprims) {
3362 s_append_asprintf(errbuf, "Line %d: wrong number of primitive exponents: %s", lineNumber, tokens[2]);
3366 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3367 s_append_asprintf(errbuf, "Line %d: cannot read primitive exponents", lineNumber);
3371 /* Allocate PrimInfo table */
3372 AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), nprims - 1, NULL);
3373 for (i = 0, pp = bset->priminfos; i < nprims; i++, pp++) {
3378 } else if (strcmp(buf, "Contraction coefficients") == 0) {
3379 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
3380 s_append_asprintf(errbuf, "Line %d: wrong number of contraction coefficients: %s", lineNumber, tokens[2]);
3384 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3385 s_append_asprintf(errbuf, "Line %d: cannot read contraction coefficients", lineNumber);
3389 for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
3394 } else if (strcmp(buf, "P(S=P) Contraction coefficients") == 0) {
3395 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
3396 s_append_asprintf(errbuf, "Line %d: wrong number of P(S=P) contraction coefficients: %s", lineNumber, tokens[2]);
3400 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3401 s_append_asprintf(errbuf, "Line %d: cannot read P(S=P) contraction coefficients", lineNumber);
3405 for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
3410 } else if (strcmp(buf, "Alpha Orbital Energies") == 0) {
3411 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
3412 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha orbitals: %s", lineNumber, tokens[2]);
3416 if (sReadNumberArray(&bset->moenergies, &bset->nmos, sizeof(Double), i, fp, &lineNumber) != 0) {
3417 s_append_asprintf(errbuf, "Line %d: cannot read alpha orbital energies", lineNumber);
3421 } else if (strcmp(buf, "Alpha MO coefficients") == 0) {
3422 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
3423 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha MO coefficients: %s", lineNumber, tokens[2]);
3427 if (sReadNumberArray(&bset->mo, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3428 s_append_asprintf(errbuf, "Line %d: cannot read MO coefficients", lineNumber);
3432 } else if (strcmp(buf, "Beta Orbital Energies") == 0) {
3433 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
3434 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta orbitals: %s", lineNumber, tokens[2]);
3438 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3439 s_append_asprintf(errbuf, "Line %d: cannot read beta orbital energies", lineNumber);
3443 bset->moenergies = (Double *)realloc(bset->moenergies, sizeof(Double) * 2 * ncomps);
3444 bset->nmos = ncomps * 2;
3445 bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);
3446 memmove(bset->moenergies + ncomps, dary, sizeof(Double) * ncomps);
3447 memset(bset->mo + ncomps * ncomps, 0, sizeof(Double) * ncomps * ncomps);
3450 } else if (strcmp(buf, "Beta MO coefficients") == 0) {
3451 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
3452 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta MO coefficients: %s", lineNumber, tokens[2]);
3456 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3457 s_append_asprintf(errbuf, "Line %d: cannot read alpha MO coefficients", lineNumber);
3461 bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps); /* Should be unnecessary, just in case */
3462 memmove(bset->mo + ncomps * ncomps, dary, sizeof(Double) * ncomps * ncomps);
3465 } else if (strcmp(buf, "Total SCF Density") == 0) {
3466 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * (ncomps + 1) / 2) {
3467 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of SCF densities: %s", lineNumber, tokens[2]);
3471 if (sReadNumberArray(&bset->scfdensities, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3472 s_append_asprintf(errbuf, "Line %d: cannot read SCF densities", lineNumber);
3478 if (mp->natoms == 0) {
3479 s_append_asprintf(errbuf, "Atom information is missing");
3483 if (bset->shells == NULL || bset->priminfos == NULL) {
3484 s_append_asprintf(errbuf, "Gaussian primitive information is missing");
3488 if (bset->mo == NULL) {
3489 s_append_asprintf(errbuf, "MO coefficients were not found");
3493 if (sSetupGaussianCoefficients(bset) != 0) {
3494 s_append_asprintf(errbuf, "Internal error during setup MO calculation");
3507 if (mp->bset != NULL) {
3508 BasisSetRelease(mp->bset);
3514 Panic("low memory while reading fchk file %s", fname);
3515 return -1; /* not reached */
3519 MoleculeLoadGamessDatFile(Molecule *mol, const char *fname, char **errbuf)
3524 int lineNumber, i, j, k, len, natoms = 0;
3531 Vector *vbuf = NULL;
3533 int optimizing = 0, status = 0;
3537 mol = MoleculeNew();
3539 if (mol->natoms == 0)
3542 fp = fopen(fname, "rb");
3544 s_append_asprintf(errbuf, "Cannot open file");
3548 /* ESP is cleared (not undoable!) */
3549 if (mol->elpots != NULL) {
3556 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3559 if (strncmp(buf, " $DATA", 6) == 0) {
3560 /* Initial geometry */
3562 vbuf = (Vector *)calloc(sizeof(Vector), mol->natoms);
3565 ReadLine(buf, sizeof buf, fp, &lineNumber); /* Title */
3566 ReadLine(buf, sizeof buf, fp, &lineNumber); /* Symmetry */
3567 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3568 if (strncmp(buf, " $END", 5) == 0)
3570 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3571 s_append_asprintf(errbuf, "Line %d: bad format in $DATA section", lineNumber);
3577 memset(&a, 0, sizeof(a));
3578 strncpy(a.aname, sval, 4);
3582 a.atomicNumber = (Int)dval[0];
3583 strncpy(a.element, ElementToString(a.atomicNumber, sval), 3);
3584 a.type = AtomTypeEncodeToUInt(a.element);
3585 a.weight = WeightForAtomicNumber(a.atomicNumber);
3586 MoleculeCreateAnAtom(mol, &a, mol->natoms);
3589 if (i >= mol->natoms) {
3590 s_append_asprintf(errbuf, "Line %d: too many atoms", lineNumber);
3594 if ((ap = ATOM_AT_INDEX(mol->atoms, i))->atomicNumber != dval[0]) {
3595 s_append_asprintf(errbuf, "Line %d: atomic number does not match", lineNumber);
3599 vbuf[i].x = dval[1];
3600 vbuf[i].y = dval[2];
3601 vbuf[i].z = dval[3];
3603 /* Skip until a blank line is found */
3604 /* 2013.6.11. Line including "PM3" is also recognized as the end of atom */
3605 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3606 for (j = 0; buf[j] == ' '; j++);
3607 if (buf[j] == '\n' || strncmp(buf + j, "PM3", 3) == 0)
3614 /* Set atom positions */
3616 if (natoms < mol->natoms) {
3617 s_append_asprintf(errbuf, "Line %d: too few atoms", lineNumber);
3621 ig = IntGroupNewWithPoints(0, natoms, -1);
3622 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, natoms, vbuf);
3623 IntGroupRelease(ig);
3626 vbuf = (Vector *)calloc(sizeof(Vector), natoms);
3627 nframes = MoleculeGetNumberOfFrames(mol);
3631 } else if (strstr(buf, "DATA FROM NSERCH") != NULL || (strstr(buf, "RESULTS FROM SUCCESSFUL") != NULL && (n1 = 1))) {
3632 /* Skip until the separator line is read (three or four lines) */
3636 s_append_asprintf(errbuf, "Line %d: the separator line at the top of the coordinates is not found: bad format?", lineNumber);
3640 ReadLine(buf, sizeof buf, fp, &lineNumber);
3641 } while (strstr(buf, "----------------------------") == NULL);
3642 for (i = 0; i < natoms; i++) {
3643 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3644 s_append_asprintf(errbuf, "Unexpected end of file in reading NSERCH data");
3648 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3649 s_append_asprintf(errbuf, "Line %d: bad format in NSERCH coordinate data", lineNumber);
3653 vbuf[i].x = dval[1];
3654 vbuf[i].y = dval[2];
3655 vbuf[i].z = dval[3];
3657 ig = IntGroupNewWithPoints(nframes, 1, -1);
3658 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, natoms, vbuf, 0, NULL);
3659 IntGroupRelease(ig);
3662 optimizing = 1; /* Flag to skip reading the VEC group */
3666 } else if (strstr(buf, "E(UHF)") != NULL || (strstr(buf, "E(RHF)") != NULL && (n1 = 1)) || (strstr(buf, "E(ROHF)") != NULL && (n1 = 2))) {
3667 if (mol->bset == NULL) {
3668 i = MoleculeSetMOInfo(mol, n1, 0, 0);
3670 s_append_asprintf(errbuf, "Line %d: cannot allocate basis set internal buffer", lineNumber);
3675 } else if (strncmp(buf, " $VEC", 5) == 0) {
3677 /* Read the vec group */
3678 if (mol->bset == NULL || mol->bset->ncomps == 0)
3679 continue; /* Just ignore */
3681 continue; /* Ignore VEC group during optimization */
3682 coeffs = (Double *)calloc(sizeof(Double), mol->bset->ncomps);
3683 if (coeffs == NULL) {
3684 s_append_asprintf(errbuf, "Line %d: low memory during $VEC", lineNumber);
3689 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3691 if (strncmp(buf, " $END", 5) == 0)
3693 while ((j = 5 + (k % 5) * 15) <= len && buf[j] != 0 && buf[j] != '\n') {
3694 strncpy(sval, buf + j, 15);
3696 coeffs[k] = strtod(sval, NULL);
3701 if (k < mol->bset->ncomps)
3703 j = MoleculeSetMOCoefficients(mol, i + 1, -1000000, k, coeffs);
3705 s_append_asprintf(errbuf, "Line %d: cannot set coefficients for MO %d", lineNumber, i + 1);
3716 } else if ((strstr(buf, "ELECTRIC POTENTIAL") != NULL || strstr(buf, "ELECTROSTATIC POTENTIAL") != NULL) && strstr(buf, "ELPOTT") != NULL) {
3718 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3720 if (strstr(buf, "TOTAL NUMBER OF GRID POINTS") != NULL)
3722 if (sscanf(buf, "%d %lf %lf %lf %lf", &ival[0], &dval[0], &dval[1], &dval[2], &dval[3]) < 5)
3724 ep = AssignArray(&mol->elpots, &mol->nelpots, sizeof(Elpot), i, NULL);
3725 ep->pos.x = dval[0];
3726 ep->pos.y = dval[1];
3727 ep->pos.z = dval[2];
3732 goto redo; /* This section has no end line, so the last line should be processed again */
3733 else break; /* End of file encountered or interrupted */
3734 } /* TODO: read MOLPLT info if present */
3737 s_append_asprintf(errbuf, "User interrupt at line %d", lineNumber);
3743 if (mol->natoms > 0)
3744 retval = 0; /* Return the partially constructed molecule */
3745 if (newmol && mol->nbonds == 0) {
3748 MoleculeGuessBonds(mol, 0.0, &nbonds, &bonds);
3750 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
3758 MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
3761 if (ftype == NULL || *ftype == 0) {
3763 cp = strrchr(fname, '.');
3767 cp = guessMoleculeType(fname);
3768 if (strcmp(cp, "???") != 0)
3772 if (strcasecmp(ftype, "pdb") == 0) {
3773 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3776 /* Try all formats once again */
3777 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3783 MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char **errbuf)
3789 int i, j, new_unit, retval;
3797 fp = fopen(fname, "rb");
3799 s_append_asprintf(errbuf, "Cannot open file");
3802 /* flockfile(fp); */
3803 if (mp->natoms == 0)
3806 /* Allocate buffer for undo-capable modification */
3807 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
3808 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3809 /* Retain current position if the atom info is missing in the input file */
3815 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3816 if (strncmp(buf, "END", 3) == 0)
3818 if (strncmp(buf, "HETATM", 6) == 0 || strncmp(buf, "ATOM", 4) == 0) {
3820 Int serial, intCharge, resSeq;
3823 char segName[5], resName[4], atomName[5], resSeqStr[5], atomType[3], element[3], occStr[6];
3825 memset(&w, 0, sizeof(w));
3826 ReadFormat(buf, "x6 I5 x1 S4 x1 S3 x1 x1 S4 x1 x3 F8 F8 F8 S6 F6 x6 S4 S2 I2",
3827 &w.serial, w.atomName, w.resName, w.resSeqStr, &w.r.x, &w.r.y, &w.r.z,
3828 w.occStr, &w.temp, w.segName, w.element, &w.intCharge);
3829 if (w.atomName[0] == 0) {
3830 continue; /* Atom name is empty */
3832 /* A workaround for residue number >= 10000 (XPLOR style) */
3833 if (w.resSeqStr[0] >= 'A' && w.resSeqStr[0] <= 'Z') {
3834 w.resSeq = (w.resSeqStr[0] - 'A' + 10) * 1000 + atoi(w.resSeqStr + 1);
3836 w.resSeq = atoi(w.resSeqStr);
3838 if (w.element[0] == 0) {
3839 /* $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl */
3840 for (p = w.atomName; *p != 0; p++) {
3841 if (isalpha(*p) && *p != '_') {
3842 w.element[0] = toupper(*p);
3843 if (isalpha(p[1]) && p[1] != '_') {
3844 w.element[1] = toupper(p[1]);
3853 if (w.occStr[0] == 0)
3856 w.occ = atof(w.occStr);
3857 if (w.serial <= 0) {
3858 s_append_asprintf(errbuf, "line %d: non-positive atom number %d", lineNumber, w.serial);
3862 w.serial--; /* The internal atom number is 0-based */
3863 if (w.serial >= mp->natoms) {
3865 /* Create a new atom entry */
3866 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, w.serial, NULL);
3868 s_append_asprintf(errbuf, "line %d: the atom number %d does not exist in the structure file", lineNumber, w.serial+1);
3874 ap = ATOM_AT_INDEX(mp->atoms, w.serial);
3876 ap->occupancy = w.occ;
3877 ap->tempFactor = w.temp;
3878 if (w.segName[0] == 0)
3879 strncpy(w.segName, "MAIN", 4);
3880 strncpy(ap->segName, w.segName, 4);
3881 ap->resSeq = w.resSeq;
3882 strncpy(ap->resName, w.resName, 4);
3883 strncpy(ap->aname, w.atomName, 4);
3884 strncpy(ap->element, w.element, 2);
3886 ap->atomicNumber = ElementToInt(ap->element);
3887 ap->type = AtomTypeEncodeToUInt(ap->element);
3888 ap->weight = WeightForAtomicNumber(ap->atomicNumber);
3889 ap->intCharge = w.intCharge;
3890 if (ap->resSeq > 0) {
3891 if (ap->resSeq < mp->nresidues) {
3892 /* Update the resName according to residues[] */
3893 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
3895 /* Register the resName to residues[] */
3896 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, w.resName);
3900 strcpy(ap->resName, "XXX");
3901 if (mp->nresidues == 0)
3902 AssignArray(&mp->residues, &mp->nresidues, 4, 0, ap->resName);
3904 i = ElementToInt(ap->element);
3906 ap->weight = gElementParameters[i].weight;
3908 /* Not a new unit: only the atom position is updated */
3912 } else if (strncmp(buf, "CONECT", 6) == 0 && new_unit) {
3913 i = ReadFormat(buf, "x6 I5I5I5I5I5I5I5I5I5I5I5I5",
3914 ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
3915 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7,
3916 ibuf + 8, ibuf + 9, ibuf + 10, ibuf + 11);
3920 for (j = 0; j < i; j++) {
3921 if (ibuf[j] < 0 || ibuf[j] > mp->natoms) {
3922 s_append_asprintf(errbuf, "line %d: The CONECT record contains non-existent atom %d", lineNumber, ibuf[j]);
3925 } else if (ibuf[j] == 0)
3931 for (j = 1, bi = 0; j < i; j++) {
3932 if (ibuf[0] < ibuf[j]) {
3933 if (MoleculeLookupBond(mp, ibuf[0], ibuf[j]) >= 0) {
3934 s_append_asprintf(errbuf, "line %d: warning: duplicate bond %d-%d\n", lineNumber, ibuf[0], ibuf[j]);
3936 bbuf[bi * 2] = ibuf[0] - 1;
3937 bbuf[bi * 2 + 1] = ibuf[j] - 1;
3945 retval = MoleculeAddBonds(mp, bi, bbuf, NULL, 1);
3947 s_append_asprintf(errbuf, "line %d: bad bond specification", lineNumber);
3954 /* funlockfile(fp); */
3957 /* Renumber atoms if some atom number is unoccupied */
3958 int *old2new, oldidx, newidx;
3959 old2new = (int *)calloc(sizeof(int), mp->natoms);
3960 if (old2new == NULL) {
3961 s_append_asprintf(errbuf, "Out of memory");
3965 for (oldidx = newidx = 0; oldidx < mp->natoms; oldidx++) {
3966 ap = ATOM_AT_INDEX(mp->atoms, oldidx);
3967 if (ap->aname[0] != 0) {
3968 old2new[oldidx] = newidx;
3969 if (oldidx > newidx)
3970 memmove(ATOM_AT_INDEX(mp->atoms, newidx), ap, gSizeOfAtomRecord);
3974 mp->natoms = newidx;
3975 if (oldidx > newidx) {
3976 /* Renumber the connects and bonds */
3978 for (i = 0; i < mp->natoms; i++) {
3979 ap = ATOM_AT_INDEX(mp->atoms, i);
3980 cp = AtomConnectData(&ap->connect);
3981 for (j = 0; j < ap->connect.count; j++) {
3982 cp[j] = old2new[cp[j]];
3985 for (i = 0; i < mp->nbonds * 2; i++) {
3986 mp->bonds[i] = old2new[mp->bonds[i]];
3989 retval = MoleculeRebuildTablesFromConnects(mp);
3991 /* This error may not happen */
3992 s_append_asprintf(errbuf, "Cannot build angle/dihedral/improper tables");
3996 /* Undo action: delete all atoms */
3999 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
4000 act = MolActionNew(gMolActionUnmergeMolecule, ig);
4001 act->frame = mp->cframe;
4002 MolActionCallback_registerUndo(mp, act);
4003 MolActionRelease(act);
4004 IntGroupRelease(ig);
4007 /* Set the new atom positions */
4008 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
4009 MolActionCreateAndPerform(mp, gMolActionSetAtomPositions, ig, mp->natoms, vp);
4010 IntGroupRelease(ig);
4014 mp->nframes = -1; /* Should be recalculated later */
4016 return 1; /* No atoms */
4020 /* funlockfile(fp); */
4026 return 1; /* Maybe different format? */
4031 MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char **errbuf)
4034 SFloat32 *xp, *yp, *zp;
4037 int n, errcount = 0;
4039 if (mp == NULL || mp->natoms == 0) {
4040 s_append_asprintf(errbuf, "Molecule is empty");
4043 n = DcdOpen(fname, &dcd);
4046 case -2: s_append_asprintf(errbuf, "Cannot open file"); break;
4047 case 1: s_append_asprintf(errbuf, "Premature EOF encountered"); break;
4048 case 2: s_append_asprintf(errbuf, "Bad block length of the first section"); break;
4049 case 3: s_append_asprintf(errbuf, "\"CORD\" signature is missing"); break;
4050 case 4: s_append_asprintf(errbuf, "Bad termination of the first section"); break;
4051 case 5: s_append_asprintf(errbuf, "The title section is not correct"); break;
4052 case 6: s_append_asprintf(errbuf, "The atom number section is not correct"); break;
4053 default: s_append_asprintf(errbuf, "Read error in dcd file"); break;
4057 if (dcd.natoms == 0) {
4058 s_append_asprintf(errbuf, "No atoms were found in the dcd file");
4060 } else if (dcd.nframes == 0) {
4061 s_append_asprintf(errbuf, "No frames were found in the dcd file");
4071 vp = (Vector *)calloc(sizeof(Vector), mp->natoms * dcd.nframes);
4073 cp = (Vector *)calloc(sizeof(Vector), dcd.nframes * 4);
4075 xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4076 yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4077 zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4078 ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mp), dcd.nframes, -1);
4079 if (vp == NULL || xp == NULL || yp == NULL || zp == NULL || ig == NULL) {
4080 s_append_asprintf(errbuf, "Cannot allocate memory");
4086 if (ig) IntGroupRelease(ig);
4089 for (n = 0; n < dcd.nframes; n++) {
4092 SFloat32 dcdcell[6];
4093 if (DcdReadFrame(&dcd, n, xp, yp, zp, dcdcell)) {
4094 s_append_asprintf(errbuf, "Read error in dcd file");
4097 for (i = 0, vpp = &vp[n * mp->natoms]; i < dcd.natoms && i < mp->natoms; i++, vpp++) {
4105 /* dcdcell = {a, gamma, b, beta, alpha, c} */
4106 /* angles are described either in cosines (Charmm and NAMD > 2.5) or degrees (NAMD 2.5) */
4107 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) {
4108 dcdcell[4] = cos(dcdcell[4] * kDeg2Rad); /* cos(alpha) */
4109 dcdcell[3] = cos(dcdcell[3] * kDeg2Rad); /* cos(beta) */
4110 dcdcell[1] = cos(dcdcell[1] * kDeg2Rad); /* cos(gamma) */
4112 /* a axis lies along the cartesian x axis */
4113 sing = sqrt(1 - dcdcell[1] * dcdcell[1]);
4114 vpp[0].x = dcdcell[0];
4117 vpp[1].x = dcdcell[2] * dcdcell[1];
4118 vpp[1].y = dcdcell[2] * sing;
4120 vpp[2].x = dcdcell[5] * dcdcell[3];
4121 vpp[2].y = dcdcell[5] * (dcdcell[4] - dcdcell[3] * dcdcell[1]) / sing;
4122 vpp[2].z = sqrt(dcdcell[5] * dcdcell[5] - vpp[2].x * vpp[2].x - vpp[2].y * vpp[2].y);
4123 vpp[3].x = vpp[3].y = vpp[3].z = 0.0;
4124 if (mp->cell == NULL) {
4125 /* Create periodicity if not present */
4126 MolActionCreateAndPerform(mp, gMolActionSetBox, &vpp[0], &vpp[1], &vpp[2], &vpp[3], 7, 0);
4130 if (MolActionCreateAndPerform(mp, gMolActionInsertFrames, ig, mp->natoms * dcd.nframes, vp, (cp == NULL ? 0 : dcd.nframes * 4), cp) != 0)
4131 s_append_asprintf(errbuf, "Cannot insert frames");
4132 mp->startStep = dcd.nstart;
4133 mp->stepsPerFrame = dcd.ninterval;
4134 mp->psPerStep = dcd.delta;
4143 IntGroupRelease(ig);
4150 MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
4161 fp = fopen(fname, "rb");
4163 s_append_asprintf(errbuf, "Cannot open file");
4169 flags[0] = flags[1] = flags[2] = 0;
4170 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
4171 if (strncmp(buf, "Bounding box:", 13) == 0) {
4172 for (i = 0; i < 3; i++) {
4173 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
4174 s_append_asprintf(errbuf, "line %d: missing %d component of the bounding box", lineNumber, i + 1);
4178 n = sscanf(buf, "%lf %lf %lf %d", &d[0], &d[1], &d[2], &flag);
4180 vv.x = vv.y = vv.z = 0.0;
4182 case 0: vv.x = d[0]; break;
4183 case 1: vv.y = d[0]; break;
4184 case 2: vv.z = d[0]; break;
4186 if (n == 1 || (n == 2 && d[1] != 0.0))
4193 flags[i] = (flag != 0);
4195 flags[i] = (VecLength2(vv) != 0);
4199 if (mp->cell != NULL)
4200 vv = mp->cell->origin;
4202 vv.x = vv.y = vv.z = 0.0;
4203 MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
4204 } else if (strncmp(buf, "Bounding box origin:", 20) == 0) {
4205 if (mp->cell != NULL) {
4206 v[0] = mp->cell->axes[0];
4207 v[1] = mp->cell->axes[1];
4208 v[2] = mp->cell->axes[2];
4209 memmove(flags, mp->cell->flags, 3);
4211 v[0].x = 1.0; v[0].y = v[0].z = 0.0;
4212 v[1].y = 1.0; v[1].x = v[1].z = 0.0;
4213 v[2].z = 1.0; v[2].x = v[2].y = 0.0;
4214 flags[0] = flags[1] = flags[2] = 1.0;
4216 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0 || (n = sscanf(buf, "%lf %lf %lf", &d[0], &d[1], &d[2]) < 3)) {
4217 s_append_asprintf(errbuf, "line %d: wrong format for the bounding box origin", lineNumber);
4224 MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
4236 MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
4240 if (ftype == NULL || *ftype == 0) {
4242 cp = strrchr(fname, '.');
4246 cp = guessMoleculeType(fname);
4247 if (strcmp(cp, "???") != 0)
4251 if (strcasecmp(ftype, "psf") == 0) {
4252 retval = MoleculeWriteToPsfFile(mp, fname, errbuf);
4253 } else if (strcasecmp(ftype, "pdb") == 0) {
4254 retval = MoleculeWriteToPdbFile(mp, fname, errbuf);
4255 } else if (strcasecmp(ftype, "tep") == 0) {
4256 retval = MoleculeWriteToTepFile(mp, fname, errbuf);
4258 s_append_asprintf(errbuf, "The file format should be specified");
4262 MoleculeSetPath(mp, fname);
4267 MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char **errbuf)
4270 Int i, j, k, n1, n2, n3, n_aniso, nframes, nanchors, n_uff;
4276 fp = fopen(fname, "wb");
4278 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4283 nframes = MoleculeFlushFrames(mp);
4285 fprintf(fp, "!:atoms\n");
4286 fprintf(fp, "! idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge\n");
4287 n1 = n2 = n3 = n_aniso = nanchors = n_uff = 0;
4288 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4289 strncpy(bufs[0], ap->segName, 4);
4291 strncpy(bufs[1], ap->resName, 4);
4293 strncpy(bufs[2], ap->aname, 4);
4295 AtomTypeDecodeToString(ap->type, bufs[3]);
4297 strncpy(bufs[4], ap->element, 4);
4299 for (j = 0; j < 5; j++) {
4300 if (bufs[j][0] == 0) {
4304 for (k = 0; k < 6; k++) {
4305 if (bufs[j][k] == 0)
4307 if (bufs[j][k] > 0 && bufs[j][k] < ' ')
4311 if (SYMOP_ALIVE(ap->symop))
4313 if (ap->fix_force != 0)
4315 if (ap->mm_exclude || ap->periodic_exclude)
4317 if (ap->aniso != NULL)
4319 if (ap->anchor != NULL)
4321 if (ap->uff_type[0] != 0)
4323 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);
4328 fprintf(fp, "!:uff_type\n");
4329 fprintf(fp, "! idx uff_type\n");
4330 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4331 fprintf(fp, "%d %.5s\n", i, ap->uff_type);
4337 fprintf(fp, "!:atoms_symop\n");
4338 fprintf(fp, "! idx symop symbase\n");
4339 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4341 n = ap->symop.sym * 1000000 + ap->symop.dx * 10000 + ap->symop.dy * 100 + ap->symop.dz;
4342 fprintf(fp, "%d %d %d\n", i, n, ap->symbase);
4348 fprintf(fp, "!:atoms_fix\n");
4349 fprintf(fp, "! idx fix_force fix_pos\n");
4350 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4351 fprintf(fp, "%d %f %f %f %f\n", i, ap->fix_force, ap->fix_pos.x, ap->fix_pos.y, ap->fix_pos.z);
4357 fprintf(fp, "!:mm_exclude\n");
4358 fprintf(fp, "! idx mm_exclude periodic_exclude\n");
4359 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4360 fprintf(fp, "%d %d %d\n", i, ap->mm_exclude, ap->periodic_exclude);
4366 fprintf(fp, "!:pi_anchor\n");
4367 fprintf(fp, "! idx count; n1 weight1; n2 weight2; ...; nN weightN\n");
4368 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4370 if (ap->anchor == NULL)
4372 k = ap->anchor->connect.count;
4373 ip = AtomConnectData(&ap->anchor->connect);
4374 fprintf(fp, "%d %d\n", i, k);
4375 for (j = 0; j < k; j++) {
4376 fprintf(fp, "%d %f\n", ip[j], ap->anchor->coeffs[j]);
4387 for (i = 0; (i == n2 || i < n1); i++) {
4388 fprintf(fp, "!:positions ; frame %d\n", i);
4389 fprintf(fp, "! idx x y z [sx sy sz]\n");
4390 for (j = 0, ap = mp->atoms; j < mp->natoms; j++, ap = ATOM_NEXT(ap)) {
4393 if (i != n2 && i < ap->nframes)
4394 vp = ap->frames + i;
4397 if (ap->sigma.x != 0.0 || ap->sigma.y != 0.0 || ap->sigma.z != 0.0)
4400 fprintf(fp, "%d %.8f %.8f %.8f", j, vp->x, vp->y, vp->z);
4402 fprintf(fp, " %.8f %.8f %.8f", ap->sigma.x, ap->sigma.y, ap->sigma.z);
4409 if (mp->nbonds > 0) {
4410 fprintf(fp, "!:bonds\n");
4411 fprintf(fp, "! from1 to1 from2 to2 from3 to3 from4 to4\n");
4412 for (i = 0; i < mp->nbonds; i++) {
4413 fprintf(fp, "%d %d%c", mp->bonds[i * 2], mp->bonds[i * 2 + 1], (i % 4 == 3 || i == mp->nbonds - 1 ? '\n' : ' '));
4418 if (mp->nbondOrders > 0) {
4419 fprintf(fp, "!:bond_orders\n");
4420 fprintf(fp, "! order1 order2 order3 order4\n");
4421 for (i = 0; i < mp->nbondOrders; i++) {
4422 fprintf(fp, "%.6f%c", mp->bondOrders[i], (i % 4 == 3 || i == mp->nbondOrders - 1 ? '\n' : ' '));
4427 if (mp->nangles > 0) {
4428 fprintf(fp, "!:angles\n");
4429 fprintf(fp, "! a1 b1 c1 a2 b2 c2 a3 b3 c3\n");
4430 for (i = 0; i < mp->nangles; i++) {
4431 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' : ' '));
4436 if (mp->ndihedrals > 0) {
4437 fprintf(fp, "!:dihedrals\n");
4438 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
4439 for (i = 0; i < mp->ndihedrals; i++) {
4440 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' : ' '));
4445 if (mp->nimpropers > 0) {
4446 fprintf(fp, "!:impropers\n");
4447 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
4448 for (i = 0; i < mp->nimpropers; i++) {
4449 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' : ' '));
4454 if (mp->cell != NULL) {
4455 fprintf(fp, "!:xtalcell\n");
4456 fprintf(fp, "! a b c alpha beta gamma\n");
4457 fprintf(fp, "! This information is redundant and overridden by the following periodic_box info\n");
4458 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]);
4461 fprintf(fp, "!:periodic_box\n");
4462 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");
4463 for (i = 0; i < 3; i++)
4464 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->axes[i].x, mp->cell->axes[i].y, mp->cell->axes[i].z);
4465 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->origin.x, mp->cell->origin.y, mp->cell->origin.z);
4466 fprintf(fp, "%d %d %d%s\n", mp->cell->flags[0], mp->cell->flags[1], mp->cell->flags[2], (mp->cell->has_sigma ? " 1" : ""));
4467 if (mp->cell->has_sigma) {
4468 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]);
4473 if (mp->nframe_cells > 0) {
4474 fprintf(fp, "!:frame_periodic_boxes\n");
4475 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz\n");
4476 for (i = 0; i < mp->nframe_cells * 4; i++) {
4477 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->frame_cells[i].x, mp->frame_cells[i].y, mp->frame_cells[i].z);
4482 if (mp->nsyms > 0) {
4483 fprintf(fp, "!:symmetry_operations\n");
4484 fprintf(fp, "! a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3\n");
4485 for (i = 0; i < mp->nsyms; i++) {
4486 Transform *tp = mp->syms + i;
4487 const unsigned char s_index_order[12] = {0, 3, 6, 1, 4, 7, 2, 5, 8, 9, 10, 11};
4488 for (j = 0; j < 12; j++)
4489 fprintf(fp, "%11.6f%c", (*tp)[s_index_order[j]], (j % 3 == 2 ? '\n' : ' '));
4495 fprintf(fp, "!:anisotropic_thermal_parameters\n");
4496 fprintf(fp, "! b11 b22 b33 b12 b13 b23 [sigma; sb11 sb22 sb33 sb12 sb13 sb23]\n");
4497 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4498 if (ap->aniso != NULL) {
4499 Double *bp = ap->aniso->bij;
4500 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" : ""));
4501 if (ap->aniso->has_bsig) {
4502 bp = ap->aniso->bsig;
4503 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]);
4506 fprintf(fp, "0 0 0 0 0 0\n");
4512 if (mp->arena != NULL) {
4513 MDArena *arena = mp->arena;
4514 fprintf(fp, "!:md_parameters\n");
4515 fprintf(fp, "log_file %s\n", arena->log_result_name);
4516 fprintf(fp, "coord_file %s\n", arena->coord_result_name);
4517 fprintf(fp, "vel_file %s\n", arena->vel_result_name);
4518 fprintf(fp, "force_file %s\n", arena->force_result_name);
4519 fprintf(fp, "debug_file %s\n", arena->debug_result_name);
4520 fprintf(fp, "debug_output_level %d\n", arena->debug_output_level);
4521 fprintf(fp, "step %d\n", arena->step);
4522 fprintf(fp, "coord_output_freq %d\n", arena->coord_output_freq);
4523 fprintf(fp, "energy_output_freq %d\n", arena->energy_output_freq);
4524 fprintf(fp, "coord_frame %d\n", arena->coord_result_frame);
4525 fprintf(fp, "timestep %g\n", arena->timestep);
4526 fprintf(fp, "cutoff %g\n", arena->cutoff);
4527 fprintf(fp, "electro_cutoff %g\n", arena->electro_cutoff);
4528 fprintf(fp, "pairlist_distance %g\n", arena->pairlist_distance);
4529 fprintf(fp, "switch_distance %g\n", arena->switch_distance);
4530 fprintf(fp, "temperature %g\n", arena->temperature);
4531 fprintf(fp, "andersen_freq %d\n", arena->andersen_thermo_freq);
4532 fprintf(fp, "andersen_coupling %g\n", arena->andersen_thermo_coupling);
4533 fprintf(fp, "random_seed %d\n", arena->random_seed);
4534 fprintf(fp, "dielectric %g\n", arena->dielectric);
4535 fprintf(fp, "gradient_convergence %g\n", arena->gradient_convergence);
4536 fprintf(fp, "coordinate_convergence %g\n", arena->coordinate_convergence);
4537 fprintf(fp, "use_xplor_shift %d\n", arena->use_xplor_shift);
4538 fprintf(fp, "scale14_vdw %g\n", arena->scale14_vdw);
4539 fprintf(fp, "scale14_elect %g\n", arena->scale14_elect);
4540 fprintf(fp, "relocate_center %d\n", arena->relocate_center);
4541 fprintf(fp, "surface_probe_radius %g\n", arena->probe_radius);
4542 fprintf(fp, "surface_tension %g\n", arena->surface_tension);
4543 fprintf(fp, "surface_potential_freq %d\n", arena->surface_potential_freq);
4544 fprintf(fp, "use_graphite %d\n", arena->use_graphite);
4545 fprintf(fp, "alchemical_lambda %g\n", arena->alchem_lambda);
4546 fprintf(fp, "alchemical_delta_lambda %g\n", arena->alchem_dlambda);
4547 if (arena->nalchem_flags > 0) {
4548 fprintf(fp, "alchem_flags %d", arena->nalchem_flags);
4549 for (i = 0; i < arena->nalchem_flags; i++) {
4552 else if (i % 10 == 0)
4554 fputc('0' + arena->alchem_flags[i], fp);
4558 if (arena->pressure != NULL) {
4560 fprintf(fp, "pressure_freq %d\n", arena->pressure->freq);
4561 fprintf(fp, "pressure_coupling %g\n", arena->pressure->coupling);
4562 dp = arena->pressure->apply;
4563 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]);
4564 dp = arena->pressure->cell_flexibility;
4565 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]);
4566 fprintf(fp, "pressure_fluctuate_cell_origin %g\n", arena->pressure->fluctuate_cell_origin);
4567 fprintf(fp, "pressure_fluctuate_cell_orientation %g\n", arena->pressure->fluctuate_cell_orientation);
4571 if (mp->par != NULL) {
4572 Parameter *par = mp->par;
4573 fprintf(fp, "!:parameters\n");
4574 ParameterAppendToFile(par, fp);
4578 fprintf(fp, "!:velocity\n");
4579 fprintf(fp, "! idx vx vy vz\n");
4580 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4581 fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->v.x, ap->v.y, ap->v.z);
4585 fprintf(fp, "!:force\n");
4586 fprintf(fp, "! idx fx fy fz\n");
4587 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4588 fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->f.x, ap->f.y, ap->f.z);
4593 if (mp->mview != NULL) {
4595 if (mp->mview->track != NULL) {
4596 fprintf(fp, "!:trackball\n");
4597 fprintf(fp, "! scale; trx try trz; theta_deg x y z\n");
4598 f[0] = TrackballGetScale(mp->mview->track);
4599 fprintf(fp, "%f\n", f[0]);
4600 TrackballGetTranslate(mp->mview->track, f);
4601 fprintf(fp, "%f %f %f\n", f[0], f[1], f[2]);
4602 TrackballGetRotate(mp->mview->track, f);
4603 fprintf(fp, "%f %f %f %f\n", f[0], f[1], f[2], f[3]);
4606 fprintf(fp, "!:view\n");
4607 fprintf(fp, "show_unit_cell %d\n", mp->mview->showUnitCell);
4608 fprintf(fp, "show_periodic_box %d\n", mp->mview->showPeriodicBox);
4609 fprintf(fp, "show_expanded_atoms %d\n", mp->mview->showExpandedAtoms);
4610 fprintf(fp, "show_ellipsoids %d\n", mp->mview->showEllipsoids);
4611 fprintf(fp, "show_hydrogens %d\n", mp->mview->showHydrogens);
4612 fprintf(fp, "show_dummy_atoms %d\n", mp->mview->showDummyAtoms);
4613 fprintf(fp, "show_rotation_center %d\n", mp->mview->showRotationCenter);
4614 fprintf(fp, "show_graphite_flag %d\n", mp->mview->showGraphiteFlag);
4615 fprintf(fp, "show_graphite %d\n", mp->mview->showGraphite);
4616 fprintf(fp, "show_periodic_image_flag %d\n", mp->mview->showPeriodicImageFlag);
4617 fprintf(fp, "show_periodic_image %d %d %d %d %d %d\n",
4618 mp->mview->showPeriodicImage[0], mp->mview->showPeriodicImage[1],
4619 mp->mview->showPeriodicImage[2], mp->mview->showPeriodicImage[3],
4620 mp->mview->showPeriodicImage[4], mp->mview->showPeriodicImage[5]);
4621 if (mp->mview->atomRadius != 0.2)
4622 fprintf(fp, "atom_radius %f\n", mp->mview->atomRadius);
4623 if (mp->mview->bondRadius != 0.1)
4624 fprintf(fp, "bond_radius %f\n", mp->mview->bondRadius);
4625 if (mp->mview->atomResolution != 12)
4626 fprintf(fp, "atom_resolution %d\n", mp->mview->atomResolution);
4627 if (mp->mview->bondResolution != 8)
4628 fprintf(fp, "bond_resolution %d\n", mp->mview->bondResolution);
4632 if (mp->nmolprops > 0) {
4634 for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
4635 /* Encode the property name if necessary */
4638 for (p = prp->propname; *p != 0 && n1 < 900; p++) {
4639 if (*p > ' ' && *p != '%' && *p < 0x7f) {
4643 sprintf(enc + n1, "%%%02x", *p);
4650 enc[n2] = 0; /* Truncate after last ASCII character */
4654 sprintf(enc, "prop_%d", i + 1);
4657 fprintf(fp, "!:property ; %s\n", enc);
4658 for (j = 0; j < nframes; j++) {
4659 fprintf(fp, "%.18g\n", prp->propvals[j]);
4665 if (mp->bset != NULL) {
4666 /* Gaussian primitive info */
4669 fprintf(fp, "!:gaussian_primitives\n");
4670 fprintf(fp, "! sym nprims a_idx; A C Csp\n");
4671 for (i = 0, sp = mp->bset->shells; i < mp->bset->nshells; i++, sp++) {
4673 case kGTOType_S: p = "S"; break;
4674 case kGTOType_P: p = "P"; break;
4675 case kGTOType_SP: p = "SP"; break;
4676 case kGTOType_D: p = "D"; break;
4677 case kGTOType_D5: p = "D5"; break;
4678 case kGTOType_F: p = "F"; break;
4679 case kGTOType_F7: p = "F7"; break;
4680 case kGTOType_G: p = "G"; break;
4681 case kGTOType_G9: p = "G9"; break;
4682 default: snprintf(bufs[0], 8, "X%d", sp->sym); p = bufs[0]; break;
4684 fprintf(fp, "%s %d %d\n", p, sp->nprim, sp->a_idx);
4685 pp = mp->bset->priminfos + sp->p_idx;
4686 for (j = 0; j < sp->nprim; j++, pp++) {
4687 fprintf(fp, "%.18g %.18g %.18g\n", pp->A, pp->C, pp->Csp);
4693 fprintf(fp, "!:mo_info\n");
4694 fprintf(fp, "! uhf|rhf|rohf ne_alpha ne_beta\n");
4695 switch (mp->bset->rflag) {
4696 case 0: p = "UHF"; break;
4697 case 1: p = "RHF"; break;
4698 case 2: p = "ROHF"; break;
4699 default: p = "(unknown)"; break;
4701 fprintf(fp, "%s %d %d\n", p, mp->bset->ne_alpha, mp->bset->ne_beta);
4704 /* MO coefficients */
4705 fprintf(fp, "!:mo_coefficients\n");
4706 for (i = 0; i < mp->bset->nmos; i++) {
4707 fprintf(fp, "MO %d %.18g\n", i + 1, mp->bset->moenergies[i]);
4708 for (j = 0; j < mp->bset->ncomps; j++) {
4709 fprintf(fp, "%.18g%c", mp->bset->mo[i * mp->bset->ncomps + j], (j % 6 == 5 || j == mp->bset->ncomps - 1 ? '\n' : ' '));
4715 if (mp->mview != NULL && mp->mview->ngraphics > 0) {
4716 MainViewGraphic *gp;
4717 fprintf(fp, "!:graphics\n");
4718 for (i = 0; i < mp->mview->ngraphics; i++) {
4719 gp = mp->mview->graphics + i;
4721 case kMainViewGraphicLine: fprintf(fp, "line\n"); break;
4722 case kMainViewGraphicPoly: fprintf(fp, "poly\n"); break;
4723 case kMainViewGraphicCylinder: fprintf(fp, "cylinder\n"); break;
4724 case kMainViewGraphicCone: fprintf(fp, "cone\n"); break;
4725 case kMainViewGraphicEllipsoid: fprintf(fp, "ellipsoid\n"); break;
4726 default: fprintf(fp, "unknown\n"); break;
4728 fprintf(fp, "%d %d\n", gp->closed, gp->visible);
4729 fprintf(fp, "%.4f %.4f %.4f %.4f\n", gp->rgba[0], gp->rgba[1], gp->rgba[2], gp->rgba[3]);
4730 fprintf(fp, "%d\n", gp->npoints);
4731 for (j = 0; j < gp->npoints; j++)
4732 fprintf(fp, "%.6f %.6f %.6f\n", gp->points[j * 3], gp->points[j * 3 + 1], gp->points[j * 3 + 2]);
4733 fprintf(fp, "%d\n", gp->nnormals);
4734 for (j = 0; j < gp->nnormals; j++)
4735 fprintf(fp, "%.6f %.6f %.6f\n", gp->normals[j * 3], gp->normals[j * 3 + 1], gp->normals[j * 3 + 2]);
4740 /* Plug-in in the Ruby world */
4743 if (MolActionCreateAndPerform(mp, SCRIPT_ACTION(";s"),
4744 "proc { savembsf_plugin rescue \"Plug-in error: #{$!.to_s}\" }", &outMessage) == 0) {
4745 if (outMessage[0] != 0) {
4746 if (strncmp(outMessage, "Plug-in", 7) == 0) {
4747 s_append_asprintf(errbuf, "%s", outMessage);
4749 fprintf(fp, "%s\n", outMessage);
4761 MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char **errbuf)
4767 fp = fopen(fname, "wb");
4769 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4772 fprintf(fp, "PSF\n\n");
4773 fprintf(fp, " 1 !NTITLE\n");
4774 fprintf(fp, " REMARKS FILENAME=\n");
4778 fprintf(fp, "%8d !NATOM\n", mp->natoms);
4779 for (i = 0; i < mp->natoms; i++) {
4781 ap = ATOM_AT_INDEX(mp->atoms, i);
4782 fprintf(fp, "%8d ", i + 1);
4783 if (ap->resSeq >= 10000) {
4784 fmt = "%-3.3s %-5d ";
4786 fmt = "%-4.4s %-4d ";
4788 fprintf(fp, fmt, ap->segName, ap->resSeq);
4789 fprintf(fp, "%-3.3s %-4.4s %-4.4s %12.6f %8.4f 0\n",
4790 ap->resName, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->charge, ap->weight);
4795 fprintf(fp, "%8d !NBOND: bonds\n", mp->nbonds);
4796 for (i = 0; i < mp->nbonds * 2; i++) {
4797 fprintf(fp, "%8d", mp->bonds[i] + 1);
4806 fprintf(fp, "%8d !NTHETA: angles\n", mp->nangles);
4807 for (i = 0; i < mp->nangles * 3; i++) {
4808 fprintf(fp, "%8d", mp->angles[i] + 1);
4817 fprintf(fp, "%8d !NPHI: dihedrals\n", mp->ndihedrals);
4818 for (i = 0; i < mp->ndihedrals * 4; i++) {
4819 fprintf(fp, "%8d", mp->dihedrals[i] + 1);
4828 fprintf(fp, "%8d !NIMPHI: impropers\n", mp->nimpropers);
4829 for (i = 0; i < mp->nimpropers * 4; i++) {
4830 fprintf(fp, "%8d", mp->impropers[i] + 1);
4838 fprintf(fp, "%8d !NDON: donors\n\n", 0);
4839 fprintf(fp, "%8d !NACC: acceptors\n\n", 0);
4840 fprintf(fp, "%8d !NNB: non-bonding exclusions\n\n", 0);
4841 for (i = 0; i < mp->natoms; i++) {
4842 fprintf(fp, "%8d", 0);
4849 fprintf(fp, "%8d !NGRP: groups\n", 1);
4850 fprintf(fp, " 0 0 0\n");
4854 if (i > 5 && strcmp(fname + i - 5, ".psfx") == 0) {
4855 /* Extended psf (with coordinates and other info) */
4856 fprintf(fp, "%8d !COORD: coordinates\n", mp->natoms);
4857 for (i = 0; i < mp->natoms; i++) {
4859 ap = ATOM_AT_INDEX(mp->atoms, i);
4861 fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->aname);
4871 MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char **errbuf)
4877 fp = fopen(fname, "wb");
4879 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4882 for (i = 0; i < mp->natoms; i++) {
4884 ap = ATOM_AT_INDEX(mp->atoms, i);
4885 if (ap->resSeq >= 10000) {
4886 snprintf(buf, sizeof buf, "%c%03d", 'A' + (ap->resSeq - 10000) / 1000, ap->resSeq % 1000);
4888 snprintf(buf, sizeof buf, "%4d", ap->resSeq);
4890 fprintf(fp, "ATOM %5d %-4.4s%1.1s%-3.3s %1.1s%4.4s%1.1s "
4891 "%8.3f%8.3f%8.3f %5.2f %5.2f "
4892 "%-4.4s%-2.2s%-2d\n",
4893 i + 1, ap->aname, " ", ap->resName, " ", buf, " ",
4894 ap->r.x, ap->r.y, ap->r.z, ap->occupancy, ap->tempFactor,
4895 ap->segName, ap->element, ap->intCharge);
4897 for (i = 0; i < mp->natoms; i++) {
4899 ap = ATOM_AT_INDEX(mp->atoms, i);
4900 cp = AtomConnectData(&ap->connect);
4901 for (j = 0; j < ap->connect.count; j++) {
4905 fprintf(fp, "CONECT%5d", i + 1);
4907 fprintf(fp, "%5d", cp[j] + 1);
4912 fprintf(fp, "END\n");
4918 MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char **errbuf)
4921 SFloat32 *xp, *yp, *zp;
4924 if (mp == NULL || mp->natoms == 0) {
4925 s_append_asprintf(errbuf, "Molecule is empty");
4928 memset(&dcd, 0, sizeof(dcd));
4929 dcd.natoms = mp->natoms;
4930 dcd.nframes = MoleculeGetNumberOfFrames(mp);
4931 if (dcd.nframes == 0) {
4932 s_append_asprintf(errbuf, "no frame is present");
4935 dcd.nstart = mp->startStep;
4936 dcd.ninterval = mp->stepsPerFrame;
4937 if (dcd.ninterval == 0)
4939 dcd.nend = dcd.nstart + (dcd.nframes - 1) * dcd.ninterval;
4940 if (mp->cell != NULL)
4942 dcd.delta = mp->psPerStep;
4943 if (dcd.delta == 0.0)
4946 n = DcdCreate(fname, &dcd);
4949 s_append_asprintf(errbuf, "Cannot create dcd file");
4951 s_append_asprintf(errbuf, "Cannot write dcd header");
4956 xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4957 yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4958 zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4959 if (xp == NULL || yp == NULL || zp == NULL) {
4960 s_append_asprintf(errbuf, "Cannot allocate memory");
4967 for (n = 0; n < dcd.nframes; n++) {
4970 for (i = 0, ap = mp->atoms; i < dcd.natoms && mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4972 if (ap->frames == NULL || n >= ap->nframes)
4980 if (i < dcd.natoms) {
4981 size_t sz = (dcd.natoms - i) * sizeof(SFloat32);
4982 memset(xp + i, 0, sz);
4983 memset(yp + i, 0, sz);
4984 memset(zp + i, 0, sz);
4986 if (n < mp->nframe_cells && mp->frame_cells != NULL) {
4987 Vector *cp = &(mp->frame_cells[n * 4]);
4988 dcd.globalcell[0] = VecLength(cp[0]);
4989 dcd.globalcell[2] = VecLength(cp[1]);
4990 dcd.globalcell[5] = VecLength(cp[2]);
4991 dcd.globalcell[1] = VecDot(cp[0], cp[1]) / (dcd.globalcell[0] * dcd.globalcell[2]);
4992 dcd.globalcell[3] = VecDot(cp[0], cp[2]) / (dcd.globalcell[0] * dcd.globalcell[5]);
4993 dcd.globalcell[4] = VecDot(cp[1], cp[2]) / (dcd.globalcell[2] * dcd.globalcell[5]);
4995 if (DcdWriteFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
4996 s_append_asprintf(errbuf, "Write error in dcd file");
5012 MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
5018 fp = fopen(fname, "wb");
5020 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
5023 if (mp->cell != NULL) {
5024 fprintf(fp, "Bounding box:\n");
5025 for (i = 0; i < 3; i++) {
5026 v = mp->cell->axes[i];
5027 fprintf(fp, "%.3f %.3f %.3f %d\n", v.x, v.y, v.z, mp->cell->flags[i]);
5029 fprintf(fp, "Bounding box origin:\n");
5030 v = mp->cell->origin;
5031 fprintf(fp, "%.3f %.3f %.3f\n", v.x, v.y, v.z);
5038 sCompareByElement(const void *ap, const void *bp)
5040 return ((*(Atom **)bp)->atomicNumber - (*(Atom **)ap)->atomicNumber);
5044 sMakeAdc(int n, int base, Symop symop)
5047 if (SYMOP_ALIVE(symop)) {
5049 sym = (symop.dx + 5) * 10000 + (symop.dy + 5) * 1000 + (symop.dz + 5) * 100 + symop.sym + 1;
5054 return (an + 1) * 100000 + sym;
5058 sCompareAdc(const void *ap, const void *bp)
5060 int n = *((Int *)ap) % 100000 - *((Int *)bp) % 100000;
5062 n = *((Int *)ap) / 100000 - *((Int *)bp) / 100000;
5067 sOutputAtomListInstructions(FILE *fp, int natoms, Atom *atoms)
5069 int i, j, k, an, sym;
5072 adc = (Int *)malloc(sizeof(Int) * natoms);
5075 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
5076 if (ap->exflags & kAtomHiddenFlag)
5078 adc[i] = sMakeAdc(i, ap->symbase, ap->symop);
5080 mergesort(adc, natoms, sizeof(Int), sCompareAdc);
5082 /* Create the atom list */
5084 for (i = j = k = 0; i < natoms; i++) {
5085 int an1 = adc[i] / 100000;
5086 int sym1 = adc[i] % 100000;
5087 if (sym == sym1 && an1 == an + 1) {
5094 /* Output the last atom with a minus sign */
5095 adc[j++] = -(an * 100000 + sym);
5096 /* Output this atom */
5103 adc[j++] = -(an * 100000 + sym);
5105 /* Create the instruction cards */
5106 for (i = k = 0; i < j; i++) {
5108 fprintf(fp, " 401");
5109 fprintf(fp, "%9d", adc[i]);
5111 if (i == j - 1 || k == 6 || (k == 5 && i < j - 2 && adc[i + 2] < 0)) {
5120 sEllipsoidType(int an)
5122 return (an >= 18 ? 3 : (an >= 2 && an != 6 ? 2 : (an > 0 ? 1 : 0)));
5126 sOutputAtomTypeInstructions(FILE *fp, int natoms, Atom *atoms)
5130 int etype, elast, istart, ilast, n1, n2;
5131 elast = istart = ilast = -1;
5132 for (i = 0, ap = atoms; i <= natoms; i++, ap++) {
5134 if (SYMOP_ALIVE(ap->symop))
5136 if (ap->exflags & kAtomHiddenFlag)
5138 etype = sEllipsoidType(ap->atomicNumber);
5143 } else if (elast == etype && ilast == i - 1) {
5148 /* Output the instruction card for the 'last' block of atoms */
5151 n1 = 4; n2 = 0; break;
5153 n1 = 4; n2 = 5; break;
5155 n1 = 1; n2 = 0; break;
5157 fprintf(fp, " 1 715 %8d 0 %8d 0 0.100 0.000 0.000\n", n1, n2);
5158 fprintf(fp, " %9d%9d\n", istart + 1, ilast + 1);
5165 sCompareBondType(const void *ap, const void *bp)
5167 /* Descending order */
5168 return *((int *)bp) - *((int *)ap);
5172 sOutputBondInstructions(FILE *fp, int natoms, Atom *atoms, int overlap_correction)
5176 int i, j, n[5], an, count, n1, n2, k;
5180 static const float sBondRad[4] = {0.060, 0.060, 0.060, 0.040};
5181 static const int sBondShade[4] = {5, 3, 1, 1};
5183 n[0] = n[1] = n[2] = n[3] = 0; /* Start index of 3rd row atoms (and higher), 2nd row, 1st row, and H */
5185 for (i = natoms - 1, ap = atoms + i; i >= 0; i--, ap--) {
5186 an = ap->atomicNumber;
5198 if (overlap_correction)
5199 strcpy(buf, " 2 1001 0.000\n");
5201 strcpy(buf, " 2 812\n");
5203 for (i = 0; i < 4; i++) {
5204 for (j = i; j < 4; j++) {
5205 /* Examine bonds between "group i" and "group j" */
5208 double min_bond = 10000.0; /* Minimum distance between bound atoms */
5209 double min_nonbond = 10000.0; /* Minimum distance between non-bound atoms */
5210 double max_bond = -10000.0; /* Maximum distance between bound atoms */
5211 int count_exbond = 0; /* Number of explicit bonds in this group */
5212 for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
5213 for (n2 = n[j], ap2 = atoms + n2; n2 < n[j + 1]; n2++, ap2++) {
5216 VecSub(dr, ap->r, ap2->r);
5218 cp = AtomConnectData(&ap->connect);
5219 for (k = ap->connect.count - 1; k >= 0; k--) {
5224 /* n1 and n2 are bound */
5230 /* n1 and n2 are not bound */
5231 if (d < min_nonbond)
5236 if (min_bond == 10000.0)
5237 continue; /* No bonds between these groups */
5239 if (max_bond + 0.002 < min_nonbond)
5242 max_bond = min_nonbond - 0.002;
5243 /* Some bonds may be omitted, so scan all bonds again */
5244 for (n1 = n[i], ap = ATOM_AT_INDEX(atoms, n1); n1 < n[i + 1]; n1++, ap = ATOM_NEXT(ap)) {
5245 cp = AtomConnectData(&ap->connect);
5246 for (k = ap->connect.count - 1; k >= 0; k--) {
5248 if (n2 < n[j] || n2 >= n[j + 1])
5251 VecSub(dr, ap->r, ap2->r);
5254 /* This bond should be explicitly defined */
5256 if (count_exbond == 0) {
5257 adc1 = -(i + 1); /* Bond type */
5258 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
5260 adc1 = sMakeAdc(n1, ap->symbase, ap->symop);
5261 adc2 = sMakeAdc(n2, ap2->symbase, ap2->symop);
5262 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
5263 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc2);
5269 /* Output the last instruction card */
5271 /* Make a new trailer card */
5272 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]);
5277 /* Output the last trailer card */
5282 if (count == 0 && overlap_correction) {
5283 /* 1001 card is not yet written, so write it */
5287 snprintf(buf, sizeof(buf), " 1 %3d", (overlap_correction ? 821 : 811));
5288 k = -exbonds[0] - 1; /* Bond type for the first block */
5289 i = 1; /* Index for exbonds[] */
5290 j = 0; /* Count in this block */
5291 while (i <= nexbonds) {
5292 if (j >= 29 || i == nexbonds || exbonds[i] < 0) {
5296 /* The trailer card */
5297 fprintf(fp, " %3d %6.3f\n", sBondShade[k], sBondRad[k]);
5301 k = -exbonds[i++] - 1; /* The new bond type */
5303 } else if (j > 0 && j % 3 == 0) {
5309 snprintf(buf + 9 + (j % 3) * 18, sizeof(buf) - 9 - (j % 3) * 18, "%9d%9d\n", n1, n2);
5317 MoleculeWriteToTepFile(Molecule *mp, const char *fname, char **errbuf)
5320 int i, j, natoms, *ip;
5322 Atom *ap, *atoms, **app;
5324 static Double sUnit[] = {1, 1, 1, 90, 90, 90};
5328 /* Create sorted array of atoms */
5329 natoms = mp->natoms;
5330 atoms = (Atom *)calloc(sizeof(Atom), natoms);
5331 app = (Atom **)calloc(sizeof(Atom *), natoms);
5332 ip = (int *)calloc(sizeof(int), natoms);
5333 if (atoms == NULL || app == NULL || ip == NULL) {
5334 s_append_asprintf(errbuf, "Cannot allocate memory");
5337 /* Sort the atom pointer by atomic number */
5338 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
5340 mergesort(app, natoms, sizeof(Atom *), sCompareByElement);
5341 for (i = 0; i < natoms; i++) {
5342 /* ip[old_index] is new_index */
5343 ip[app[i] - mp->atoms] = i;
5345 /* Copy the atom record to atoms[] */
5346 /* The 'v' member contains crystallographic coordinates */
5347 /* The connection table and symbase are renumbered */
5348 /* Hidden flags are modified to reflect the visibility in the MainView */
5349 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
5350 AtomDuplicateNoFrame(ap, app[i]);
5351 /* memmove(ap, app[i], gSizeOfAtomRecord); */
5352 MoleculeCartesianToXtal(mp, &(ap->v), &(ap->r));
5353 cp = AtomConnectData(&ap->connect);
5354 for (j = ap->connect.count - 1; j >= 0; j--) {
5357 if (SYMOP_ALIVE(ap->symop))
5358 ap->symbase = ip[ap->symbase];
5359 if (MainView_isAtomHidden(mp->mview, i)) {
5360 ap->exflags |= kAtomHiddenFlag;
5362 ap->exflags &= ~kAtomHiddenFlag;
5368 fp = fopen(fname, "wb");
5370 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
5375 fprintf(fp, "Generated by Molby\n");
5378 if (mp->cell != NULL) {
5379 dp = mp->cell->cell;
5383 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]);
5385 /* Symmetry operations */
5386 if (mp->nsyms > 0) {
5387 for (i = 0; i < mp->nsyms; i++) {
5389 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]);
5392 fprintf(fp, "1 0 1 0 0 0 0 1 0 0 0 0 1\n");
5396 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
5397 /* The 'v' field contains crystallographic coordinates */
5398 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);
5399 if (ap->aniso != NULL) {
5400 dp = ap->aniso->bij;
5401 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);
5403 Double temp = ap->tempFactor;
5406 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", temp, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5409 /* Special points */
5411 Vector camera, lookat, up, xvec, yvec, zvec;
5412 MainView_getCamera(mp->mview, &camera, &lookat, &up);
5413 VecSub(zvec, lookat, camera);
5414 VecCross(xvec, zvec, up);
5415 NormalizeVec(&xvec, &xvec);
5416 NormalizeVec(&yvec, &up);
5417 VecInc(xvec, lookat);
5418 VecInc(yvec, lookat);
5419 MoleculeCartesianToXtal(mp, &lookat, &lookat);
5420 MoleculeCartesianToXtal(mp, &xvec, &xvec);
5421 MoleculeCartesianToXtal(mp, &yvec, &yvec);
5422 fprintf(fp, " ORGN %9g%9g%9g 0\n", 0.0, 0.0, 0.0);
5423 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5424 fprintf(fp, " CNTR %9g%9g%9g 0\n", lookat.x, lookat.y, lookat.z);
5425 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5426 fprintf(fp, " X %9g%9g%9g 0\n", xvec.x, xvec.y, xvec.z);
5427 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5428 fprintf(fp, " Y %9g%9g%9g 0\n", yvec.x, yvec.y, yvec.z);
5429 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);
5433 fprintf(fp, " 201\n");
5434 fprintf(fp, " 205 12\n");
5435 fprintf(fp, " 301 6.6 6.6 0 0.8\n");
5436 sOutputAtomListInstructions(fp, natoms, atoms);
5437 fprintf(fp, " 501%4d55501%4d55501%4d55501%4d55501%4d55501 1\n", natoms + 2, natoms + 2, natoms + 3, natoms + 2, natoms + 4);
5438 fprintf(fp, " 502 1 0.0 2 0.0 3 0.0\n");
5439 fprintf(fp, " 604 1.538\n");
5441 sOutputBondInstructions(fp, natoms, atoms, 1);
5442 sOutputAtomTypeInstructions(fp, natoms, atoms);
5443 sOutputBondInstructions(fp, natoms, atoms, 0);
5445 for (i = 0; i < natoms; i++) {
5446 AtomClean(atoms + i);
5450 fprintf(fp, " 202\n");
5451 fprintf(fp, " 0 -1\n");
5457 MoleculeDump(Molecule *mol)
5462 for (i = 0; i < mol->natoms; i++) {
5464 ap = ATOM_AT_INDEX(mol->atoms, i);
5465 snprintf(buf1, sizeof buf1, "%3.4s.%d", ap->resName, ap->resSeq);
5466 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);
5467 cp = AtomConnectData(&ap->connect);
5468 for (j = 0; j < ap->connect.count; j++) {
5469 fprintf(stderr, "%s%d", (j > 0 ? "," : ""), cp[j]);
5471 fprintf(stderr, "]\n");
5475 #pragma mark ====== MD support (including modification of Molecule) ======
5477 /* Call md_prepare for the MDArena. If MDArena has not been created, a new arena is created.
5478 If something goes wrong, returns 1 (for missing parameters) or -1 (more serious error).
5479 If retmsg is not NULL, a message describing the problem is returned there. This message
5480 must be free'd by the caller. */
5482 MoleculePrepareMDArena(Molecule *mol, int check_only, char **retmsg)
5485 Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
5487 IntGroup *ig1, *ig2, *ig3;
5488 MDArena *arena = mol->arena;
5490 if (arena == NULL) {
5493 } else if (arena->xmol != mol)
5494 md_arena_set_molecule(arena, mol);
5496 arena->is_initialized = 0;
5498 /* Rebuild the tables */
5499 ig1 = ig2 = ig3 = NULL;
5500 nangles = MoleculeFindMissingAngles(mol, &angles);
5501 ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
5502 nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
5504 ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
5505 MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
5507 IntGroupRelease(ig1);
5509 if (ndihedrals > 0) {
5510 ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
5511 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
5513 IntGroupRelease(ig2);
5515 if (nimpropers > 0) {
5516 ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
5517 MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
5519 IntGroupRelease(ig3);
5523 /* Update the path information of the molecule before MD setup */
5524 char *buf = (char *)malloc(4096);
5525 MoleculeCallback_pathName(mol, buf, sizeof buf);
5526 MoleculeSetPath(mol, buf);
5530 /* Prepare parameters and internal information */
5531 msg = md_prepare(arena, check_only);
5533 /* Some parameters are missing? */
5535 if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
5539 asprintf(retmsg, "cannot initialize for MD: %s", msg);
5544 /* The local parameter list is updated */
5547 if (mol->par == NULL)
5548 mol->par = ParameterNew();
5549 for (parType = kFirstParType; parType <= kLastParType; parType++) {
5550 /* Delete global and undefined parameters */
5551 UnionPar *up, *upbuf;
5553 ig1 = IntGroupNew();
5554 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
5555 if (up->bond.src != 0)
5556 IntGroupAdd(ig1, idx, 1);
5558 if (IntGroupGetCount(ig1) > 0)
5559 MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
5560 IntGroupRelease(ig1);
5561 /* Copy global and undefined parameters from arena and insert to mol->par */
5562 nparams = ParameterGetCountForType(arena->par, parType);
5565 upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
5566 ig1 = IntGroupNew();
5567 ig2 = IntGroupNew();
5568 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
5569 if (up->bond.src > 0)
5570 IntGroupAdd(ig1, idx, 1); /* Global parameter */
5571 else if (up->bond.src < 0)
5572 IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
5574 if ((count = IntGroupGetCount(ig1)) > 0) {
5575 /* Insert global parameters (at the top) */
5576 ParameterCopy(arena->par, parType, upbuf, ig1);
5577 ig3 = IntGroupNewWithPoints(0, count, -1);
5578 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5579 IntGroupRelease(ig3);
5581 if ((count = IntGroupGetCount(ig2)) > 0) {
5582 /* Insert undefined parameters (at the bottom) */
5583 ParameterCopy(arena->par, parType, upbuf, ig2);
5584 idx = ParameterGetCountForType(mol->par, parType);
5585 ig3 = IntGroupNewWithPoints(idx, count, -1);
5586 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5587 IntGroupRelease(ig3);
5589 IntGroupRelease(ig2);
5590 IntGroupRelease(ig1);
5593 mol->needsMDRebuild = 0; /* We know the "modified" parameters are consistent with the MDArena */
5598 *retmsg = strdup(msg);
5603 #pragma mark ====== Serialize ======
5606 MoleculeDeserialize(const char *data, Int length, Int *timep)
5616 par = ParameterNew();
5620 while (length >= 12) {
5621 const char *ptr = data + 8 + sizeof(Int);
5622 int len = *((const Int *)(data + 8));
5624 if (strcmp(data, "ATOM") == 0) {
5625 n = len / gSizeOfAtomRecord;
5626 NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, n);
5627 memmove(mp->atoms, ptr, len);
5628 } else if (strcmp(data, "ANISO") == 0) {
5629 n = len / (sizeof(Int) + sizeof(Aniso));
5630 for (i = 0; i < n; i++) {
5631 j = *((const Int *)ptr);
5632 if (j < 0 || j >= mp->natoms)
5634 ap = ATOM_AT_INDEX(mp->atoms, j);
5635 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
5636 if (ap->aniso == NULL)
5638 *(ap->aniso) = *((Aniso *)(ptr + sizeof(Int)));
5639 ptr += sizeof(Int) + sizeof(Aniso);
5641 } else if (strcmp(data, "FRAME") == 0) {
5642 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5643 if (ap->nframes == 0)
5648 NewArray(&ap->frames, &ap->nframes, sizeof(Vector), n);
5649 if (ap->frames == NULL)
5651 memmove(ap->frames, ptr, sizeof(Vector) * ap->nframes);
5652 ptr += sizeof(Vector) * ap->nframes;
5654 } else if (strcmp(data, "EXTCON") == 0) {
5655 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5656 if (ap->connect.count <= ATOM_CONNECT_LIMIT)
5658 n = ap->connect.count;
5659 ap->connect.count = 0;
5660 ap->connect.u.ptr = NULL;
5661 NewArray(&(ap->connect.u.ptr), &(ap->connect.count), sizeof(Int), n);
5662 memmove(ap->connect.u.ptr, ptr, sizeof(Int) * n);
5663 ptr += sizeof(Int) * n;
5665 } else if (strcmp(data, "BOND") == 0) {
5666 n = len / (sizeof(Int) * 2);
5667 NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, n);
5668 memmove(mp->bonds, ptr, len);
5669 } else if (strcmp(data, "ANGLE") == 0) {
5670 n = len / (sizeof(Int) * 3);
5671 NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, n);
5672 memmove(mp->angles, ptr, len);
5673 } else if (strcmp(data, "DIHED") == 0) {
5674 n = len / (sizeof(Int) * 4);
5675 NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, n);
5676 memmove(mp->dihedrals, ptr, len);
5677 } else if (strcmp(data, "IMPROP") == 0) {
5678 n = len / (sizeof(Int) * 4);
5679 NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, n);
5680 memmove(mp->impropers, ptr, len);
5681 } else if (strcmp(data, "RESIDUE") == 0) {
5683 NewArray(&mp->residues, &mp->nresidues, 4, n);
5684 memmove(mp->residues, ptr, len);
5685 } else if (strcmp(data, "CELL") == 0) {
5686 mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
5687 if (mp->cell == NULL)
5689 memmove(mp->cell, ptr, sizeof(XtalCell));
5690 } else if (strcmp(data, "SYMOP") == 0) {
5691 n = len / sizeof(Transform);
5692 NewArray(&mp->syms, &mp->nsyms, sizeof(Transform), n);
5693 memmove(mp->syms, ptr, len);
5694 } else if (strcmp(data, "ANCHOR") == 0) {
5695 const char *ptr2 = ptr + len;
5696 while (ptr < ptr2) {
5698 memset(&an, 0, sizeof(an));
5700 if (i >= 0 && i < mp->natoms) {
5701 n = *((Int *)(ptr + sizeof(Int)));
5702 AtomConnectResize(&(an.connect), n);
5703 memmove(AtomConnectData(&(an.connect)), ptr + sizeof(Int) * 2, sizeof(Int) * n);
5704 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), n);
5705 memmove(an.coeffs, ptr + sizeof(Int) * (2 + n), sizeof(Double) * n);
5706 ap = ATOM_AT_INDEX(mp->atoms, i);
5707 ap->anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
5708 memmove(ap->anchor, &an, sizeof(PiAnchor));
5710 ptr += sizeof(Int) * (2 + n) + sizeof(Double) * n;
5712 } else if (strcmp(data, "TIME") == 0) {
5714 *timep = *((Int *)ptr);
5715 } else if (strcmp(data, "BONDPAR") == 0) {
5717 n = len / sizeof(BondPar);
5718 NewArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), n);
5719 memmove(par->bondPars, ptr, len);
5720 } else if (strcmp(data, "ANGPAR") == 0) {
5722 n = len / sizeof(AnglePar);
5723 NewArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), n);
5724 memmove(par->anglePars, ptr, len);
5725 } else if (strcmp(data, "DIHEPAR") == 0) {
5727 n = len / sizeof(TorsionPar);
5728 NewArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), n);
5729 memmove(par->dihedralPars, ptr, len);
5730 } else if (strcmp(data, "IMPRPAR") == 0) {
5732 n = len / sizeof(TorsionPar);
5733 NewArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), n);
5734 memmove(par->improperPars, ptr, len);
5735 } else if (strcmp(data, "VDWPAR") == 0) {
5737 n = len / sizeof(VdwPar);
5738 NewArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), n);
5739 memmove(par->vdwPars, ptr, len);
5740 } else if (strcmp(data, "VDWPPAR") == 0) {
5742 n = len / sizeof(VdwPairPar);
5743 NewArray(&par->vdwpPars, &par->nvdwpPars, sizeof(VdwPairPar), n);
5744 memmove(par->vdwpPars, ptr, len);
5745 } else if (strcmp(data, "VCUTPAR") == 0) {
5747 n = len / sizeof(VdwCutoffPar);
5748 NewArray(&par->vdwCutoffPars, &par->nvdwCutoffPars, sizeof(VdwCutoffPar), n);
5749 memmove(par->vdwCutoffPars, ptr, len);
5751 len += 8 + sizeof(Int);
5755 if (mp->par == NULL)
5756 ParameterRelease(par);
5757 /* result = MoleculeRebuildTablesFromConnects(mp);
5763 Panic("Low memory while deserializing molecule data");
5764 return NULL; /* Not reached */
5767 Panic("internal error: bad format during deserializing molecule data");
5768 return NULL; /* Not reached */
5772 MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep)
5775 int len, len_all, i, naniso, nframes, nconnects, nanchors;
5778 /* Array of atoms */
5779 len = 8 + sizeof(Int) + gSizeOfAtomRecord * mp->natoms;
5780 ptr = (char *)malloc(len);
5783 memmove(ptr, "ATOM\0\0\0\0", 8);
5784 *((Int *)(ptr + 8)) = gSizeOfAtomRecord * mp->natoms;
5785 p = ptr + 8 + sizeof(Int);
5786 memmove(p, mp->atoms, gSizeOfAtomRecord * mp->natoms);
5787 naniso = nframes = nconnects = nanchors = 0;
5788 for (i = 0; i < mp->natoms; i++) {
5789 ap = ATOM_AT_INDEX(p, i);
5790 if (ap->aniso != NULL) {
5794 if (ap->frames != NULL) {
5795 nframes += ap->nframes;
5798 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5799 nconnects += ap->connect.count;
5800 ap->connect.u.ptr = NULL;
5802 if (ap->anchor != NULL) {
5809 /* Array of aniso */
5811 len = 8 + sizeof(Int) + (sizeof(Int) + sizeof(Aniso)) * naniso;
5812 ptr = (char *)realloc(ptr, len_all + len);
5816 memmove(p, "ANISO\0\0\0", 8);
5817 *((Int *)(p + 8)) = (sizeof(Int) + sizeof(Aniso)) * naniso;
5818 p += 8 + sizeof(Int);
5819 for (i = 0; i < mp->natoms; i++) {
5820 ap = ATOM_AT_INDEX(mp->atoms, i);
5821 if (ap->aniso != NULL) {
5823 *((Aniso *)(p + sizeof(Int))) = *(ap->aniso);
5824 p += sizeof(Int) + sizeof(Aniso);
5830 /* Array of frames */
5832 len = 8 + sizeof(Int) + sizeof(Vector) * nframes;
5833 ptr = (char *)realloc(ptr, len_all + len);
5837 memmove(p, "FRAME\0\0\0", 8);
5838 *((Int *)(p + 8)) = sizeof(Vector) * nframes;
5839 p += 8 + sizeof(Int);
5840 for (i = 0; i < mp->natoms; i++) {
5841 ap = ATOM_AT_INDEX(mp->atoms, i);
5842 if (ap->frames != NULL) {
5843 memmove(p, ap->frames, sizeof(Vector) * ap->nframes);
5844 p += sizeof(Vector) * ap->nframes;
5850 /* Array of connects */
5851 if (nconnects > 0) {
5852 len = 8 + sizeof(Int) + sizeof(Int) * nconnects;
5853 ptr = (char *)realloc(ptr, len_all + len);
5857 memmove(p, "EXTCON\0\0", 8);
5858 *((Int *)(p + 8)) = sizeof(Int) * nconnects;
5859 p += 8 + sizeof(Int);
5860 for (i = 0; i < mp->natoms; i++) {
5861 ap = ATOM_AT_INDEX(mp->atoms, i);
5862 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5863 memmove(p, ap->connect.u.ptr, sizeof(Int) * ap->connect.count);
5864 p += sizeof(Int) * ap->connect.count;
5870 /* Bonds, angles, dihedrals, impropers */
5871 if (mp->nbonds > 0) {
5872 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nbonds;
5873 ptr = (char *)realloc(ptr, len_all + len);
5877 memmove(p, "BOND\0\0\0\0", 8);
5878 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nbonds;
5879 p += 8 + sizeof(Int);
5880 memmove(p, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
5883 if (mp->nangles > 0) {
5884 len = 8 + sizeof(Int) + sizeof(Int) * 3 * mp->nangles;
5885 ptr = (char *)realloc(ptr, len_all + len);
5889 memmove(p, "ANGLE\0\0\0", 8);
5890 *((Int *)(p + 8)) = sizeof(Int) * 3 * mp->nangles;
5891 p += 8 + sizeof(Int);
5892 memmove(p, mp->angles, sizeof(Int) * 3 * mp->nangles);
5895 if (mp->ndihedrals > 0) {
5896 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->ndihedrals;
5897 ptr = (char *)realloc(ptr, len_all + len);
5901 memmove(p, "DIHED\0\0\0", 8);
5902 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->ndihedrals;
5903 p += 8 + sizeof(Int);
5904 memmove(p, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
5907 if (mp->nimpropers > 0) {
5908 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->nimpropers;
5909 ptr = (char *)realloc(ptr, len_all + len);
5913 memmove(p, "IMPROP\0\0", 8);
5914 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->nimpropers;
5915 p += 8 + sizeof(Int);
5916 memmove(p, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
5920 /* Array of residues */
5921 if (mp->nresidues > 0) {
5922 len = 8 + sizeof(Int) + 4 * mp->nresidues;
5923 ptr = (char *)realloc(ptr, len_all + len);
5927 memmove(p, "RESIDUE\0", 8);
5928 *((Int *)(p + 8)) = 4 * mp->nresidues;
5929 p += 8 + sizeof(Int);
5930 memmove(p, mp->residues, 4 * mp->nresidues);
5935 if (mp->cell != NULL) {
5936 len = 8 + sizeof(Int) + sizeof(XtalCell);
5937 ptr = (char *)realloc(ptr, len_all + len);
5941 memmove(p, "CELL\0\0\0\0", 8);
5942 *((Int *)(p + 8)) = sizeof(XtalCell);
5943 p += 8 + sizeof(Int);
5944 memmove(p, mp->cell, sizeof(XtalCell));
5948 /* Symmetry operations */
5949 if (mp->nsyms > 0) {
5950 len = 8 + sizeof(Int) + sizeof(Transform) * mp->nsyms;
5951 ptr = (char *)realloc(ptr, len_all + len);
5955 memmove(p, "SYMOP\0\0\0", 8);
5956 *((Int *)(p + 8)) = sizeof(Transform) * mp->nsyms;
5957 p += 8 + sizeof(Int);
5958 memmove(p, mp->syms, sizeof(Transform) * mp->nsyms);
5964 /* Estimate the necessary storage first */
5965 /* One entry consists of { atom_index (Int), number_of_connects (Int), connects (Int's), weights (Double's) } */
5966 len = 8 + sizeof(Int);
5967 for (i = 0; i < mp->natoms; i++) {
5968 ap = ATOM_AT_INDEX(mp->atoms, i);
5969 if (ap->anchor != NULL)
5970 len += sizeof(Int) * 2 + (sizeof(Int) + sizeof(Double)) * ap->anchor->connect.count;
5972 ptr = (char *)realloc(ptr, len_all + len);
5976 memmove(p, "ANCHOR\0\0", 8);
5977 *((Int *)(p + 8)) = len - (8 + sizeof(Int));
5978 p += 8 + sizeof(Int);
5979 for (i = 0; i < mp->natoms; i++) {
5981 ap = ATOM_AT_INDEX(mp->atoms, i);
5982 if (ap->anchor != NULL) {
5983 count = ap->anchor->connect.count;
5985 *((Int *)(p + sizeof(Int))) = count;
5986 p += sizeof(Int) * 2;
5987 ip = AtomConnectData(&(ap->anchor->connect));
5988 memmove(p, ip, sizeof(Int) * count);
5989 p += sizeof(Int) * count;
5990 memmove(p, ap->anchor->coeffs, sizeof(Double) * count);
5991 p += sizeof(Double) * count;
5998 if (mp->par != NULL) {
6000 for (type = kFirstParType; type <= kLastParType; type++) {
6001 const char *parname;
6002 Int parsize, parcount;
6006 parname = "BONDPAR\0";
6007 parsize = sizeof(BondPar);
6008 parcount = mp->par->nbondPars;
6009 parptr = mp->par->bondPars;
6012 parname = "ANGPAR\0\0";
6013 parsize = sizeof(AnglePar);
6014 parcount = mp->par->nanglePars;
6015 parptr = mp->par->anglePars;
6017 case kDihedralParType:
6018 parname = "DIHEPAR\0";
6019 parsize = sizeof(TorsionPar);
6020 parcount = mp->par->ndihedralPars;
6021 parptr = mp->par->dihedralPars;
6023 case kImproperParType:
6024 parname = "IMPRPAR\0";
6025 parsize = sizeof(TorsionPar);
6026 parcount = mp->par->nimproperPars;
6027 parptr = mp->par->improperPars;
6030 parname = "VDWPAR\0\0";
6031 parsize = sizeof(VdwPar);
6032 parcount = mp->par->nvdwPars;
6033 parptr = mp->par->vdwPars;
6035 case kVdwPairParType:
6036 parname = "VDWPPAR\0";
6037 parsize = sizeof(VdwPairPar);
6038 parcount = mp->par->nvdwpPars;
6039 parptr = mp->par->vdwpPars;
6041 case kVdwCutoffParType:
6042 parname = "VCUTPAR\0";
6043 parsize = sizeof(VdwCutoffPar);
6044 parcount = mp->par->nvdwCutoffPars;
6045 parptr = mp->par->vdwCutoffPars;
6051 len = 8 + sizeof(Int) + parsize * parcount;
6052 ptr = (char *)realloc(ptr, len_all + len);
6056 memmove(p, parname, 8);
6057 *((Int *)(p + 8)) = parsize * parcount;
6058 p += 8 + sizeof(Int);
6059 memmove(p, parptr, parsize * parcount);
6067 time_t tm = time(NULL);
6068 len = 8 + sizeof(Int) + sizeof(Int);
6069 ptr = (char *)realloc(ptr, len_all + len);
6073 memmove(p, "TIME\0\0\0\0", 8);
6074 *((Int *)(p + 8)) = sizeof(Int);
6075 p += 8 + sizeof(Int);
6076 *((Int *)p) = (Int)tm;
6082 if (outLength != NULL)
6083 *outLength = len_all;
6087 Panic("Low memory while serializing a molecule data");
6088 return NULL; /* Not reached */
6091 #pragma mark ====== Search for bonds, angles, dihedrals, impropers ======
6094 sMoleculeSearchIncludingAtoms(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
6098 IntGroup *gp = NULL;
6099 if (atomgroup == NULL)
6101 for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
6102 for (j = 0; j < nsize; j++) {
6103 if (IntGroupLookup(atomgroup, ip[j], NULL) != 0) {
6106 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
6107 Panic("Low memory while searching %s", msg);
6116 MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6120 return sMoleculeSearchIncludingAtoms(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
6124 MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6128 return sMoleculeSearchIncludingAtoms(mp->nangles, mp->angles, 3, atomgroup, "angles");
6132 MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6136 return sMoleculeSearchIncludingAtoms(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
6140 MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6144 return sMoleculeSearchIncludingAtoms(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
6148 sMoleculeSearchAcrossAtomGroup(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
6152 IntGroup *gp = NULL;
6153 if (atomgroup == NULL)
6155 for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
6157 for (j = 0; j < nsize; j++) {
6159 kk = (IntGroupLookup(atomgroup, ip[j], NULL) != 0);
6163 /* This bond etc. crosses the atom group border */
6166 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
6167 Panic("Low memory while searching %s", msg);
6176 MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6180 return sMoleculeSearchAcrossAtomGroup(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
6184 MoleculeSearchAnglesAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6188 return sMoleculeSearchAcrossAtomGroup(mp->nangles, mp->angles, 3, atomgroup, "angles");
6192 MoleculeSearchDihedralsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6196 return sMoleculeSearchAcrossAtomGroup(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
6200 MoleculeSearchImpropersAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6204 return sMoleculeSearchAcrossAtomGroup(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
6207 /* Subroutine for MoleculeGuessBonds. It can be also used independently, but make sure that *outNbonds / *outBonds
6208 _correctly_ represents an array of two integers (as in mp->nbonds/mp->bonds). */
6209 /* Find atoms within the given "distance" from the given position. */
6210 /* If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
6211 the threshold distance is given by the sum of van der Waals radii times limit, and radius is
6212 the van der Waals radius of the atom at the given position. */
6213 /* Index is the atom index of the given atom; it is only used in returning the "bond" array
6214 to the caller. If index is negative, then (-index) is the real atom index, and
6215 only atoms with lower indices than (-index) are looked for. */
6217 MoleculeFindCloseAtoms(Molecule *mp, const Vector *vp, Double radius, Double limit, Int *outNbonds, Int **outBonds, Int index)
6219 Int n2, j, nlim, newbond[2];
6223 nlim = index = -index;
6227 for (j = 0; j < nlim; j++) {
6228 Atom *bp = ATOM_AT_INDEX(mp->atoms, j);
6231 n2 = bp->atomicNumber;
6232 if (n2 >= 0 && n2 < gCountElementParameters)
6233 a2 = gElementParameters[n2].radius;
6234 else a2 = gElementParameters[6].radius;
6236 VecSub(dr, *vp, r2);
6240 alim = limit * (radius + a2);
6241 if (VecLength2(dr) < alim * alim) {
6244 /* MoleculeAddBonds(mp, 1, newbonds); */
6245 AssignArray(outBonds, outNbonds, sizeof(Int) * 2, *outNbonds, newbond);
6251 /* Guess the bonds from the coordinates */
6252 /* If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
6253 the threshold distance is given by the sum of van der Waals radii times limit. */
6255 MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds)
6257 Int nbonds, *bonds, i, newbond[2];
6263 for (i = 1, ap = ATOM_NEXT(mp->atoms); i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6265 Int an = ap->atomicNumber;
6267 if (an >= 0 && an < gCountElementParameters)
6268 rad = gElementParameters[an].radius;
6269 else rad = gElementParameters[6].radius;
6270 MoleculeFindCloseAtoms(mp, &r, rad, limit, &nbonds, &bonds, -i);
6273 newbond[0] = kInvalidIndex;
6275 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
6278 if (outNbonds != NULL)
6279 *outNbonds = nbonds;
6280 if (outBonds != NULL)
6285 /* Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information */
6287 MoleculeRebuildTablesFromConnects(Molecule *mp)
6289 int i, j, k, retval;
6296 if (mp->nbonds == 0) {
6297 for (i = 0; i < mp->natoms; i++) {
6298 ap = ATOM_AT_INDEX(mp->atoms, i);
6299 cp = AtomConnectData(&ap->connect);
6300 for (j = 0; j < ap->connect.count; j++) {
6306 /* MoleculeAddBonds() should not be used, because it assumes connects[] and
6307 bonds are already in sync */
6308 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
6309 /* retval = MoleculeAddBonds(mp, 1, ibuf);
6317 if (mp->nangles == 0) {
6318 for (i = 0; i < mp->natoms; i++) {
6319 ap = ATOM_AT_INDEX(mp->atoms, i);
6320 cp = AtomConnectData(&ap->connect);
6321 for (j = 0; j < ap->connect.count; j++) {
6322 for (k = j + 1; k < ap->connect.count; k++) {
6327 retval = MoleculeAddAngles(mp, ibuf, NULL);
6335 /* Find dihedrals */
6336 if (mp->ndihedrals == 0) {
6337 for (i = 0; i < mp->natoms; i++) {
6338 ap = ATOM_AT_INDEX(mp->atoms, i);
6339 cp = AtomConnectData(&ap->connect);
6340 for (j = 0; j < ap->connect.count; j++) {
6347 apjj = ATOM_AT_INDEX(mp->atoms, jj);
6348 cpjj = AtomConnectData(&apjj->connect);
6349 for (k = 0; k < ap->connect.count; k++) {
6353 for (m = 0; m < apjj->connect.count; m++) {
6355 if (mm == i || mm == kk)
6362 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
6371 /* Find impropers */
6372 if (mp->nimpropers == 0) {
6373 for (i = 0; i < mp->natoms; i++) {
6374 int i1, i2, i4, n1, n2, n4;
6375 ap = ATOM_AT_INDEX(mp->atoms, i);
6376 cp = AtomConnectData(&ap->connect);
6377 for (i1 = 0; i1 < ap->connect.count; i1++) {
6379 for (i2 = i1 + 1; i2 < ap->connect.count; i2++) {
6381 for (i4 = i2 + 1; i4 < ap->connect.count; i4++) {
6388 retval = MoleculeAddImpropers(mp, ibuf, NULL);
6397 mp->needsMDRebuild = 1;
6398 __MoleculeUnlock(mp);
6402 __MoleculeUnlock(mp);
6407 MoleculeAreAtomsConnected(Molecule *mol, int idx1, int idx2)
6409 Atom *ap1 = ATOM_AT_INDEX(mol->atoms, idx1);
6410 if (AtomConnectHasEntry(&ap1->connect, idx2))
6412 else if (ap1->anchor != NULL && AtomConnectHasEntry(&(ap1->anchor->connect), idx2))
6417 #pragma mark ====== Atom names ======
6419 /* Look for the n1-th atom in resno-th residue (n1 is 0-based) */
6421 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
6425 if (mp == NULL || mp->natoms == 0)
6428 for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6429 if (ap->resSeq == resno) {
6436 return lasti; /* max */
6441 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
6445 n = strtol(s, &p, 0);
6457 if ((p = strchr(s, ':')) != NULL) {
6458 /* Residue is specified */
6460 if ((pp = strchr(s, '.')) != NULL && pp < p) {
6461 /* Residue number is also specified */
6464 *resSeq = strtol(pp + 1, &ppp, 0);
6466 return -2; /* Bad format */
6467 while (isspace(*ppp))
6470 return -2; /* Bad format */
6473 /* Check whether the "residue name" is an integer */
6474 n = strtol(s, &pp, 0);
6476 while (isspace(*pp))
6478 if (*pp == 0 || *pp == ':') {
6481 return -2; /* Bad format */
6489 if (n >= sizeof(resName))
6490 n = sizeof(resName) - 1;
6491 strncpy(resName, s, n);
6499 strncpy(atomName, p, 4);
6504 /* Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer */
6506 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
6513 n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
6514 if (atomName[0] == 0) {
6515 if (n >= mp->natoms)
6516 n = -1; /* Out of range */
6519 for (n = 0; n < mp->natoms; n++) {
6520 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
6521 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
6522 && (resSeq < 0 || ap->resSeq == resSeq)
6523 && strncmp(atomName, ap->aname, 4) == 0) {
6527 return -1; /* Not found */
6531 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
6535 if (mp == NULL || index < 0 || index >= mp->natoms) {
6539 ap = mp->atoms + index;
6540 if (ap->resSeq != 0) {
6541 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
6545 snprintf(buf, bufsize, "%.4s", ap->aname);
6548 #pragma mark ====== Selection ======
6551 sMoleculeNotifyChangeSelection(Molecule *mp)
6553 /* TODO: Finer control of notification types may be necessary */
6554 MoleculeCallback_notifyModification(mp, 0);
6558 MoleculeSetSelection(Molecule *mp, IntGroup *select)
6563 IntGroupRetain(select);
6564 if (mp->selection != NULL)
6565 IntGroupRelease(mp->selection);
6566 mp->selection = select;
6567 sMoleculeNotifyChangeSelection(mp);
6571 MoleculeGetSelection(Molecule *mp)
6575 else return mp->selection;
6579 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
6581 if (mp->selection == NULL)
6582 mp->selection = IntGroupNew();
6584 IntGroupClear(mp->selection);
6585 IntGroupAdd(mp->selection, n1, 1);
6586 sMoleculeNotifyChangeSelection(mp);
6590 MoleculeUnselectAtom(Molecule *mp, int n1)
6592 if (mp->selection != NULL)
6593 IntGroupRemove(mp->selection, n1, 1);
6594 sMoleculeNotifyChangeSelection(mp);
6598 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
6600 if (mp->selection == NULL)
6601 mp->selection = IntGroupNew();
6602 IntGroupReverse(mp->selection, n1, 1);
6603 sMoleculeNotifyChangeSelection(mp);
6607 MoleculeIsAtomSelected(Molecule *mp, int n1)
6609 if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
6615 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
6617 if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
6623 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
6626 IntGroup *remain, *ig1, *ig2;
6628 remain = IntGroupNewFromIntGroup(remove);
6632 status = IntGroupReverse(remain, 0, mp->natoms);
6634 ig1 = IntGroupNew();
6638 status = IntGroupDifference(selection, remove, ig1);
6641 ig2 = IntGroupNew();
6645 status = IntGroupDeconvolute(ig1, remain, ig2);
6648 IntGroupRelease(remain);
6650 IntGroupRelease(ig1);
6655 IntGroupRelease(ig2);
6660 #pragma mark ====== Atom Equivalence ======
6664 struct sEqList *next;
6665 struct sEqList *link;
6668 static struct sEqList *sListBase = NULL;
6669 static struct sEqList *sListFree = NULL;
6671 static struct sEqList *
6675 if (sListFree != NULL) {
6677 sListFree = lp->next;
6678 lp->i[0] = lp->i[1] = 0;
6682 lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
6683 lp->link = sListBase;
6689 sFreeEqList(struct sEqList *list)
6691 list->next = sListFree;
6696 sDeallocateEqLists(void)
6698 struct sEqList *lp, *lp_link;
6699 for (lp = sListBase; lp != NULL; lp = lp_link) {
6708 sExistInEqList(int i, int idx, struct sEqList *list)
6710 while (list != NULL) {
6711 if (list->i[idx] == i)
6718 static struct sEqList *
6719 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
6722 struct sEqList *list1, *list2;
6723 Int ii, jj, ni, nj, *cpi, *cpj;
6724 api = ATOM_AT_INDEX(mol->atoms, i);
6725 apj = ATOM_AT_INDEX(mol->atoms, j);
6726 if (api->atomicNumber != apj->atomicNumber)
6728 list1 = sAllocEqList();
6734 if (i == j || (db[i] != NULL && db[i] == db[j]))
6736 cpi = AtomConnectData(&api->connect);
6737 cpj = AtomConnectData(&apj->connect);
6738 for (ni = 0; ni < api->connect.count; ni++) {
6740 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
6742 if (sExistInEqList(ii, 0, list1))
6745 for (nj = 0; nj < apj->connect.count; nj++) {
6747 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6749 if (sExistInEqList(jj, 1, list1))
6751 list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
6755 if (list2 == NULL) {
6757 return NULL; /* No equivalent to ii */
6759 list1 = list2; /* ii is OK, try next */
6765 sDBInclude(Int *ip, int i)
6770 for (j = ip[0] - 1; j >= 0; j--) {
6778 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
6780 Int **db; /* List of equivalents for each atom */
6782 Atom *api, *apj, *apk;
6783 Int *cpi, *cpj, *ibuf, nibuf;
6784 int i, j, k, ii, jj, kk;
6785 if (mol == NULL || mol->natoms == 0)
6787 db = (Int **)calloc(sizeof(Int *), mol->natoms);
6791 /* Find the equivalent univalent atoms */
6792 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6793 if (api->connect.count < 2)
6795 cpi = AtomConnectData(&api->connect);
6796 for (j = 0; j < api->connect.count; j++) {
6800 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6802 AssignArray(&ibuf, &nibuf, sizeof(Int), n, &jj);
6804 apj = ATOM_AT_INDEX(mol->atoms, jj);
6805 if (apj->connect.count != 1 || db[jj] != NULL)
6807 cpj = AtomConnectData(&apj->connect);
6808 for (k = j + 1; k < api->connect.count; k++) {
6810 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
6812 apk = ATOM_AT_INDEX(mol->atoms, kk);
6813 if (apk->connect.count != 1 || db[kk] != NULL)
6815 if (apj->atomicNumber == apk->atomicNumber) {
6816 AssignArray(&ibuf, &nibuf, sizeof(Int), n, &kk);
6821 ip = (Int *)calloc(sizeof(Int), n + 1);
6825 memmove(ip + 1, ibuf, sizeof(Int) * n);
6826 for (k = 0; k < n; k++)
6836 /* Try matching (i,j) pair */
6837 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6838 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
6840 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
6841 struct sEqList *list;
6842 if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
6844 if (api->atomicNumber != apj->atomicNumber)
6845 continue; /* Different elements do not match */
6846 if (db[i] != NULL && db[i] == db[j])
6847 continue; /* Already equivalent */
6848 list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
6850 continue; /* (i,j) do not match */
6851 while (list != NULL) {
6854 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
6855 /* Merge db[ii] and db[jj] */
6856 k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
6857 ip = (Int *)calloc(sizeof(Int), k + 1);
6859 return NULL; /* Out of memory */
6860 if (db[ii] == NULL) {
6864 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
6867 if (db[jj] == NULL) {
6870 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
6879 for (k = 0; k < ip[0]; k++)
6883 printf("(%d,%d) matched: ", ii, jj);
6884 for (k = 0; k < ip[0]; k++) {
6885 printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
6895 /* Record the equivalent atoms with the lowest index for each atom */
6896 result = (Int *)calloc(sizeof(Int), mol->natoms);
6897 for (i = 0; i < mol->natoms; i++)
6899 for (i = 0; i < mol->natoms; i++) {
6900 if (result[i] >= 0 || (ip = db[i]) == NULL)
6903 for (j = 0; j < ip[0]; j++) {
6908 for (j = 0; j < ip[0]; j++) {
6909 result[ip[j + 1]] = k;
6910 db[ip[j + 1]] = NULL;
6914 sDeallocateEqLists();
6918 #pragma mark ====== Symmetry expansion ======
6921 MoleculeGetTransformForSymop(Molecule *mp, Symop symop, Transform *tf, int is_cartesian)
6924 if (mp == NULL || mp->cell == NULL)
6926 if (symop.sym >= mp->nsyms && symop.sym != 0)
6928 memmove(*tf, SYMMETRY_AT_INDEX(mp->syms, symop.sym), sizeof(Transform));
6929 (*tf)[9] += symop.dx;
6930 (*tf)[10] += symop.dy;
6931 (*tf)[11] += symop.dz;
6933 TransformMul(t, *tf, mp->cell->rtr);
6934 TransformMul(*tf, mp->cell->tr, t);
6940 MoleculeGetSymopForTransform(Molecule *mp, const Transform tf, Symop *symop, int is_cartesian)
6944 if (mp == NULL || mp->cell == NULL)
6947 TransformMul(t, tf, mp->cell->tr);
6948 TransformMul(t, mp->cell->rtr, t);
6950 memmove(t, tf, sizeof(Transform));
6952 for (i = 0; i < mp->nsyms || i == 0; i++) {
6953 Transform *tp = &(SYMMETRY_AT_INDEX(mp->syms, i));
6954 for (j = 0; j < 9; j++) {
6955 if (fabs((*tp)[j] - t[j]) > 1e-4)
6959 for (j = 9; j < 12; j++) {
6960 double f1 = t[j] - (*tp)[j];
6961 double f2 = floor(f1 + 0.5);
6962 if (fabs(f1 - f2) > 1e-4)
6972 symop->alive = (SYMOP_ALIVE((*symop)) != 0);
6977 return -3; /* Not found */
6981 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
6985 if (symop.sym >= mp->nsyms && symop.sym != 0)
6987 if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
6988 TransformVec(vpout, mp->cell->rtr, vpin);
6989 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpout);
6990 vpout->x += symop.dx;
6991 vpout->y += symop.dy;
6992 vpout->z += symop.dz;
6993 TransformVec(vpout, mp->cell->tr, vpout);
6995 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpin);
6996 vpout->x += symop.dx;
6997 vpout->y += symop.dy;
6998 vpout->z += symop.dz;
7003 /* Add expanded atoms. Returns the number of newly created atoms.
7004 If indices is non-NULL, it should be an array of Int with at least
7005 IntGroupGetCount(group) entries, and on return it contains the
7006 indices of the expanded atoms (may be existing atoms if the expanded
7007 atoms are already present)
7008 If allowOverlap is non-zero, then the new atom is created even when the
7009 coordinates coincide with the some other atom (special position) of the
7010 same element; otherwise, such atom will not be created and the existing
7011 atom is returned in indices[]. */
7013 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group, Int *indices, Int allowOverlap)
7015 int i, n, n0, n1, n2, base, count, *table;
7017 IntGroupIterator iter;
7023 if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
7025 if (symop.sym != 0 && symop.sym >= mp->nsyms)
7028 /* Create atoms, with avoiding duplicates */
7029 n0 = n1 = mp->natoms;
7030 table = (int *)malloc(sizeof(int) * n0);
7033 for (i = 0; i < n0; i++)
7035 IntGroupIteratorInit(group, &iter);
7036 MoleculeGetTransformForSymop(mp, symop, &tr, 0);
7038 for (i = 0; i < count; i++) {
7039 n = IntGroupIteratorNext(&iter);
7040 ap = ATOM_AT_INDEX(mp->atoms, n);
7041 if (SYMOP_ALIVE(ap->symop)) {
7042 /* Calculate the cumulative symop */
7044 MoleculeGetTransformForSymop(mp, ap->symop, &t1, 0);
7045 TransformMul(tr2, tr, t1);
7046 if (MoleculeGetSymopForTransform(mp, tr2, &symop1, 0) != 0) {
7047 if (indices != NULL)
7049 continue; /* Skip this atom */
7057 /* Calculate the expande position */
7058 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
7060 /* Is this expansion already present? */
7061 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
7062 /* Symmetry operation and the base atom are the same */
7063 if (ap2->symbase == base && SYMOP_EQUAL(symop1, ap2->symop))
7065 /* Atomic number and the position are the same */
7066 if (ap2->atomicNumber == ap->atomicNumber && allowOverlap == 0) {
7067 VecSub(dr, ap2->r, nr);
7068 if (VecLength2(dr) < 1e-6)
7073 /* If yes, then skip it */
7074 if (indices != NULL)
7078 /* Create a new atom */
7080 AtomDuplicate(&newAtom, ap);
7081 MoleculeCreateAnAtom(mp, &newAtom, -1);
7082 AtomClean(&newAtom);
7083 ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
7085 ap2->symbase = base;
7086 ap2->symop = symop1;
7087 ap2->symop.alive = (symop1.dx != 0 || symop1.dy != 0 || symop1.dz != 0 || symop1.sym != 0);
7088 table[n] = n1; /* The index of the new atom */
7089 MoleculeSetAnisoBySymop(mp, n1); /* Recalculate anisotropic parameters according to symop */
7090 if (indices != NULL)
7095 IntGroupIteratorRelease(&iter);
7098 for (i = n0; i < n1; i++) {
7100 ap = ATOM_AT_INDEX(mp->atoms, i);
7101 if (SYMOP_ALIVE(ap->symop) && MoleculeGetTransformForSymop(mp, ap->symop, &tr, 1) == 0) {
7102 /* For each connected atom, look for the transformed atom */
7104 ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
7105 cp = AtomConnectData(&ap2->connect);
7106 n2 = ap2->connect.count;
7107 for (n = 0; n < n2; n++) {
7108 Atom *apn = ATOM_AT_INDEX(mp->atoms, cp[n]);
7110 TransformVec(&nr, tr, &nr);
7111 /* Look for the bonded atom transformed by ap->symop */
7112 for (j = 0, ap2 = mp->atoms; j < mp->natoms; j++, ap2 = ATOM_NEXT(ap2)) {
7113 if (ap2->symbase == cp[n] && SYMOP_EQUAL(ap->symop, ap2->symop))
7115 VecSub(dr, nr, ap2->r);
7116 if (ap2->atomicNumber == apn->atomicNumber && VecLength2(dr) < 1e-6)
7119 if (j < mp->natoms) {
7120 /* Bond i-j is created */
7123 if (MoleculeLookupBond(mp, b[0], b[1]) < 0)
7124 MoleculeAddBonds(mp, 1, b, NULL, 1);
7129 mp->needsMDRebuild = 1;
7130 __MoleculeUnlock(mp);
7132 return n1 - n0; /* The number of added atoms */
7135 /* Recalculate the coordinates of symmetry expanded atoms.
7136 (Also recalculate the positions of pi-anchor atoms)
7137 Returns the number of affected atoms.
7138 If group is non-NULL, only the expanded atoms whose base atoms are in the
7139 given group are considered.
7140 If groupout and vpout are non-NULL, the indices of the affected atoms
7141 and the original positions are returned (for undo operation).
7142 The pointers returned in *groupout and *vpout must be released and
7143 free()'ed by the caller */
7145 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
7150 IntGroup *ig = NULL;
7153 if (mp == NULL || mp->natoms == 0)
7158 if (mp->nsyms != 0) {
7159 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7160 if (!SYMOP_ALIVE(ap->symop))
7162 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
7164 bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
7165 MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
7166 VecSub(dr, nr, ap->r);
7167 if (VecLength2(dr) < 1e-20)
7169 if (groupout != NULL) {
7172 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
7175 IntGroupAdd(ig, i, 1);
7181 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7183 if (ap->anchor == NULL)
7185 if (group != NULL) {
7186 if (IntGroupLookup(group, i, NULL) == 0) {
7187 n = ap->anchor->connect.count;
7188 ip = AtomConnectData(&(ap->anchor->connect));
7189 for (j = 0; j < n; j++) {
7190 if (IntGroupLookup(group, ip[j], NULL) != 0)
7194 continue; /* This pi-anchor should not be modified */
7198 MoleculeCalculatePiAnchorPosition(mp, i);
7199 VecSub(dr, nr, ap->r);
7200 if (VecLength2(dr) < 1e-20) {
7201 ap->r = nr; /* No change */
7204 if (groupout != NULL) {
7207 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
7210 IntGroupAdd(ig, i, 1);
7214 mp->needsMDCopyCoordinates = 1;
7215 __MoleculeUnlock(mp);
7218 if (groupout != NULL && vpout != NULL) {
7220 *vpout = (Vector *)realloc(vp, sizeof(Vector) * count);
7222 IntGroupRelease(ig);
7226 if (groupout != NULL && vpout != NULL) {
7234 #pragma mark ====== Show/hide atoms ======
7237 sMoleculeNotifyChangeAppearance(Molecule *mp)
7239 /* TODO: Finer control of notification types may be necessary */
7240 MoleculeCallback_notifyModification(mp, 0);
7245 sMoleculeUnselectHiddenAtoms(Molecule *mp)
7248 if (mp == NULL || mp->selection == NULL)
7250 for (i = 0; i < mp->natoms; i++) {
7251 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7252 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
7253 IntGroupRemove(mp->selection, i, 1);
7255 sMoleculeNotifyChangeAppearance(mp);
7259 MoleculeShowAllAtoms(Molecule *mp)
7264 for (i = 0; i < mp->natoms; i++) {
7265 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7266 ap->exflags &= ~kAtomHiddenFlag;
7268 sMoleculeNotifyChangeAppearance(mp);
7273 MoleculeShowReverse(Molecule *mp)
7278 for (i = 0; i < mp->natoms; i++) {
7279 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7280 ap->exflags ^= kAtomHiddenFlag;
7282 sMoleculeUnselectHiddenAtoms(mp);
7283 sMoleculeNotifyChangeAppearance(mp);
7288 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
7291 if (mp == NULL || ig == NULL)
7293 for (i = 0; i < mp->natoms; i++) {
7294 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7295 if (ap->exflags & kAtomHiddenFlag)
7296 continue; /* Already hidden */
7297 if (IntGroupLookupPoint(ig, i) >= 0)
7298 ap->exflags |= kAtomHiddenFlag;
7300 sMoleculeUnselectHiddenAtoms(mp);
7301 sMoleculeNotifyChangeAppearance(mp);
7305 #pragma mark ====== Reversible Editing ======
7309 sMoleculeNotifyModification(Molecule *mp)
7311 ** TODO: Finer control of notification types may be necessary **
7312 MoleculeCallback_notifyModification(mp, 0);
7316 /* Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup */
7318 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
7321 if (where == NULL) {
7322 /* Append the new objects at the end */
7323 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
7326 n1 = IntGroupGetCount(where); /* Position to get new object */
7327 n2 = nobjs; /* Position to get old object */
7328 n3 = n1 + n2; /* Position to place new/old object */
7329 for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
7330 int start = IntGroupGetStartPoint(where, i);
7331 int end = IntGroupGetEndPoint(where, i);
7333 /* old[end-(n3-n2)..n2-1] is moved to old[end..n3-1] */
7334 memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
7335 n2 = end - (n3 - n2);
7338 /* new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1] */
7339 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
7346 /* Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
7348 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
7350 int n1, n2, n3, start, end, i;
7351 if (where == NULL || IntGroupGetCount(where) == 0)
7352 return 0; /* No operation */
7353 if (objs == NULL || nobjs == 0)
7354 return 1; /* Bad argument */
7355 n1 = 0; /* Position to move remaining elements to */
7356 n2 = 0; /* Position to move remaining elements from */
7357 n3 = 0; /* Position to move removed elements to */
7358 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
7359 end = IntGroupGetEndPoint(where, i);
7361 /* Move (start - n2) elements from objs[n2] to objs[n1] */
7363 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
7367 /* Move (end - start) elements from objs[n2] to clip[n3] */
7369 memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
7370 n3 += (end - start);
7371 n2 += (end - start);
7373 /* Move (nobjs - n2) elements from objs[n2] to objs[n1] */
7375 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
7379 /* Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
7381 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
7383 int n1, start, end, i;
7384 if (objs == NULL || where == NULL)
7385 return 1; /* Bad argument */
7386 n1 = 0; /* Position to move removed elements to */
7387 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
7388 end = IntGroupGetEndPoint(where, i);
7389 /* Copy (end - start) elements from objs[start] to clip[n1] */
7391 memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
7392 n1 += (end - start);
7397 /* Create a new atom with no bonding information. ap must _not_ be inside the given molecule
7398 (Use AtomDuplicate() first) */
7400 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
7404 if (mp == NULL || ap == NULL || mp->noModifyTopology)
7407 if (pos < 0 || pos >= mp->natoms)
7409 ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
7411 goto error; /* Out of memory */
7412 ap1 = ATOM_AT_INDEX(mp->atoms, pos);
7413 if (pos < mp->natoms - 1) {
7414 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
7416 if (AtomDuplicate(ap1, ap) == NULL) {
7417 /* Cannot duplicate: restore the original state */
7418 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
7422 ap1->connect.count = 0;
7423 if (ap1->resSeq >= mp->nresidues)
7424 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
7425 if (ap1->resName[0] == 0)
7426 strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
7427 if (ap1->segName[0] == 0)
7428 strncpy(ap1->segName, "MAIN", 4);
7429 if (pos < mp->natoms - 1) {
7430 /* Renumber the connect table, bonds, angles, etc. */
7431 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
7434 cp = AtomConnectData(&api->connect);
7435 for (j = 0; j < api->connect.count; j++) {
7439 if (api->anchor != NULL) {
7440 cp = AtomConnectData(&api->anchor->connect);
7441 for (j = 0; j < api->anchor->connect.count; j++) {
7447 for (i = 0; i < mp->nbonds * 2; i++) {
7448 if (mp->bonds[i] >= pos)
7451 for (i = 0; i < mp->nangles * 3; i++) {
7452 if (mp->angles[i] >= pos)
7455 for (i = 0; i < mp->ndihedrals * 4; i++) {
7456 if (mp->dihedrals[i] >= pos)
7459 for (i = 0; i < mp->nimpropers * 4; i++) {
7460 if (mp->impropers[i] >= pos)
7464 mp->nframes = -1; /* Should be recalculated later */
7465 MoleculeIncrementModifyCount(mp);
7466 mp->needsMDRebuild = 1;
7467 __MoleculeUnlock(mp);
7470 __MoleculeUnlock(mp);
7476 static int s_error_count;
7479 s_fprintf(FILE *fp, const char *fmt, ...)
7484 return vfprintf(fp, fmt, va);
7488 MoleculeCheckSanity(Molecule *mol)
7490 const char *fail = "Sanity check failure";
7491 Int i, j, *ip, c[4];
7494 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7495 if (ap->resSeq >= mol->nresidues)
7496 s_fprintf(stderr, "%s: atom %d residue %d but nresidues %d\n", fail, i, ap->resSeq, mol->nresidues);
7497 if (ap->type != 0 && ap->type < kAtomTypeMinimum)
7498 s_fprintf(stderr, "%s: atom %d atom type %d less than minimum\n", fail, i, ap->type);
7499 if (ap->atomicNumber < 0 || ap->atomicNumber > 113)
7500 s_fprintf(stderr, "%s: atom %d atomic number %d\n", fail, i, ap->atomicNumber);
7501 ip = AtomConnectData(&ap->connect);
7502 for (j = 0; j < ap->connect.count; j++) {
7503 if (ip[j] < 0 || ip[j] >= mol->natoms)
7504 s_fprintf(stderr, "%s: atom %d connect[%d] = %d out of range\n", fail, i, j, ip[j]);
7505 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[j])->connect), i) == 0)
7506 s_fprintf(stderr, "%s: atom %d has connect %d but atom %d has no connect %d\n", fail, i, ip[j], ip[j], i);
7509 for (i = 0, ip = mol->bonds; i < mol->nbonds; i++, ip += 2) {
7510 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms)
7511 s_fprintf(stderr, "%s: bond %d %d-%d out of range\n", fail, i, ip[0], ip[1]);
7512 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[0])->connect), ip[1]) == 0)
7513 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]);
7515 for (i = 0, ip = mol->angles; i < mol->nangles; i++, ip += 3) {
7516 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms)
7517 s_fprintf(stderr, "%s: angle %d %d-%d-%d out of range\n", fail, i, ip[0], ip[1], ip[2]);
7518 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
7520 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]);
7521 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
7523 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]);
7524 if (c[0] == 2 && c[1] == 2)
7525 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]);
7527 for (i = 0, ip = mol->dihedrals; i < mol->ndihedrals; i++, ip += 4) {
7528 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)
7529 s_fprintf(stderr, "%s: dihedral %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
7530 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
7531 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
7532 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
7534 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]);
7536 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]);
7538 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]);
7540 for (i = 0, ip = mol->impropers; i < mol->nimpropers; i++, ip += 4) {
7541 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)
7542 s_fprintf(stderr, "%s: improper %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
7543 c[0] = MoleculeAreAtomsConnected(mol, ip[2], ip[0]);
7544 c[1] = MoleculeAreAtomsConnected(mol, ip[2], ip[1]);
7545 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
7547 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]);
7549 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]);
7551 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]);
7553 return s_error_count;
7557 /* Merge two molecules. We use this procedure for all add-atom operations. */
7558 /* resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
7559 /* If nactions and actions are non-NULL, then the corresponding undo actions are created and returned. */
7560 /* If forUndo is non-zero, then only the atoms are inserted; other information should be inserted
7561 separately by other undo actions. */
7563 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, Int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7566 Int i, j, n1, n2, n3, n4, *cp;
7567 Int *new2old, *old2new;
7572 if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
7573 return 0; /* Do nothing */
7575 if (dst->noModifyTopology)
7576 return 1; /* Prohibited operation */
7578 if (where != NULL && IntGroupGetCount(where) != src->natoms)
7579 return 1; /* Bad parameter */
7581 if (nactions != NULL)
7583 if (actions != NULL)
7587 __MoleculeLock(dst);
7591 if (resSeqOffset < 0)
7594 /* Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
7595 and ndst..ndst+nsrc-1 are for atoms in src. */
7596 new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
7597 if (new2old == NULL)
7599 old2new = new2old + ndst + nsrc;
7600 n1 = 0; /* dst index */
7601 n2 = 0; /* src index */
7602 n3 = 0; /* "merged" index */
7604 while (n1 < ndst || n2 < nsrc) {
7605 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
7608 /* n4 elements from dst[n1] will go to merged[n3] */
7609 for (j = 0; j < n4; j++) {
7610 old2new[n1 + j] = n3 + j;
7611 new2old[n3 + j] = n1 + j;
7615 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
7617 /* n4 elements from src[n2] will go to merged[n3] */
7618 for (j = 0; j < n4; j++) {
7619 old2new[ndst + n2 + j] = n3 + j;
7620 new2old[n3 + j] = ndst + n2 + j;
7627 /* Expand the destination array */
7628 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
7631 /* Move the atoms */
7632 if (where == NULL) {
7633 /* Duplicate atoms to the end of the destination array */
7634 for (i = 0; i < nsrc; i++) {
7635 ap = ATOM_AT_INDEX(dst->atoms, ndst + i);
7636 if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7638 if (forUndo) /* For undo action, all bonds come from another undo action, so connection info are cleared */
7639 AtomConnectResize(&ap->connect, 0);
7642 /* Duplicate to a temporary storage and then insert */
7643 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
7644 if (tempatoms == NULL)
7646 for (i = 0; i < nsrc; i++) {
7647 ap = ATOM_AT_INDEX(tempatoms, i);
7648 if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7650 if (forUndo) /* See above */
7651 AtomConnectResize(&ap->connect, 0);
7653 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
7657 dst->natoms = ndst + nsrc;
7659 /* Renumber the atom indices in connect[] and symbase, and modify the residue numbers */
7660 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7661 if (new2old[i] < ndst) {
7662 /* This atom is from dst */
7665 /* This atom is from src */
7666 n1 = ndst; /* Offset to the internal number */
7667 if (ap->resSeq != 0)
7668 ap->resSeq += resSeqOffset; /* Modify residue number */
7670 cp = AtomConnectData(&ap->connect);
7671 for (j = 0; j < ap->connect.count; j++)
7672 cp[j] = old2new[cp[j] + n1];
7673 if (SYMOP_ALIVE(ap->symop))
7674 ap->symbase = old2new[ap->symbase + n1];
7675 if (ap->anchor != NULL) {
7676 cp = AtomConnectData(&ap->anchor->connect);
7677 for (j = 0; j < ap->anchor->connect.count; j++)
7678 cp[j] = old2new[cp[j] + n1];
7682 /* Move the bonds, angles, dihedrals, impropers */
7683 for (i = 0; i < 4; i++) {
7684 Int *nitems, *nitems_src;
7685 Int **items, **items_src;
7686 Int nsize; /* Number of Ints in one element */
7689 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
7691 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
7693 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
7695 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
7697 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
7698 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
7700 /* During undo, no bonds etc. are copied from src; they will be taken care later
7705 /* Keep the old number of entries in dst, because it is updated by AssignArray() */
7707 /* Also keep the old number of entries in src, in case src and dst point the same molecule */
7709 /* Expand the array */
7710 if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
7712 /* Copy the items */
7713 memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
7715 /* Copy the bond order info if present */
7716 Int nn1 = dst->nbondOrders;
7717 if (dst->bondOrders != NULL || src->bondOrders != NULL) {
7718 if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), dst->nbonds - 1, NULL) == NULL)
7720 memset(dst->bondOrders + nn1, 0, sizeof(Double) * (dst->nbonds - nn1));
7721 if (src->bondOrders != NULL)
7722 memmove(dst->bondOrders + n1, src->bondOrders, sizeof(Double) * n2);
7727 for (j = 0; j < n1 * nsize; j++)
7728 (*items)[j] = old2new[(*items)[j]];
7729 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
7730 (*items)[j] = old2new[(*items)[j] + ndst];
7731 if (forUndo == 0 && actions != NULL) {
7732 ig = IntGroupNewWithPoints(n1, n2, -1);
7734 case 0: act = MolActionNew(gMolActionDeleteBonds, ig); break;
7735 case 1: act = MolActionNew(gMolActionDeleteAngles, ig); break;
7736 case 2: act = MolActionNew(gMolActionDeleteDihedrals, ig); break;
7737 case 3: act = MolActionNew(gMolActionDeleteImpropers, ig); break;
7739 IntGroupRelease(ig);
7740 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7745 /* Renumber existing parameters */
7746 if (dst->par != NULL) {
7748 for (type = kFirstParType; type <= kLastParType; type++) {
7750 n1 = ParameterGetCountForType(dst->par, type);
7751 for (i = 0; i < n1; i++) {
7752 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7753 ParameterRenumberAtoms(type, up1, ndst, old2new);
7758 /* Merge parameters from src */
7759 if (src->par != NULL && forUndo == 0) {
7760 UnionPar *up1, *up2;
7762 if (dst->par == NULL)
7763 dst->par = ParameterNew();
7765 /* Renumber existing parameters */
7766 for (type = kFirstParType; type <= kLastParType; type++) {
7767 n1 = ParameterGetCountForType(dst->par, type);
7768 for (i = 0; i < n1; i++) {
7769 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7770 ParameterRenumberAtoms(type, up1, ndst, old2new);
7775 for (type = kFirstParType; type <= kLastParType; type++) {
7776 n1 = ParameterGetCountForType(src->par, type);
7777 n2 = ParameterGetCountForType(dst->par, type);
7780 /* Determine which parameter should be copied from src to dst */
7781 for (i = 0; i < n1; i++) {
7783 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
7784 n3 = ParameterGetAtomTypes(type, up1, types);
7785 for (j = 0; j < n3; j++) {
7786 /* If it includes explicit atom index, then it should be copied */
7787 if (types[j] < kAtomTypeMinimum) {
7788 IntGroupAdd(ig, i, 1);
7793 for (j = 0; j < n2; j++) {
7794 up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
7795 if (ParameterCompare(up1, up2, type))
7799 /* This is an unknown parameter; should be copied */
7800 IntGroupAdd(ig, i, 1);
7803 n1 = IntGroupGetCount(ig);
7806 up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
7809 /* Copy parameters and renumber indices if necessary */
7810 for (i = j = 0; i < n1; i++) {
7811 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
7815 ParameterRenumberAtoms(type, up1 + j, nsrc, old2new + ndst);
7818 /* Merge parameters */
7820 IntGroupAdd(ig, n2, j);
7821 if (ParameterInsert(dst->par, type, up1, ig) < j)
7823 if (actions != NULL) {
7824 act = MolActionNew(gMolActionDeleteParameters, type, ig);
7825 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7831 IntGroupRelease(ig);
7834 /* Copy the residues if necessary */
7835 /* src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
7836 However, 1+resSeqOffset should not overwrite the existing residue in dst;
7837 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1]. */
7839 n1 = dst->nresidues;
7840 if (1 + resSeqOffset < n1) {
7842 } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
7843 if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
7844 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
7846 memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
7847 if (nactions != NULL) {
7848 act = MolActionNew(gMolActionChangeNumberOfResidues, n1);
7849 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7855 MoleculeCleanUpResidueTable(dst);
7858 dst->nframes = -1; /* Should be recalculated later */
7860 MoleculeIncrementModifyCount(dst);
7861 dst->needsMDRebuild = 1;
7862 __MoleculeUnlock(dst);
7866 __MoleculeUnlock(dst);
7867 Panic("Low memory while adding atoms");
7868 return 1; /* Not reached */
7871 /* Unmerge the molecule. If necessary, the undo actions are stored in nactions/actions array.
7872 (The nactions/actions array must be initialized by the caller) */
7874 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag, Int *nactions, MolAction ***actions, Int forUndo)
7876 Int nsrc, ndst, nsrcnew;
7877 Int i, j, n1, n2, n3, n4, *cp;
7878 Int *new2old, *old2new;
7879 IntGroup *move_g, *del_g, *remain_g, *dst_par_g, *remove_par_g;
7885 if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
7892 if (src->noModifyTopology && moveFlag)
7893 return 1; /* Prohibit editing */
7895 if ((ndst = IntGroupGetCount(where)) > src->natoms)
7896 return 1; /* Bad parameter */
7898 __MoleculeLock(src);
7903 nsrcnew = nsrc - ndst;
7904 if (resSeqOffset < 0)
7907 /* Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
7908 and nsrcnew..nsrc-1 are for atoms moved into dst. */
7909 new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
7910 if (new2old == NULL)
7912 old2new = new2old + nsrc;
7913 n1 = 0; /* src index */
7914 n2 = 0; /* dst index */
7915 n3 = 0; /* src index after "unmerge" */
7917 while (n1 < nsrc || n2 < ndst) {
7918 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
7921 /* n4 elements from src[n1] will go to unmerged[n3] */
7922 for (j = 0; j < n4; j++) {
7923 old2new[n1 + j] = n3 + j;
7924 new2old[n3 + j] = n1 + j;
7928 if ((n4 = IntGroupGetInterval(where, i)) < 0)
7930 /* n4 elements from src[n1] will go to dst[n2] */
7931 for (j = 0; j < n4; j++) {
7932 old2new[n1 + j] = nsrcnew + n2 + j;
7933 new2old[nsrcnew + n2 + j] = n1 + j;
7940 /* Atoms to remain in the source group */
7942 remain_g = IntGroupNewWithPoints(0, nsrc, -1);
7943 IntGroupRemoveIntGroup(remain_g, where);
7944 } else remain_g = NULL;
7946 /* Find parameters to be moved to the dst (dst_par_g), and to be removed from the src (remove_par_g) */
7947 if (src->par != NULL) {
7948 dst_par_g = IntGroupNew();
7950 remove_par_g = IntGroupNew();
7951 else remove_par_g = NULL;
7952 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7953 n2 = ParameterGetCountForType(src->par, n1);
7956 for (i = 0; i < n2; i++) {
7957 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7958 if (ParameterIsRelevantToAtomGroup(n1, up, src->atoms, where)) {
7959 /* This parameter is to be copied to dst */
7960 IntGroupAdd(dst_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7962 if (moveFlag && !ParameterIsRelevantToAtomGroup(n1, up, src->atoms, remain_g)) {
7963 /* This parameter is to be removed */
7964 IntGroupAdd(remove_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7968 } else dst_par_g = remove_par_g = NULL;
7970 /* Pi anchors should be modified if the anchor and its component atoms become separated between
7973 Int ibufsize, *ibuf, flag_i, flag_j;
7975 ibuf = (Int *)malloc(sizeof(Int) * ibufsize);
7976 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7977 if (ap->anchor == NULL)
7979 flag_i = (old2new[i] < nsrcnew);
7980 cp = AtomConnectData(&ap->anchor->connect);
7981 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7982 flag_j = (old2new[cp[j]] < nsrcnew);
7983 if (flag_i == flag_j) {
7984 if (n1 >= ibufsize) {
7986 ibuf = (Int *)realloc(ibuf, sizeof(Int) * ibufsize);
7992 /* Need to modify the pi anchor list */
7995 MolActionCreateAndPerform(src, SCRIPT_ACTION("isI"), "set_atom_attr", i, "anchor_list", n1, ibuf);
8000 /* Make a new molecule */
8002 dst = MoleculeNew();
8005 /* Expand the destination array */
8006 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
8008 dst_ap = dst->atoms;
8011 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
8016 /* Move the atoms */
8018 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
8020 src->natoms = nsrcnew;
8022 /* The atom record must be deallocated correctly */
8023 for (i = 0; i < ndst; i++)
8024 AtomClean(ATOM_AT_INDEX(dst_ap, i));
8028 for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
8029 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
8034 /* The dummy destination array is no longer needed */
8039 /* Renumber the atom indices in connect[] (src) */
8041 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
8042 cp = AtomConnectData(&ap->connect);
8043 for (j = n1 = 0; j < ap->connect.count; j++) {
8044 n2 = old2new[cp[j]];
8048 AtomConnectResize(&ap->connect, n1);
8049 if (ap->anchor != NULL) {
8050 cp = AtomConnectData(&ap->anchor->connect);
8051 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
8052 n2 = old2new[cp[j]];
8056 if (n1 != ap->anchor->connect.count) {
8057 /* This should not happen!! */
8058 AtomConnectResize(&ap->anchor->connect, n1);
8059 fprintf(stderr, "Internal error in sMoleculeUnmergeSub (line %d)\n", __LINE__);
8061 free(ap->anchor->coeffs);
8070 /* Renumber the atom indices in connect[] (dst) */
8072 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
8073 if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
8074 ap->resSeq -= resSeqOffset;
8075 else ap->resSeq = 0;
8076 cp = AtomConnectData(&ap->connect);
8077 for (j = n1 = 0; j < ap->connect.count; j++) {
8078 n2 = old2new[cp[j]] - nsrcnew;
8082 AtomConnectResize(&ap->connect, n1);
8083 if (ap->anchor != NULL) {
8084 cp = AtomConnectData(&ap->anchor->connect);
8085 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
8086 n2 = old2new[cp[j]] - nsrcnew;
8090 if (n1 != ap->anchor->connect.count) {
8091 /* This can happen, and the anchor info is silently modified */
8093 AtomConnectResize(&ap->anchor->connect, 0);
8094 free(ap->anchor->coeffs);
8099 AtomConnectResize(&ap->anchor->connect, n1);
8101 for (j = 0; j < n1; j++)
8102 d += ap->anchor->coeffs[j];
8103 for (j = 0; j < n1; j++)
8104 ap->anchor->coeffs[j] /= d;
8105 MoleculeCalculatePiAnchorPosition(dst, i);
8112 /* Separate the bonds, angles, dihedrals, impropers */
8113 /* TODO: Improper torsions should also be copied! */
8114 move_g = IntGroupNew();
8117 for (i = 3; i >= 0; i--) {
8118 Int *nitems, *nitems_dst;
8119 Int **items, **items_dst;
8120 Int nsize; /* Number of Ints in one element */
8121 unsigned char *counts;
8122 del_g = IntGroupNew();
8125 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
8127 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
8129 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
8131 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
8133 nitems = NULL; items = NULL; nsize = 0; break; /* Not reached */
8136 nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
8137 items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
8142 counts = (unsigned char *)calloc(1, *nitems);
8143 /* Find the entries that should be moved to dst */
8145 for (j = 0; j < *nitems * nsize; j++) {
8146 n1 = old2new[(*items)[j]];
8148 counts[j / nsize]++; /* Count the atom belonging to dst */
8150 for (j = n2 = n3 = 0; j < *nitems; j++) {
8151 if (counts[j] > 0) {
8152 /* Remove from src */
8154 if (IntGroupAdd(del_g, j, 1) != 0)
8156 if (counts[j] == nsize) {
8159 if (IntGroupAdd(move_g, j, 1) != 0)
8165 /* Expand the destination array */
8166 if (items_dst != NULL && n3 > 0) {
8167 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
8169 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
8171 if (i == 0 && src->bondOrders != NULL) {
8172 if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), n3 - 1, NULL) == NULL)
8174 if (sCopyElementsFromArrayAtPositions(src->bondOrders, src->nbondOrders, dst->bondOrders, sizeof(Double), move_g) != 0)
8178 /* Remove from src */
8179 if (moveFlag && forUndo == 0) {
8180 if (nactions != NULL) {
8183 ip = (Int *)malloc(sizeof(Int) * nsize * n2);
8184 for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
8185 memmove(ip + j * nsize, *items + k * nsize, sizeof(Int) * nsize);
8186 if (i == 0 && src->bondOrders != NULL) {
8187 dp = (Double *)malloc(sizeof(Double) * n2);
8188 for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
8189 dp[j] = src->bondOrders[k];
8193 act = MolActionNew(gMolActionAddBondsForUndo, n2 * nsize, ip, del_g); break;
8195 act = MolActionNew(gMolActionAddAngles, n2 * nsize, ip, del_g); break;
8197 act = MolActionNew(gMolActionAddDihedrals, n2 * nsize, ip, del_g); break;
8199 act = MolActionNew(gMolActionAddImpropers, n2 * nsize, ip, del_g); break;
8202 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8207 act = MolActionNew(gMolActionAssignBondOrders, n2, dp, del_g);
8208 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8213 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
8218 /* Renumber the entries */
8220 for (j = 0; j < *nitems * nsize; j++) {
8221 (*items)[j] = old2new[(*items)[j]];
8224 if (items_dst != NULL) {
8225 for (j = 0; j < *nitems_dst * nsize; j++) {
8226 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
8230 IntGroupClear(move_g);
8231 IntGroupRelease(del_g);
8233 IntGroupRelease(move_g);
8235 /* Copy the residues */
8237 /* src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset) */
8238 n1 = src->nresidues - resSeqOffset; /* This will be dst->nresidues (if >0) */
8239 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
8242 memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
8246 /* Copy the parameters to dst */
8247 if (dst != NULL && dst_par_g != NULL && (n2 = IntGroupGetCount(dst_par_g)) > 0) {
8248 IntGroup *dst_new_g = IntGroupNew();
8249 Int dst_par_count[kLastParType - kFirstParType + 1];
8250 if (dst_new_g == NULL)
8252 for (i = 0; i <= kLastParType - kFirstParType; i++)
8253 dst_par_count[i] = 0;
8254 up = (UnionPar *)calloc(sizeof(UnionPar), n2);
8257 if (ParameterCopy(src->par, kFirstParType, up, dst_par_g) < n2)
8259 /* Renumber the explicit atom indices */
8260 for (i = 0; i < nsrc; i++)
8261 old2new[i] -= nsrcnew; /* new indices for atoms in dst; otherwise negative numbers */
8262 for (i = 0; i < n2; i++) {
8263 /* Renumber the indices, and count the number of parameters for each type */
8264 n1 = kFirstParType + IntGroupGetNthPoint(dst_par_g, i) / kParameterIndexOffset;
8265 dst_par_count[n1 - kFirstParType]++;
8266 ParameterRenumberAtoms(n1, up + i, nsrc, old2new);
8268 for (i = 0; i < nsrc; i++)
8269 old2new[i] += nsrcnew;
8270 if (dst->par == NULL)
8271 dst->par = ParameterNew();
8272 for (i = 0; i <= kLastParType - kFirstParType; i++) {
8273 if (dst_par_count[i] > 0)
8274 IntGroupAdd(dst_new_g, i * kParameterIndexOffset, dst_par_count[i]);
8276 if (ParameterInsert(dst->par, kFirstParType, up, dst_new_g) < n2)
8279 IntGroupRelease(dst_new_g);
8281 IntGroupRelease(dst_par_g);
8283 /* Remove the unused parameter. Note: the parameters that are in remove_par_g and not in
8284 dst_par_g will disappear. To support undo, these parameters should be taken care separately. */
8285 if (forUndo == 0 && remove_par_g != NULL && (n2 = IntGroupGetCount(remove_par_g)) > 0) {
8286 UnionPar *up = (UnionPar *)malloc(sizeof(UnionPar) * n2);
8287 ParameterDelete(src->par, kFirstParType, up, remove_par_g);
8288 if (nactions != NULL) {
8289 act = MolActionNew(gMolActionAddParameters, kFirstParType, remove_par_g, n2, up);
8290 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8295 IntGroupRelease(remove_par_g);
8297 /* Renumber the parameter records remaining in the src */
8299 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
8300 n2 = ParameterGetCountForType(src->par, n1);
8301 for (i = 0; i < n2; i++) {
8302 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
8303 ParameterRenumberAtoms(n1, up, nsrc, old2new);
8309 IntGroupRelease(remain_g);
8310 MoleculeCleanUpResidueTable(src);
8312 MoleculeCleanUpResidueTable(dst);
8315 src->nframes = -1; /* Should be recalculated later */
8317 dst->nframes = -1; /* Should be recalculated later */
8323 MoleculeIncrementModifyCount(src);
8324 src->needsMDRebuild = 1;
8325 __MoleculeUnlock(src);
8330 __MoleculeUnlock(src);
8331 /* Panic("Low memory while removing atoms"); */
8335 /* Separate molecule into two parts. The atoms specified by 'where' are moved
8336 from src to a new molecule, which is returned as *dstp. Dstp can be NULL,
8337 in which case the moved atoms are discarded. */
8339 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
8341 return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1, nactions, actions, forUndo);
8344 /* Extract atoms from a given molecule into two parts. The atoms specified by
8345 'where' are copied from src to a new molecule, which is returned as *dstp.
8346 If dummyFlag is non-zero, then the atoms that are not included in the group
8347 but are connected to any atoms in the group are converted to "dummy" atoms
8348 (i.e. with element "Du" and names beginning with an underscore) and included
8349 in the new molecule object. */
8351 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
8355 /* Extract the fragment */
8356 retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0, NULL, NULL, 0);
8362 /* Search bonds crossing the molecule border */
8363 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
8365 IntGroupIterator iter;
8368 IntGroupIteratorInit(ig, &iter);
8369 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8370 /* The atoms at the border */
8373 n1 = src->bonds[i*2];
8374 n2 = src->bonds[i*2+1];
8375 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
8379 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
8380 continue; /* Actually this is an internal error */
8382 /* n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule */
8383 /* Create a new dummy atom with the same segment/residue info with n1
8384 and the same position as n2 */
8385 ap = ATOM_AT_INDEX(src->atoms, n1);
8386 memset(&a, 0, gSizeOfAtomRecord);
8387 a.segSeq = ap->segSeq;
8388 memmove(a.segName, ap->segName, 4);
8389 a.resSeq = ap->resSeq;
8390 memmove(a.resName, ap->resName, 4);
8391 ElementToString(0, a.element); /* "Du" */
8392 snprintf(a.aname, 4, "_%d", idx++);
8393 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
8394 /* Add the dummy atom to the new molecule; nn[1] is the index
8395 of the new dummy atom in the new molecule */
8396 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
8397 /* Connect nn1 and nn2 */
8398 nn[2] = kInvalidIndex;
8399 MoleculeAddBonds(*dstp, 1, nn, NULL, 1);
8401 IntGroupIteratorRelease(&iter);
8402 IntGroupRelease(ig);
8410 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds, IntGroup *where, Int autoGenerate)
8412 Int nangles, ndihedrals;
8413 Int *angles, *dihedrals;
8414 Int i, j, k, kk, n1, n2, cn1, cn2;
8417 Atom *ap1, *ap2, *ap3;
8419 if (mp == NULL || bonds == NULL || nbonds <= 0)
8421 if (mp->noModifyTopology)
8422 return -4; /* Prohibited operation */
8424 /* Note: Duplicates and validity are not checked (the caller must do that) */
8429 if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, n1 + nbonds - 1, NULL) == NULL
8430 || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nbonds, sizeof(Int) * 2, where) != 0) {
8431 __MoleculeUnlock(mp);
8432 return -4; /* Out of memory */
8434 if (mp->bondOrders != NULL) {
8435 /* Expand the bond order info (all new entries are zero) */
8436 Double *dp = (Double *)calloc(sizeof(Double), nbonds);
8439 if (AssignArray(&(mp->bondOrders), &(mp->nbondOrders), sizeof(Double), n1 + nbonds - 1, NULL) == NULL
8440 || sInsertElementsToArrayAtPositions(mp->bondOrders, n1, dp, nbonds, sizeof(Double), where) != 0) {
8441 __MoleculeUnlock(mp);
8448 angles = dihedrals = NULL;
8449 nangles = ndihedrals = 0;
8451 /* Add connects[], and angles/dihedrals (if autoGenerate is true) */
8452 for (i = 0; i < nbonds; i++) {
8454 /* One entry at time */
8455 /* (Otherwise, duplicate entries of angles and dihedrals result) */
8457 n2 = bonds[i * 2 + 1];
8459 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
8460 AtomConnectInsertEntry(&ap1->connect, -1, n2);
8461 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
8462 AtomConnectInsertEntry(&ap2->connect, -1, n1);
8464 /* Add angles and dihedrals */
8466 AtomConnect *ac1, *ac2;
8467 if (ap1->anchor == NULL || ap2->anchor == NULL) {
8468 /* N1-N2-{XY} or N2-N1-{XY} angles (X: connected atom, Y: constitute atom of pi-anchor) */
8469 for (j = 0; j < 4; j++) {
8471 case 0: temp[0] = n1; temp[1] = n2; ac1 = &ap2->connect; break; /* N1-N2-X */
8472 case 1: if (ap2->anchor == NULL) continue; else ac1 = &ap2->anchor->connect; break; /* N1-N2-Y */
8473 case 2: temp[0] = n2; temp[1] = n1; ac1 = &ap1->connect; break; /* N2-N1-X */
8474 case 3: if (ap1->anchor == NULL) continue; else ac1 = &ap1->anchor->connect; break; /* N2-N1-Y */
8476 cp1 = AtomConnectData(ac1);
8478 for (k = 0; k < cn1; k++) {
8480 if (temp[2] == temp[0])
8482 ap3 = ATOM_AT_INDEX(mp->atoms, temp[2]);
8483 if (ap3->anchor != NULL) {
8484 /* Avoid X-anchor-anchor angle (anchor-X-anchor is allowed) */
8485 if ((j < 2 && ap2->anchor != NULL) || (j >= 2 && ap1->anchor != NULL))
8488 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
8490 /* Dihedrals N1-N2-X-{XY} or N2-N1-X-{XY} */
8491 if (j == 1 || j == 3)
8493 cp2 = AtomConnectData(&ap3->connect);
8494 for (kk = 0; kk < ap3->connect.count; kk++) {
8496 if (temp[3] == temp[0] || temp[3] == temp[1])
8498 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8501 if (ap3->anchor != NULL) {
8502 /* N1-N2-X-Y or N2-N1-X-Y */
8503 /* for Y, only the first constitute atom is considered */
8504 cp2 = AtomConnectData(&ap3->anchor->connect);
8506 if (temp[3] == temp[0] || temp[3] == temp[1])
8508 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8514 /* X-N1-N2-X dihedrals */
8515 /* Y-N1-N2-anchor is allowed, but the force may be zero if the angle N1-N2-anchor is */
8516 /* close to 180 deg (e.g. in ferrocene, C-anchor-Fe-anchor dihedral should be k=0) */
8517 if (ap1->anchor == NULL) {
8518 ac1 = &ap1->connect;
8521 ac1 = &ap1->anchor->connect;
8522 cn1 = 1; /* Only the first constitute atom of pi-anchor is considered */
8524 if (ap2->anchor == NULL) {
8525 ac2 = &ap2->connect;
8528 ac2 = &ap2->anchor->connect;
8529 cn2 = 1; /* Only the first constitute atom of pi-anchor is considered */
8533 cp1 = AtomConnectData(ac1);
8534 cp2 = AtomConnectData(ac2);
8535 for (j = 0; j < cn1; j++) {
8537 if (temp[0] == temp[2])
8539 for (k = 0; k < cn2; k++) {
8541 if (temp[3] == temp[0] || temp[3] == temp[1])
8543 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8550 if (angles != NULL) {
8551 temp[0] = kInvalidIndex;
8552 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
8554 MoleculeAddAngles(mp, angles, NULL);
8557 if (dihedrals != NULL) {
8558 temp[0] = kInvalidIndex;
8559 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8561 MoleculeAddDihedrals(mp, dihedrals, NULL);
8565 MoleculeIncrementModifyCount(mp);
8566 mp->needsMDRebuild = 1;
8567 __MoleculeUnlock(mp);
8572 __MoleculeUnlock(mp);
8573 Panic("Low memory while adding bonds");
8574 return -1; /* Not reached */
8578 /* The deleted angles and dihedrals are stored in outRemoval. */
8579 /* (*outRemoval) is an array of integers, containing:
8580 [0..na*3-1]: the angle indices
8581 [na*3..na*3+nd*4-1]: the dihedral indices
8582 [na*3+nd*4..na*3+nd*4+ni*4-1]: the improper indices
8583 *outRemovedPos is an intgroup denoting the positions of the removed angles/dihedrals/impropers.
8584 the angle indices are included as they are,
8585 the dihedral indices are offset by ATOMS_MAX_NUMBER,
8586 the improper indices are offset by ATOMS_MAX_NUMBER*2.
8587 Note: the removed bond indices are not returned, because the caller should already know them. */
8589 MoleculeDeleteBonds(Molecule *mp, Int *bonds, IntGroup *where, Int **outRemoved, IntGroup **outRemovedPos)
8591 Int i, j, n1, n2, nw;
8592 Int *ip, *jp, na, nd, ni;
8593 IntGroup *ag, *dg, *ig;
8595 IntGroupIterator iter;
8599 if (mp->noModifyTopology)
8600 return -4; /* Prohibited operation */
8604 /* Update connects[] */
8605 IntGroupIteratorInit(where, &iter);
8606 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8607 n1 = mp->bonds[i * 2];
8608 n2 = mp->bonds[i * 2 + 1];
8609 ap = ATOM_AT_INDEX(mp->atoms, n1);
8610 ip = AtomConnectData(&ap->connect);
8611 for (j = 0; j < ap->connect.count; j++) {
8613 AtomConnectDeleteEntry(&ap->connect, j);
8617 ap = ATOM_AT_INDEX(mp->atoms, n2);
8618 ip = AtomConnectData(&ap->connect);
8619 for (j = 0; j < ap->connect.count; j++) {
8621 AtomConnectDeleteEntry(&ap->connect, j);
8627 /* Remove bonds, angles, dihedrals, impropers */
8632 nw = IntGroupGetCount(where);
8633 jp = (Int *)malloc(sizeof(Int) * nw * 2);
8635 IntGroupIteratorReset(&iter);
8636 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8637 jp[j++] = mp->bonds[i * 2];
8638 jp[j++] = mp->bonds[i * 2 + 1];
8640 IntGroupIteratorRelease(&iter);
8642 for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8643 for (j = 0; j < nw; j++) {
8646 if ((ip[0] == n1 && ip[1] == n2)
8647 || (ip[1] == n1 && ip[0] == n2)
8648 || (ip[1] == n1 && ip[2] == n2)
8649 || (ip[2] == n1 && ip[1] == n2)) {
8650 if (IntGroupAdd(ag, i, 1) != 0)
8657 for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8658 for (j = 0; j < nw; j++) {
8661 if ((ip[0] == n1 && ip[1] == n2)
8662 || (ip[1] == n1 && ip[0] == n2)
8663 || (ip[1] == n1 && ip[2] == n2)
8664 || (ip[2] == n1 && ip[1] == n2)
8665 || (ip[2] == n1 && ip[3] == n2)
8666 || (ip[3] == n1 && ip[2] == n2)) {
8669 if (IntGroupAdd(dg, i, 1) != 0)
8676 for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8677 for (j = 0; j < nw; j++) {
8680 if ((ip[0] == n1 && ip[2] == n2)
8681 || (ip[1] == n1 && ip[2] == n2)
8682 || (ip[3] == n1 && ip[2] == n2)
8683 || (ip[0] == n2 && ip[2] == n1)
8684 || (ip[1] == n2 && ip[2] == n1)
8685 || (ip[3] == n2 && ip[2] == n1)) {
8688 if (IntGroupAdd(ig, i, 1) != 0)
8697 if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, bonds, sizeof(Int) * 2, where) != 0)
8699 mp->nbonds -= IntGroupGetCount(where);
8700 if (mp->nbonds == 0) {
8704 if (mp->bondOrders != NULL) {
8705 if (sRemoveElementsFromArrayAtPositions(mp->bondOrders, mp->nbondOrders, NULL, sizeof(Double), where) != 0)
8707 mp->nbondOrders -= IntGroupGetCount(where);
8708 if (mp->nbondOrders == 0) {
8709 free(mp->bondOrders);
8710 mp->bondOrders = NULL;
8713 if (na == 0 && nd == 0 && ni == 0)
8716 ip = (Int *)malloc(sizeof(Int) * (na * 3 + nd * 4 + ni * 4));
8718 MoleculeDeleteAngles(mp, ip, ag);
8720 MoleculeDeleteDihedrals(mp, ip + na * 3, dg);
8722 MoleculeDeleteImpropers(mp, ip + na * 3 + nd * 4, ig);
8724 IntGroupOffset(dg, ATOMS_MAX_NUMBER);
8725 IntGroupOffset(ig, ATOMS_MAX_NUMBER * 2);
8726 IntGroupAddIntGroup(ag, dg);
8727 IntGroupAddIntGroup(ag, ig);
8728 IntGroupRelease(dg);
8729 IntGroupRelease(ig);
8732 if (IntGroupGetCount(ag) == 0) {
8733 IntGroupRelease(ag);
8738 *outRemovedPos = ag;
8740 MoleculeIncrementModifyCount(mp);
8741 mp->needsMDRebuild = 1;
8742 __MoleculeUnlock(mp);
8744 return na * 3 + nd * 4 + ni * 4;
8747 __MoleculeUnlock(mp);
8748 Panic("Low memory while removing bonds");
8749 return -1; /* Not reached */
8753 MoleculeAssignBondOrders(Molecule *mp, const Double *orders, IntGroup *where)
8756 IntGroupIterator iter;
8757 if (mp == NULL || orders == NULL || mp->nbonds == 0)
8759 if (mp->noModifyTopology)
8760 return -4; /* Prohibited operation */
8761 if (mp->bondOrders == NULL) {
8762 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbonds - 1, NULL);
8763 memset(mp->bondOrders, 0, sizeof(Double) * mp->nbondOrders);
8765 IntGroupIteratorInit(where, &iter);
8767 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8768 if (i >= mp->nbondOrders)
8770 mp->bondOrders[i] = orders[j++];
8772 IntGroupIteratorRelease(&iter);
8777 MoleculeGetBondOrders(Molecule *mp, Double *outOrders, IntGroup *where)
8780 IntGroupIterator iter;
8781 if (mp == NULL || mp->nbonds == 0)
8783 if (mp->bondOrders == NULL) {
8784 /* Returns all zero */
8785 i = IntGroupGetCount(where);
8786 for (j = 0; j < i; j++)
8789 IntGroupIteratorInit(where, &iter);
8791 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8792 if (i < mp->nbondOrders)
8793 outOrders[j] = mp->bondOrders[i];
8794 else outOrders[j] = 0.0;
8802 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
8805 if (mp == NULL || angles == NULL)
8807 if (mp->noModifyTopology)
8808 return -4; /* Prohibited operation */
8812 nc = IntGroupGetCount(where);
8814 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
8820 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
8821 || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
8822 __MoleculeUnlock(mp);
8823 Panic("Low memory while adding angles");
8826 mp->needsMDRebuild = 1;
8827 __MoleculeUnlock(mp);
8832 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
8835 if (mp == NULL || where == NULL)
8837 if (mp->noModifyTopology)
8838 return -4; /* Prohibited operation */
8840 if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
8841 __MoleculeUnlock(mp);
8842 Panic("Bad argument while deleting angles");
8844 mp->nangles -= (nc = IntGroupGetCount(where));
8845 if (mp->nangles == 0) {
8849 mp->needsMDRebuild = 1;
8850 __MoleculeUnlock(mp);
8855 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
8858 if (mp == NULL || dihedrals == NULL)
8860 if (mp->noModifyTopology)
8861 return -4; /* Prohibited operation */
8863 nc = IntGroupGetCount(where);
8865 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
8871 n1 = mp->ndihedrals;
8873 if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8874 || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
8875 __MoleculeUnlock(mp);
8876 Panic("Low memory while adding dihedrals");
8878 mp->needsMDRebuild = 1;
8879 __MoleculeUnlock(mp);
8884 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
8887 if (mp == NULL || where == NULL)
8889 if (mp->noModifyTopology)
8890 return -4; /* Prohibited operation */
8892 if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
8893 __MoleculeUnlock(mp);
8894 Panic("Internal error: bad argument while deleting dihedrals");
8896 mp->ndihedrals -= (nc = IntGroupGetCount(where));
8897 if (mp->ndihedrals == 0) {
8898 free(mp->dihedrals);
8899 mp->dihedrals = NULL;
8901 mp->needsMDRebuild = 1;
8902 __MoleculeUnlock(mp);
8907 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
8910 if (mp == NULL || impropers == NULL)
8912 if (mp->noModifyTopology)
8913 return -4; /* Prohibited operation */
8915 nc = IntGroupGetCount(where);
8917 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
8923 n1 = mp->nimpropers;
8925 if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8926 || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
8927 __MoleculeUnlock(mp);
8928 Panic("Low memory while adding impropers");
8930 mp->needsMDRebuild = 1;
8931 __MoleculeUnlock(mp);
8936 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
8939 if (mp == NULL || where == NULL)
8941 if (mp->noModifyTopology)
8942 return -4; /* Prohibited operation */
8944 if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
8945 __MoleculeUnlock(mp);
8946 Panic("Internal error: bad argument while deleting impropers");
8948 mp->nimpropers -= (nc = IntGroupGetCount(where));
8949 if (mp->impropers == NULL) {
8950 free(mp->impropers);
8951 mp->impropers = NULL;
8953 __MoleculeUnlock(mp);
8958 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
8961 if (mp == NULL || mp->bonds == NULL)
8963 for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
8964 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
8971 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
8974 if (mp == NULL || mp->angles == NULL)
8976 for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8977 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
8978 (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
8985 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8988 if (mp == NULL || mp->dihedrals == NULL)
8990 for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8991 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
8992 (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
8999 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
9002 if (mp == NULL || mp->impropers == NULL)
9004 for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
9007 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
9008 (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
9009 (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
9015 /* Remove the bond at bondIndex and create two dummy atoms instead.
9016 The dummy atoms are placed at the end of atoms[], and the residue
9017 numbers are the same as the root atoms (i.e. the atoms to which
9018 the dummy atoms are connected). The indices are returned in
9019 dummyIndices[0,1]. */
9021 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
9023 Int roots[3], newBonds[5];
9029 if (mp == NULL || mp->noModifyTopology)
9031 if (bondIndex < 0 || bondIndex >= mp->nbonds)
9033 roots[0] = mp->bonds[bondIndex * 2];
9034 roots[1] = mp->bonds[bondIndex * 2 + 1];
9035 roots[2] = kInvalidIndex;
9036 rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
9037 rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
9038 VecSub(dr, rootp[0]->r, rootp[1]->r);
9039 for (i = 0; i < 2; i++) {
9042 memmove(nap, rootp[i], sizeof(na));
9043 nap->aname[0] = '*';
9044 strcpy(nap->element, "Du");
9046 nap->charge = nap->weight = 0.0;
9047 nap->atomicNumber = 0;
9048 nap->connect.count = 0;
9049 w = (i == 0 ? 0.4 : -0.4);
9050 VecScaleInc(nap->r, dr, w);
9057 /* Expand atoms array and append the dummy atoms at the end */
9059 natoms = mp->natoms;
9060 if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
9062 memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
9063 dummyIndices[0] = natoms;
9064 dummyIndices[1] = natoms + 1;
9066 /* Remove the old bond and create new bonds */
9067 ig = IntGroupNewWithPoints(bondIndex, 1, -1);
9070 MoleculeDeleteBonds(mp, NULL, ig, NULL, NULL);
9071 IntGroupRelease(ig);
9072 newBonds[0] = roots[0];
9073 newBonds[1] = dummyIndices[0];
9074 newBonds[2] = roots[1];
9075 newBonds[3] = dummyIndices[1];
9076 newBonds[4] = kInvalidIndex;
9078 i = (MoleculeAddBonds(mp, 2, newBonds, NULL, 1) < 0 ? -1 : 0);
9079 mp->needsMDRebuild = 1;
9080 __MoleculeUnlock(mp);
9084 __MoleculeUnlock(mp);
9085 Panic("Low memory during creating dummy atoms");
9089 /* Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
9090 a bond between the two root atoms. The value bondIndex is used as a
9091 hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
9092 the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
9093 is ignored and the new bond is stored at the end of bonds[]. */
9095 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
9102 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
9105 if (mol == NULL || mol->noModifyTopology)
9112 __MoleculeLock(mol);
9113 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
9114 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
9115 mol->needsMDRebuild = 1;
9116 __MoleculeUnlock(mol);
9123 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
9126 if (mol == NULL || mol->noModifyTopology)
9128 n1 = mol->ndihedrals;
9129 np1 = mol->dihedrals;
9130 mol->ndihedrals = 0;
9131 mol->dihedrals = NULL;
9132 if (ndihedrals > 0) {
9133 __MoleculeLock(mol);
9134 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
9135 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
9136 mol->needsMDRebuild = 1;
9137 __MoleculeUnlock(mol);
9139 *outDihedrals = np1;
9144 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
9147 if (mol == NULL || mol->noModifyTopology)
9149 n1 = mol->nimpropers;
9150 np1 = mol->impropers;
9151 mol->nimpropers = 0;
9152 mol->impropers = NULL;
9153 if (nimpropers > 0) {
9154 __MoleculeLock(mol);
9155 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
9156 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
9157 mol->needsMDRebuild = 1;
9158 __MoleculeUnlock(mol);
9160 *outImpropers = np1;
9166 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
9173 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
9174 return 0; /* molecule is empty */
9175 if (mol->noModifyTopology)
9179 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
9180 Int *cp = AtomConnectData(&ap->connect);
9181 if (ap->anchor != NULL)
9183 for (j = 0; j < ap->connect.count; j++) {
9185 if (ATOM_AT_INDEX(mol->atoms, j0)->anchor != NULL)
9187 for (k = j + 1; k < ap->connect.count; k++) {
9189 if (ATOM_AT_INDEX(mol->atoms, k0)->anchor != NULL)
9191 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
9192 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
9201 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
9205 if (outAngles != NULL)
9206 *outAngles = angles;
9211 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
9213 Int n1, n2, n3, n4, *ip, *cp2, *cp3;
9218 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
9219 return 0; /* molecule is empty */
9222 for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
9223 Int i1, i3, i4, *ip;
9224 if (ap2->anchor != NULL)
9226 cp2 = AtomConnectData(&ap2->connect);
9227 for (i3 = 0; i3 < ap2->connect.count; i3++) {
9231 ap3 = ATOM_AT_INDEX(mol->atoms, n3);
9232 if (ap3->anchor != NULL)
9234 cp3 = AtomConnectData(&ap3->connect);
9235 for (i1 = 0; i1 < ap2->connect.count; i1++) {
9239 if (ATOM_AT_INDEX(mol->atoms, n1)->anchor != NULL)
9241 for (i4 = 0; i4 < ap3->connect.count; i4++) {
9243 if (n2 == n4 || n1 == n4)
9245 if (ATOM_AT_INDEX(mol->atoms, n4)->anchor != NULL)
9247 if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
9248 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
9258 if (ndihedrals > 0) {
9259 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
9263 if (outDihedrals != NULL)
9264 *outDihedrals = dihedrals;
9269 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
9271 Int n1, n2, n3, n4, t1, t2, t3, t4, *ip, *cp;
9272 Parameter *par = mol->par;
9277 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
9278 return 0; /* molecule is empty */
9279 if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
9280 return 0; /* No improper parameters are defined */
9284 for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
9285 Int i1, i2, i4, found, *ip;
9287 cp = AtomConnectData(&ap3->connect);
9288 for (i1 = 0; i1 < ap3->connect.count; i1++) {
9290 t1 = ATOM_AT_INDEX(ap, n1)->type;
9291 for (i2 = i1 + 1; i2 < ap3->connect.count; i2++) {
9293 t2 = ATOM_AT_INDEX(ap, n2)->type;
9294 for (i4 = i2 + 1; i4 < ap3->connect.count; i4++) {
9296 t4 = ATOM_AT_INDEX(ap, n4)->type;
9298 if (ParameterLookupImproperPar(par, t1, t2, t3, t4, n1, n2, n3, n4, 0) != NULL)
9300 else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, -1, -1, -1, -1, 0) != NULL)
9302 if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
9303 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
9313 if (nimpropers > 0) {
9314 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
9318 if (outImpropers != NULL)
9319 *outImpropers = impropers;
9323 #pragma mark ====== Residues ======
9326 MoleculeCleanUpResidueTable(Molecule *mp)
9330 if (mp == NULL || mp->natoms == 0)
9334 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9335 if (ap->resSeq >= maxres)
9336 maxres = ap->resSeq + 1;
9337 if (ap->resSeq < mp->nresidues) {
9338 if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
9339 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
9341 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
9344 if (maxres < mp->nresidues)
9345 mp->nresidues = maxres;
9346 __MoleculeUnlock(mp);
9349 /* Change the number of residues. If nresidues is greater than the current value,
9350 then the array mp->residues is expanded with null names. If nresidues is smaller
9351 than the current value, mp->nresidues is set to the smallest possible value
9352 that is no smaller than nresidues and larger than any of the resSeq values. */
9354 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
9359 if (mp->nresidues == nresidues)
9361 else if (mp->nresidues < nresidues) {
9364 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
9365 while (n < nresidues)
9366 mp->residues[n++][0] = 0;
9367 __MoleculeUnlock(mp);
9373 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9374 if (ap->resSeq >= n)
9383 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
9385 IntGroupIterator iter;
9386 int withArray, resSeq, maxSeq;
9390 /* If LSB of resSeqs is 1, then a constant value is used for all specified atoms */
9391 if (((uintptr_t)resSeqs & 1) == 0) {
9396 resSeq = ((uintptr_t)resSeqs - 1) / 2;
9399 IntGroupIteratorInit(group, &iter);
9401 /* Change resSeqs */
9405 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9406 ap = ATOM_AT_INDEX(mp->atoms, i);
9408 resSeq = resSeqs[j++];
9409 if (resSeq > maxSeq)
9411 ap->resSeq = resSeq;
9413 __MoleculeUnlock(mp);
9415 /* Expand array if necessary */
9416 if (maxSeq >= mp->nresidues)
9417 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
9419 /* Synchronize resName and residues[] */
9421 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9422 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9424 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
9426 IntGroupIteratorRelease(&iter);
9427 __MoleculeUnlock(mp);
9429 MoleculeIncrementModifyCount(mp);
9435 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
9437 return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(intptr_t)(resSeq * 2 + 1));
9440 /* Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
9441 specifies the mp->nresidues after modifying the residue numbers.
9442 If all atoms are modified, then the table of residue names is also shifted. Otherwise,
9443 the table of residue names is not touched. */
9445 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
9447 int i, maxSeq, nmodatoms;
9449 IntGroupIterator iter;
9450 IntGroupIteratorInit(group, &iter);
9453 nresidues = mp->nresidues;
9456 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9457 ap = ATOM_AT_INDEX(mp->atoms, i);
9458 ap->resSeq += offset;
9459 if (ap->resSeq < 0) {
9460 /* Bad argument; undo change and returns this index + 1 */
9462 ap->resSeq -= offset;
9463 while ((i = IntGroupIteratorLast(&iter)) >= 0) {
9464 ap = ATOM_AT_INDEX(mp->atoms, i);
9465 ap->resSeq -= offset;
9467 IntGroupIteratorRelease(&iter);
9468 return bad_index + 1;
9470 if (ap->resSeq > maxSeq)
9471 maxSeq = ap->resSeq;
9474 if (maxSeq >= nresidues)
9475 nresidues = maxSeq + 1;
9476 if (offset < 0 && nmodatoms == mp->natoms) {
9477 /* Shift the residue names downward */
9478 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
9480 __MoleculeUnlock(mp);
9481 MoleculeChangeNumberOfResidues(mp, nresidues);
9482 if (offset > 0 && nmodatoms == mp->natoms) {
9483 /* Shift the residue names upward */
9485 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
9486 __MoleculeUnlock(mp);
9488 IntGroupIteratorRelease(&iter);
9490 MoleculeIncrementModifyCount(mp);
9495 /* Change residue names for the specified residue numbers. Names is an array of
9496 chars containing argc*4 characters, and every 4 characters represent a
9497 residue name; characters '\x01'-'\x1f' are converted to '\0', which allow
9498 names to be handled as a C string. */
9500 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
9505 for (i = 0; i < argc; i++) {
9506 if (maxSeq < resSeqs[i])
9507 maxSeq = resSeqs[i];
9509 if (maxSeq >= mp->nresidues)
9510 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
9512 for (i = 0; i < argc; i++) {
9513 char *p = mp->residues[resSeqs[i]];
9515 strncpy(p, names + i * 4, 4);
9516 for (j = 0; j < 4; j++) {
9517 if (p[j] >= 0 && p[j] < 0x20)
9521 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9522 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
9524 __MoleculeUnlock(mp);
9526 MoleculeIncrementModifyCount(mp);
9531 /* Returns the maximum residue number actually used */
9533 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
9538 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9539 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9541 if (ap->resSeq > maxSeq)
9542 maxSeq = ap->resSeq;
9547 /* Returns the minimum residue number actually used */
9549 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
9553 minSeq = ATOMS_MAX_NUMBER;
9554 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9555 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9557 if (ap->resSeq < minSeq)
9558 minSeq = ap->resSeq;
9560 return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
9563 #pragma mark ====== Sort by Residues ======
9566 sAtomSortComparator(const void *a, const void *b)
9568 const Atom *ap, *bp;
9569 ap = *((const Atom **)a);
9570 bp = *((const Atom **)b);
9571 if (ap->resSeq == bp->resSeq) {
9572 /* Retain the original order (i.e. atom with larger pointer address is larger) */
9579 /* Compare the residue sequence. However, residue sequence 0 is always larger. */
9580 if (ap->resSeq == 0)
9582 else if (bp->resSeq == 0)
9584 else if (ap->resSeq < bp->resSeq)
9586 else if (ap->resSeq > bp->resSeq)
9593 sMoleculeReorder(Molecule *mp)
9595 int i, res, prevRes;
9599 if (mp == NULL || mp->natoms <= 1)
9602 /* Sort the atoms, bonds, etc. */
9603 apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
9604 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9605 newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9606 if (apArray == NULL || old2new == NULL || newAtoms == NULL)
9607 Panic("Low memory during reordering atoms");
9608 for (i = 0; i < mp->natoms; i++)
9609 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
9611 /* Sort the atoms. Note: apArray is an array of "Pointer to Atom" */
9612 qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
9614 /* Make a table of 'which atom becomes which' */
9615 for (i = 0; i < mp->natoms; i++) {
9616 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
9620 /* Renumber the bonds, etc. */
9621 for (i = 0; i < mp->nbonds * 2; i++) {
9622 mp->bonds[i] = old2new[mp->bonds[i]];
9624 for (i = 0; i < mp->nangles * 3; i++) {
9625 mp->angles[i] = old2new[mp->angles[i]];
9627 for (i = 0; i < mp->ndihedrals * 4; i++) {
9628 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9630 for (i = 0; i < mp->nimpropers * 4; i++) {
9631 mp->impropers[i] = old2new[mp->impropers[i]];
9633 for (i = 0; i < mp->natoms; i++) {
9635 ip = AtomConnectData(&(apArray[i]->connect));
9636 for (j = 0; j < apArray[i]->connect.count; j++, ip++)
9640 /* Renumber the residues so that the residue numbers are contiguous */
9642 for (i = 0; i < mp->natoms; i++) {
9643 if (apArray[i]->resSeq == 0)
9645 if (apArray[i]->resSeq != prevRes) {
9647 prevRes = apArray[i]->resSeq;
9648 if (prevRes != res) {
9649 strncpy(mp->residues[res], mp->residues[prevRes], 4);
9652 apArray[i]->resSeq = res;
9654 mp->nresidues = res + 1;
9656 /* Sort the atoms and copy back to atoms[] */
9657 for (i = 0; i < mp->natoms; i++) {
9658 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
9660 memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
9662 /* Free the locally allocated storage */
9668 /* Renumber atoms */
9670 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
9672 Int *old2new, i, j, retval;
9676 if (mp->noModifyTopology)
9678 if (old2new_out != NULL)
9679 old2new = old2new_out;
9681 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9682 saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9683 if (old2new == NULL || saveAtoms == NULL)
9684 Panic("Low memory during reordering atoms");
9685 memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
9687 for (i = 0; i < mp->natoms; i++)
9689 for (i = 0; i < isize && i < mp->natoms; i++) {
9691 if (j < 0 || j >= mp->natoms) {
9692 retval = 1; /* Out of range */
9695 if (old2new[j] != -1) {
9696 retval = 2; /* Duplicate entry */
9701 if (i < mp->natoms) {
9702 for (j = 0; j < mp->natoms; j++) {
9703 if (old2new[j] != -1)
9708 if (i != mp->natoms) {
9709 retval = 3; /* Internal inconsistency */
9713 /* Renumber the bonds, etc. */
9714 for (i = 0; i < mp->nbonds * 2; i++) {
9715 mp->bonds[i] = old2new[mp->bonds[i]];
9717 for (i = 0; i < mp->nangles * 3; i++) {
9718 mp->angles[i] = old2new[mp->angles[i]];
9720 for (i = 0; i < mp->ndihedrals * 4; i++) {
9721 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9723 for (i = 0; i < mp->nimpropers * 4; i++) {
9724 mp->impropers[i] = old2new[mp->impropers[i]];
9726 /* Renumber the connection table and pi anchor table */
9727 for (i = 0; i < mp->natoms; i++) {
9728 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
9729 Int *ip = AtomConnectData(&ap->connect);
9730 for (j = 0; j < ap->connect.count; j++, ip++)
9732 if (ap->anchor != NULL) {
9733 ip = AtomConnectData(&ap->anchor->connect);
9734 for (j = 0; j < ap->anchor->connect.count; j++, ip++)
9739 if (mp->par != NULL) {
9740 /* Renumber the parameters */
9742 for (j = kFirstParType; j <= kLastParType; j++) {
9743 n = ParameterGetCountForType(mp->par, j);
9744 for (i = 0; i < n; i++) {
9745 UnionPar *up = ParameterGetUnionParFromTypeAndIndex(mp->par, j, i);
9747 ParameterRenumberAtoms(j, up, mp->natoms, old2new);
9752 /* Renumber the atoms */
9753 for (i = 0; i < mp->natoms; i++)
9754 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
9757 MoleculeIncrementModifyCount(mp);
9758 mp->needsMDRebuild = 1;
9761 __MoleculeUnlock(mp);
9763 if (old2new_out == NULL)
9768 #pragma mark ====== Coordinate Transform ======
9771 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
9776 Transform rtr, symtr;
9777 if (mp == NULL || tr == NULL)
9779 TransformInvert(rtr, tr);
9781 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9782 if (group == NULL || IntGroupLookup(group, i, NULL) != 0) {
9783 TransformVec(&ap->r, tr, &ap->r);
9784 if (!SYMOP_ALIVE(ap->symop))
9786 /* Transform symop */
9787 if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9789 TransformMul(symtr, tr, symtr);
9790 if (group == NULL || IntGroupLookup(group, ap->symbase, NULL) != 0)
9791 TransformMul(symtr, symtr, rtr);
9793 if (!SYMOP_ALIVE(ap->symop))
9795 /* Transform symop if the base atom is transformed */
9796 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
9798 if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9800 TransformMul(symtr, symtr, rtr);
9802 if (MoleculeGetSymopForTransform(mp, symtr, &new_symop, 1) != 0)
9804 ap->symop = new_symop;
9806 mp->needsMDCopyCoordinates = 1;
9807 __MoleculeUnlock(mp);
9808 sMoleculeNotifyChangeAppearance(mp);
9813 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
9817 if (mp == NULL || tr == NULL)
9820 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9821 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9823 TransformVec(&ap->r, tr, &ap->r);
9825 mp->needsMDCopyCoordinates = 1;
9826 __MoleculeUnlock(mp);
9827 sMoleculeNotifyChangeAppearance(mp);
9832 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
9835 if (mp == NULL || vp == NULL)
9837 memset(tr, 0, sizeof(tr));
9838 tr[0] = tr[4] = tr[8] = 1.0;
9842 MoleculeTransform(mp, tr, group);
9846 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
9849 TransformForRotation(tr, axis, angle, center);
9850 MoleculeTransform(mp, tr, group);
9854 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
9859 if (mp == NULL || center == NULL)
9861 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9862 return 2; /* Empty molecule */
9864 center->x = center->y = center->z = 0.0;
9865 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9866 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9868 VecScaleInc(*center, ap->r, ap->weight);
9872 return 3; /* Atomic weights are not defined? */
9874 VecScaleSelf(*center, w);
9879 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
9886 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9887 return 2; /* Empty molecule */
9888 vmin.x = vmin.y = vmin.z = 1e50;
9889 vmax.x = vmax.y = vmax.z = -1e50;
9890 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9891 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9893 if (vmin.x > ap->r.x)
9895 if (vmin.y > ap->r.y)
9897 if (vmin.z > ap->r.z)
9899 if (vmax.x < ap->r.x)
9901 if (vmax.y < ap->r.y)
9903 if (vmax.z < ap->r.z)
9913 #pragma mark ====== Measurements ======
9916 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
9919 /* if (mp->is_xtal_coord) {
9920 TransformVec(&r1, mp->cell->tr, vp1);
9921 TransformVec(&r2, mp->cell->tr, vp2);
9927 return VecLength(r1);
9931 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
9935 /* if (mp->is_xtal_coord) {
9936 TransformVec(&r1, mp->cell->tr, vp1);
9937 TransformVec(&r2, mp->cell->tr, vp2);
9938 TransformVec(&r3, mp->cell->tr, vp3);
9946 w = VecLength(r1) * VecLength(r3);
9949 return acos(VecDot(r1, r3) / w) * kRad2Deg;
9953 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
9955 Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
9957 /* if (mp->is_xtal_coord) {
9958 TransformVec(&r1, mp->cell->tr, vp1);
9959 TransformVec(&r2, mp->cell->tr, vp2);
9960 TransformVec(&r3, mp->cell->tr, vp3);
9961 TransformVec(&r4, mp->cell->tr, vp4);
9968 VecSub(r21, r1, r2);
9969 VecSub(r32, r2, r3);
9970 VecSub(r43, r3, r4);
9971 VecCross(v1, r21, r32);
9972 VecCross(v2, r32, r43);
9973 VecCross(v3, r32, v1);
9977 if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
9983 VecScaleSelf(v1, w1);
9984 VecScaleSelf(v2, w2);
9985 VecScaleSelf(v3, w3);
9986 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
9990 #pragma mark ====== XtalCell Parameters ======
9993 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
9995 if (mp->cell != NULL) {
9996 TransformVec(dst, mp->cell->tr, src);
10001 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
10003 if (mp->cell != NULL) {
10004 TransformVec(dst, mp->cell->rtr, src);
10005 } else *dst = *src;
10009 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
10011 static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
10013 Vector *vp1, *vp2, *vp3;
10018 for (n1 = 0; n1 < 3; n1++) {
10019 if (cp->flags[n1] != 0)
10023 /* All directions are non-periodic */
10024 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
10025 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
10029 vp1 = &(cp->axes[n1]);
10030 vp2 = &(cp->axes[n2]);
10031 vp3 = &(cp->axes[n3]);
10032 cp->tr[n1*3] = vp1->x;
10033 cp->tr[n1*3+1] = vp1->y;
10034 cp->tr[n1*3+2] = vp1->z;
10035 cp->tr[9] = cp->origin.x;
10036 cp->tr[10] = cp->origin.y;
10037 cp->tr[11] = cp->origin.z;
10038 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
10039 /* 1-dimensional or 2-dimensional system */
10040 /* Create "dummy" axes, so that transforms between internal and cartesian coordinates are
10041 possible with a single matrix */
10042 if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
10043 /* 1-dimensional */
10044 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
10045 VecCross(v1, *vp1, xvec);
10046 VecCross(v2, *vp1, yvec);
10047 if (VecLength2(v1) < VecLength2(v2))
10049 VecCross(v2, *vp1, v1);
10050 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
10051 return -1; /* Non-regular transform */
10052 } else if (cp->flags[n2] == 0) {
10054 VecCross(v1, v2, *vp1);
10055 if (NormalizeVec(&v1, &v1))
10056 return -1; /* Non-regular transform */
10059 VecCross(v2, *vp1, v1);
10060 if (NormalizeVec(&v2, &v2))
10061 return -1; /* Non-regular transform */
10063 cp->tr[n2*3] = v1.x;
10064 cp->tr[n2*3+1] = v1.y;
10065 cp->tr[n2*3+2] = v1.z;
10066 cp->tr[n3*3] = v2.x;
10067 cp->tr[n3*3+1] = v2.y;
10068 cp->tr[n3*3+2] = v2.z;
10070 VecCross(v1, *vp1, *vp2);
10071 if (fabs(VecDot(v1, *vp3)) < 1e-7)
10072 return -1; /* Non-regular transform */
10073 cp->tr[n2*3] = vp2->x;
10074 cp->tr[n2*3+1] = vp2->y;
10075 cp->tr[n2*3+2] = vp2->z;
10076 cp->tr[n3*3] = vp3->x;
10077 cp->tr[n3*3+1] = vp3->y;
10078 cp->tr[n3*3+2] = vp3->z;
10081 if (TransformInvert(cp->rtr, cp->tr))
10082 return -1; /* Non-regular transform */
10084 /* Calculate the reciprocal cell parameters */
10085 cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[3] * cp->rtr[3] + cp->rtr[6] * cp->rtr[6]);
10086 cp->rcell[1] = sqrt(cp->rtr[1] * cp->rtr[1] + cp->rtr[4] * cp->rtr[4] + cp->rtr[7] * cp->rtr[7]);
10087 cp->rcell[2] = sqrt(cp->rtr[2] * cp->rtr[2] + cp->rtr[5] * cp->rtr[5] + cp->rtr[8] * cp->rtr[8]);
10088 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;
10089 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;
10090 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;
10093 /* Calculate a, b, c, alpha, beta, gamma */
10094 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[1] * cp->tr[1] + cp->tr[2] * cp->tr[2]);
10095 cp->cell[1] = sqrt(cp->tr[3] * cp->tr[3] + cp->tr[4] * cp->tr[4] + cp->tr[5] * cp->tr[5]);
10096 cp->cell[2] = sqrt(cp->tr[6] * cp->tr[6] + cp->tr[7] * cp->tr[7] + cp->tr[8] * cp->tr[8]);
10097 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;
10098 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;
10099 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;
10105 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
10113 __MoleculeLock(mp);
10114 memset(&cmat, 0, sizeof(Transform));
10115 if (mp->cell != NULL)
10116 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
10118 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
10120 if (mp->cell != NULL) {
10122 mp->needsMDRebuild = 1;
10128 cp = (XtalCell *)calloc(sizeof(XtalCell), 1);
10130 Panic("Low memory during setting cell parameters");
10132 mp->needsMDRebuild = 1;
10134 /* alpha, beta, gamma are in degree */
10138 cp->cell[3] = alpha;
10139 cp->cell[4] = beta;
10140 cp->cell[5] = gamma;
10141 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
10142 /* c unique (hexagonal etc.) */
10143 Double cosa, cosb, sinb, cosg;
10144 cosa = cos(alpha * kDeg2Rad);
10145 cosb = cos(beta * kDeg2Rad);
10146 sinb = sin(beta * kDeg2Rad);
10147 cosg = cos(gamma * kDeg2Rad);
10148 cp->axes[0].x = a * sinb;
10150 cp->axes[0].z = a * cosb;
10151 cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
10152 cp->axes[1].z = b * cosa;
10153 cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
10159 Double cosg, sing, cosa, cosb;
10160 cosa = cos(alpha * kDeg2Rad);
10161 cosb = cos(beta * kDeg2Rad);
10162 cosg = cos(gamma * kDeg2Rad);
10163 sing = sin(gamma * kDeg2Rad);
10164 cp->axes[0].x = a * sing;
10165 cp->axes[0].y = a * cosg;
10170 cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
10171 cp->axes[2].y = c * cosa;
10172 cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
10174 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
10175 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
10176 MoleculeCalculateCellFromAxes(cp, 0);
10177 TransformMul(cmat, cp->tr, cmat);
10180 /* Update the coordinates (if requested) */
10181 if (convertCoordinates) {
10182 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10183 TransformVec(&(ap->r), cmat, &(ap->r));
10187 /* Update the anisotropic parameters */
10188 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10189 Aniso *anp = ap->aniso;
10191 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
10194 __MoleculeUnlock(mp);
10195 sMoleculeNotifyChangeAppearance(mp);
10199 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x13, Double x23, const Double *sigmaptr)
10203 const Double log2 = 0.693147180559945;
10204 const Double pi22 = 19.7392088021787; /* 2*pi**2 */
10210 if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
10212 anp = mp->atoms[n1].aniso;
10213 __MoleculeLock(mp);
10215 anp = (Aniso *)calloc(sizeof(Aniso), 1);
10217 __MoleculeUnlock(mp);
10218 Panic("Low memory during setting anisotropic atom parameters");
10220 mp->atoms[n1].aniso = anp;
10223 case 1: d = 1; dx = 0.5; break;
10224 case 2: d = log2; dx = log2; break;
10225 case 3: d = log2; dx = log2 * 0.5; break;
10226 case 4: u = 1; d = 0.25; dx = 0.25; break;
10227 case 5: u = 1; d = 0.25; dx = 0.125; break;
10228 case 8: u = 1; d = pi22; dx = pi22; break;
10229 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
10230 case 10: d = pi22; dx = pi22; break;
10231 default: d = dx = 1; break;
10233 anp->bij[0] = x11 * d;
10234 anp->bij[1] = x22 * d;
10235 anp->bij[2] = x33 * d;
10236 anp->bij[3] = x12 * dx;
10237 anp->bij[4] = x13 * dx;
10238 anp->bij[5] = x23 * dx;
10239 if (sigmaptr != NULL) {
10241 anp->bsig[0] = sigmaptr[0] * d;
10242 anp->bsig[1] = sigmaptr[1] * d;
10243 anp->bsig[2] = sigmaptr[2] * d;
10244 anp->bsig[3] = sigmaptr[3] * dx;
10245 anp->bsig[4] = sigmaptr[4] * dx;
10246 anp->bsig[5] = sigmaptr[5] * dx;
10249 anp->bsig[0] = anp->bsig[1] = anp->bsig[2] = anp->bsig[3] = anp->bsig[4] = anp->bsig[5] = 0.0;
10252 if (cp != NULL && u == 1) {
10253 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
10254 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
10255 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
10256 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
10257 anp->bij[4] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[3] * kDeg2Rad); */
10258 anp->bij[5] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[4] * kDeg2Rad); */
10259 if (sigmaptr != NULL) {
10260 anp->bsig[0] *= cp->rcell[0] * cp->rcell[0];
10261 anp->bsig[1] *= cp->rcell[1] * cp->rcell[1];
10262 anp->bsig[2] *= cp->rcell[2] * cp->rcell[2];
10263 anp->bsig[3] *= cp->rcell[0] * cp->rcell[1];
10264 anp->bsig[4] *= cp->rcell[2] * cp->rcell[0];
10265 anp->bsig[5] *= cp->rcell[1] * cp->rcell[2];
10269 /* Calculate the principal axes (in Cartesian coordinates) */
10270 /* The principal axes are the eigenvectors of matrix At(B^-1)A, where
10271 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
10272 in which x and z are the crystal-space and cartesian coordinates. */
10273 m1[0] = anp->bij[0] / pi22;
10274 m1[4] = anp->bij[1] / pi22;
10275 m1[8] = anp->bij[2] / pi22;
10276 m1[1] = m1[3] = anp->bij[3] / pi22;
10277 m1[2] = m1[6] = anp->bij[4] / pi22;
10278 m1[5] = m1[7] = anp->bij[5] / pi22;
10279 MatrixInvert(m1, m1);
10281 memmove(m2, cp->rtr, sizeof(Mat33));
10282 MatrixMul(m1, m1, m2);
10283 MatrixTranspose(m2, m2);
10284 MatrixMul(m1, m2, m1);
10286 MatrixSymDiagonalize(m1, val, axis);
10287 for (u = 0; u < 3; u++) {
10288 anp->eigval[u] = val[u];
10290 fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
10293 val[u] = 1 / sqrt(val[u]);
10295 anp->pmat[u*3] = axis[u].x * val[u];
10296 anp->pmat[u*3+1] = axis[u].y * val[u];
10297 anp->pmat[u*3+2] = axis[u].z * val[u];
10299 __MoleculeUnlock(mp);
10302 /* Set the anisotropic parameter for atom idx according to the symop. If symop is not alive, nothing is done. */
10304 MoleculeSetAnisoBySymop(Molecule *mp, int idx)
10308 if (mp == NULL || idx < 0 || idx >= mp->natoms)
10310 ap = ATOM_AT_INDEX(mp->atoms, idx);
10311 if (!SYMOP_ALIVE(ap->symop))
10313 ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
10314 if (ap2->aniso == NULL) {
10315 if (ap->aniso != NULL) {
10321 if (ap->aniso == NULL)
10322 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
10323 if (ap->symop.sym == 0 || ap->symop.sym >= mp->nsyms) {
10324 /* Just copy the aniso parameters */
10325 memmove(ap->aniso, ap2->aniso, sizeof(Aniso));
10328 memmove(t1, SYMMETRY_AT_INDEX(mp->syms, ap->symop.sym), sizeof(Transform));
10329 t1[9] = t1[10] = t1[11] = 0.0;
10330 memset(t2, 0, sizeof(Transform));
10331 t2[0] = ap2->aniso->bij[0];
10332 t2[4] = ap2->aniso->bij[1];
10333 t2[8] = ap2->aniso->bij[2];
10334 t2[1] = t2[3] = ap2->aniso->bij[3];
10335 t2[2] = t2[6] = ap2->aniso->bij[4];
10336 t2[5] = t2[7] = ap2->aniso->bij[5];
10337 TransformMul(t2, t1, t2);
10338 TransformInvert(t1, t1);
10339 TransformMul(t2, t2, t1);
10340 MoleculeSetAniso(mp, idx, 0, t2[0], t2[4], t2[8], t2[1], t2[2], t2[5], (ap2->aniso->has_bsig ? ap2->aniso->bsig : NULL));
10344 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic, int convertCoordinates)
10346 static Vector zeroVec = {0, 0, 0};
10353 if (mp->cell != NULL)
10354 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
10356 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
10358 if (mp->cell != NULL) {
10360 mp->needsMDRebuild = 1;
10365 memset(&b, 0, sizeof(b));
10366 b.axes[0] = (ax != NULL ? *ax : zeroVec);
10367 b.axes[1] = (ay != NULL ? *ay : zeroVec);
10368 b.axes[2] = (az != NULL ? *az : zeroVec);
10370 memmove(b.flags, periodic, 3);
10371 if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
10373 __MoleculeLock(mp);
10374 if (mp->cell == NULL) {
10375 mp->needsMDRebuild = 1;
10377 if (mp->cell->has_sigma) {
10378 /* Keep the sigma */
10380 memmove(b.cellsigma, mp->cell->cellsigma, sizeof(mp->cell->cellsigma));
10382 if ((b.flags[0] != mp->cell->flags[0]) || (b.flags[1] != mp->cell->flags[1]) || (b.flags[2] != mp->cell->flags[2])) {
10383 mp->needsMDRebuild = 1;
10387 mp->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
10388 if (mp->cell != NULL) {
10389 memmove(mp->cell, &b, sizeof(XtalCell));
10390 TransformMul(cmat, b.tr, cmat);
10391 /* Update the coordinates (if requested) */
10392 if (convertCoordinates) {
10393 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10394 TransformVec(&(ap->r), cmat, &(ap->r));
10398 /* Update the anisotropic parameters */
10399 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10400 Aniso *anp = ap->aniso;
10402 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
10406 } else n = -2; /* Out of memory */
10407 __MoleculeUnlock(mp);
10408 sMoleculeNotifyChangeAppearance(mp);
10412 #pragma mark ====== Fragment manipulation ======
10415 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
10419 if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
10421 IntGroupAdd(result, idx, 1);
10422 ap = ATOM_AT_INDEX(mp->atoms, idx);
10423 cp = AtomConnectData(&ap->connect);
10424 for (i = 0; i < ap->connect.count; i++) {
10426 if (IntGroupLookup(result, idx2, NULL))
10428 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, idx2)->anchor != NULL)
10429 continue; /* bond between two pi_anchors is ignored */
10430 sMoleculeFragmentSub(mp, idx2, result, exatoms);
10432 if (ap->anchor != NULL) {
10433 cp = AtomConnectData(&ap->anchor->connect);
10434 for (i = 0; i < ap->anchor->connect.count; i++) {
10436 if (IntGroupLookup(result, idx2, NULL))
10438 sMoleculeFragmentSub(mp, idx2, result, exatoms);
10443 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
10444 not containing the atoms in exatoms */
10446 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
10449 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
10451 result = IntGroupNew();
10452 sMoleculeFragmentSub(mp, n1, result, exatoms);
10456 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
10457 not containing the atoms n2, n3, ... (terminated by -1) */
10459 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
10462 IntGroup *exatoms, *result;
10463 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
10465 exatoms = IntGroupNew();
10466 for (i = 0; i < argc; i++)
10467 IntGroupAdd(exatoms, argv[i], 1);
10468 result = IntGroupNew();
10469 sMoleculeFragmentSub(mp, n1, result, exatoms);
10470 IntGroupRelease(exatoms);
10474 /* The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
10475 not containing the atoms in exatoms */
10477 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
10479 IntGroupIterator iter;
10482 if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
10484 IntGroupIteratorInit(inatoms, &iter);
10485 result = IntGroupNew();
10486 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
10487 sMoleculeFragmentSub(mp, i, result, exatoms);
10489 IntGroupIteratorRelease(&iter);
10493 /* Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
10494 group is bound to the rest of the molecule via only one bond.
10495 If the result is true, then the atoms belonging to the (only) bond are returned
10496 in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
10497 and n2 can be NULL, if those informations are not needed. */
10499 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
10501 Int i, i1, i2, j, k, bond_count, nval1, nval2, *cp;
10503 if (mp == NULL || mp->natoms == 0 || group == NULL)
10504 return 0; /* Invalid arguments */
10506 for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
10507 i2 = IntGroupGetEndPoint(group, i);
10508 for (j = i1; j < i2; j++) {
10509 if (j < 0 || j >= mp->natoms)
10510 return 0; /* Invalid atom group */
10511 ap = ATOM_AT_INDEX(mp->atoms, j);
10512 cp = AtomConnectData(&ap->connect);
10513 for (k = 0; k < ap->connect.count; k++) {
10514 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, cp[k])->anchor != NULL)
10515 continue; /* Ignore bond between two pi_anchors */
10516 if (IntGroupLookup(group, cp[k], NULL) == 0) {
10520 if (bond_count > 1)
10521 return 0; /* Too many bonds */
10524 if (ap->anchor != NULL) {
10525 cp = AtomConnectData(&ap->anchor->connect);
10526 for (k = 0; k < ap->anchor->connect.count; k++) {
10527 if (IntGroupLookup(group, cp[k], NULL) == 0) {
10531 if (bond_count > 1)
10532 return 0; /* Too many bonds */
10538 if (bond_count == 1) {
10549 /* Returns non-zero if the given group is 'rotatable' in the molecule. The group
10550 is said to be 'rotatable' when either of the following conditions are met; (1)
10551 the group is detachable, or (2) the group consists of two bonded atoms that define
10552 a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
10553 (either a new IntGroup or 'group' with incremented reference count; thus the caller
10554 is responsible for releasing the returned value). */
10556 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
10559 if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
10560 if (rotGroup != NULL) {
10561 IntGroupRetain(group);
10566 if (group != NULL && IntGroupGetCount(group) == 2) {
10567 i1 = IntGroupGetNthPoint(group, 0);
10568 i2 = IntGroupGetNthPoint(group, 1);
10569 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
10570 IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
10573 i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
10575 IntGroupRelease(frag);
10576 if (rotGroup != NULL)
10580 if (rotGroup != NULL)
10582 else if (frag != NULL)
10583 IntGroupRelease(frag);
10590 #pragma mark ====== Multiple frame ======
10593 MoleculeGetNumberOfFrames(Molecule *mp)
10597 if (mp->nframes <= 0) {
10601 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10602 if (ap->nframes > n)
10609 return mp->nframes;
10613 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
10615 int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
10616 Vector *tempv, *vp;
10621 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
10624 n_old = MoleculeGetNumberOfFrames(mp);
10625 n_new = n_old + count;
10626 last_inserted = IntGroupGetNthPoint(group, count - 1);
10627 if (n_new <= last_inserted) {
10628 exframes = last_inserted - n_new + 1; /* number of extra frames that will be silently inserted */
10630 } else exframes = 0;
10632 tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4); /* "*4" for handling cells */
10636 __MoleculeLock(mp);
10638 /* Copy back the current coordinates */
10639 /* No change in the current coordinates, but the frame buffer is updated */
10640 MoleculeSelectFrame(mp, mp->cframe, 1);
10642 /* Expand ap->frames for all atoms */
10643 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10644 Int n = ap->nframes;
10645 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), n_new - 1, NULL);
10646 if (ap->frames == NULL) {
10647 __MoleculeUnlock(mp);
10650 for (j = n; j < n_new; j++)
10651 ap->frames[j] = ap->r;
10653 if (mp->cell != NULL) {
10654 j = mp->nframe_cells;
10655 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, n_new - 1, NULL);
10656 for (i = j; i < n_new; i++) {
10657 /* Set the current cell parameters to the expanded frames */
10658 mp->frame_cells[i * 4] = mp->cell->axes[0];
10659 mp->frame_cells[i * 4 + 1] = mp->cell->axes[1];
10660 mp->frame_cells[i * 4 + 2] = mp->cell->axes[2];
10661 mp->frame_cells[i * 4 + 3] = mp->cell->origin;
10665 /* Expand propvals for all properties */
10666 for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
10667 dp = (Double *)realloc(prp->propvals, sizeof(Double) * n_new);
10669 __MoleculeUnlock(mp);
10672 for (j = n_old; j < n_new; j++)
10674 prp->propvals = dp;
10677 /* group = [n0..n1-1, n2..n3-1, ...] */
10679 /* tempv[0..n0-1] <- ap[0..n0-1], s += n0,
10680 tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
10681 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
10682 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
10684 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
10685 At last, s will become n_old and t will become count. */
10686 for (i = 0, ap = mp->atoms, prp = mp->molprops; i <= mp->natoms + mp->nmolprops; i++) {
10687 int s, t, ns, ne, mult;
10690 if (i == mp->natoms) {
10691 if (mp->cell == NULL || mp->frame_cells == NULL)
10693 vp = mp->frame_cells;
10695 } else if (i < mp->natoms) {
10700 dp = prp->propvals;
10702 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10704 if (i <= mp->natoms)
10705 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
10707 memmove((Double *)tempv + ne, dp + s, sizeof(Double) * (ns - ne));
10710 ne = IntGroupGetEndPoint(group, j);
10712 if (i == mp->natoms) {
10713 if (inFrameCell != NULL) {
10714 tempv[ns * 4] = inFrameCell[t * 4];
10715 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
10716 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
10717 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
10719 tempv[ns * 4] = mp->cell->axes[0];
10720 tempv[ns * 4 + 1] = mp->cell->axes[1];
10721 tempv[ns * 4 + 2] = mp->cell->axes[2];
10722 tempv[ns * 4 + 3] = mp->cell->origin;
10724 } else if (i < mp->natoms) {
10725 if (inFrame != NULL)
10726 tempv[ns] = inFrame[natoms * t + i];
10730 ((Double *)tempv)[ns] = 0.0;
10737 if (i <= mp->natoms)
10738 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
10740 memmove((Double *)tempv + ne, dp + s, sizeof(Double) * (n_new - ne));
10743 if (i < mp->natoms)
10744 ap->nframes = n_new;
10745 if (i <= mp->natoms) {
10746 memmove(vp, tempv, sizeof(Vector) * mult * n_new);
10747 if (i < mp->natoms) {
10748 ap->nframes = n_new;
10749 ap = ATOM_NEXT(ap);
10752 memmove(dp, (Double *)tempv, sizeof(Double) * n_new);
10757 mp->nframes = n_new;
10758 MoleculeSelectFrame(mp, last_inserted, 0);
10759 MoleculeIncrementModifyCount(mp);
10760 __MoleculeUnlock(mp);
10765 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
10767 int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
10768 Vector *tempv, *vp;
10771 IntGroup *group, *group2;
10773 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
10776 /* outFrame[] should have enough size for Vector * natoms * group.count */
10777 memset(outFrame, 0, sizeof(Vector) * natoms * count);
10778 if (mp->cell != NULL && mp->frame_cells != NULL)
10779 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
10781 n_old = MoleculeGetNumberOfFrames(mp);
10783 return -2; /* Cannot delete last frame */
10785 group = IntGroupNew();
10786 group2 = IntGroupNewWithPoints(0, n_old, -1);
10787 IntGroupIntersect(inGroup, group2, group);
10788 IntGroupRelease(group2);
10789 count = IntGroupGetCount(group);
10790 n_new = n_old - count;
10792 IntGroupRelease(group);
10793 return -2; /* Trying to delete too many frames */
10795 tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4); /* "*4" for handling cells */
10796 if (tempv == NULL) {
10797 IntGroupRelease(group);
10801 __MoleculeLock(mp);
10803 /* Copy back the current coordinates */
10804 /* No change in the current coordinates, but the frame buffer is updated */
10805 MoleculeSelectFrame(mp, mp->cframe, 1);
10807 /* Determine which frame should be selected after removal is completed */
10810 if (IntGroupLookup(group, mp->cframe, &i)) {
10811 /* cframe will be removed */
10812 n1 = IntGroupGetStartPoint(group, i) - 1;
10814 n1 = IntGroupGetEndPoint(group, i);
10815 } else n1 = mp->cframe;
10816 /* Change to that frame */
10817 MoleculeSelectFrame(mp, n1, 0);
10818 group2 = IntGroupNewFromIntGroup(group);
10819 IntGroupReverse(group2, 0, n_old);
10820 new_cframe = IntGroupLookupPoint(group2, n1);
10821 if (new_cframe < 0)
10822 return -3; /* This cannot happen */
10823 IntGroupRelease(group2);
10826 /* group = [n0..n1-1, n2..n3-1, ...] */
10828 /* tempv[0..n0-1] -> ap[0..n0-1], s += n0,
10829 tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
10830 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
10831 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
10833 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
10834 At last, s will become n_new and t will become count. */
10836 for (i = 0, ap = mp->atoms, prp = mp->molprops; i <= mp->natoms + mp->nmolprops; i++) {
10837 int s, t, j, ns, ne;
10839 /* if i == mp->natoms, mp->frame_cells is handled */
10840 if (i == mp->natoms) {
10841 if (mp->cell == NULL || mp->frame_cells == NULL)
10843 mult = 4 * sizeof(Vector);
10844 vp = mp->frame_cells;
10846 } else if (i < mp->natoms) {
10847 mult = sizeof(Vector);
10850 NewArray(&ap->frames, &ap->nframes, sizeof(Vector), n_old);
10851 if (ap->frames == NULL) {
10852 __MoleculeUnlock(mp);
10857 old_count = ap->nframes;
10859 mult = sizeof(Double);
10860 vp = (Vector *)prp->propvals;
10864 /* Copy vp to tempv */
10865 memset(tempv, 0, mult * n_old);
10866 memmove(tempv, vp, mult * (old_count > n_old ? n_old : old_count));
10867 ne = ns = s = t = 0;
10868 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10872 memmove((char *)vp + s * mult, (char *)tempv + ne * mult, mult * (ns - ne));
10875 ne = IntGroupGetEndPoint(group, j);
10879 if (i < mp->natoms)
10880 outFrame[natoms * t + i] = tempv[ns];
10881 else if (i == mp->natoms) {
10882 if (outFrameCell != NULL) {
10883 outFrameCell[t * 4] = tempv[ns * 4];
10884 outFrameCell[t * 4 + 1] = tempv[ns * 4 + 1];
10885 outFrameCell[t * 4 + 2] = tempv[ns * 4 + 2];
10886 outFrameCell[t * 4 + 3] = tempv[ns * 4 + 3];
10894 memmove((char *)vp + s * mult, (char *)tempv + ne * mult, mult * (n_old - ne));
10897 if (i < mp->natoms)
10902 if (i < mp->natoms) {
10906 } else if (i == mp->natoms) {
10907 free(mp->frame_cells);
10908 mp->frame_cells = NULL;
10909 mp->nframe_cells = 0;
10911 prp->propvals = (Double *)realloc(prp->propvals, sizeof(Double));
10914 if (i < mp->natoms) {
10915 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), s - 1, NULL);
10917 } else if (i == mp->natoms) {
10918 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, s - 1, NULL);
10919 mp->nframe_cells = s;
10921 prp->propvals = (Double *)realloc(prp->propvals, sizeof(Double) * s);
10924 if (i < mp->natoms) {
10925 ap = ATOM_NEXT(ap);
10926 } else if (i > mp->natoms) {
10931 mp->nframes = nframes;
10933 /* Select the "last" frame; do not "copy back" the coordinates to the frame table */
10934 /* i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
10935 MoleculeSelectFrame(mp, new_cframe, 0);
10937 IntGroupRelease(group);
10939 MoleculeIncrementModifyCount(mp);
10940 __MoleculeUnlock(mp);
10945 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
10947 int i, cframe, nframes, modified;
10949 cframe = mp->cframe;
10950 nframes = MoleculeGetNumberOfFrames(mp);
10952 frame = mp->cframe;
10953 if (mp == NULL || mp->natoms == 0 || frame < 0 || frame >= nframes)
10956 __MoleculeLock(mp);
10957 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10958 if (copyback && cframe >= 0 && cframe < ap->nframes) {
10959 /* Write the current coordinate back to the frame array */
10960 ap->frames[cframe] = ap->r;
10962 if ((frame != cframe || copyback == 0) && frame >= 0 && frame < ap->nframes) {
10963 /* Read the coordinate from the frame array */
10964 ap->r = ap->frames[frame];
10969 if (mp->cell != NULL && mp->frame_cells != NULL) {
10970 /* Write the current cell back to the frame_cells array */
10971 if (copyback && cframe >= 0) {
10972 Vector *vp = (Vector *)AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, cframe, NULL);
10973 vp[0] = mp->cell->axes[0];
10974 vp[1] = mp->cell->axes[1];
10975 vp[2] = mp->cell->axes[2];
10976 vp[3] = mp->cell->origin;
10978 /* Set the cell from the frame array */
10979 if ((frame != cframe || copyback == 0) && frame >= 0 && frame < mp->nframe_cells) {
10980 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);
10982 MoleculeAmendBySymmetry(mp, NULL, NULL, NULL);
10985 mp->cframe = frame;
10987 mp->needsMDCopyCoordinates = 1;
10988 __MoleculeUnlock(mp);
10989 sMoleculeNotifyChangeAppearance(mp);
10993 /* If molecule is multi-frame, then flush the current information to the frame buffer.
10994 Returns the number of frames. */
10996 MoleculeFlushFrames(Molecule *mp)
10998 int nframes = MoleculeGetNumberOfFrames(mp);
11000 MoleculeSelectFrame(mp, mp->cframe, 1);
11005 MoleculeReorderFrames(Molecule *mp, const Int *old_idx)
11007 Int *ip, i, j, n, nframes;
11011 if (mp == NULL || old_idx == NULL)
11013 nframes = MoleculeGetNumberOfFrames(mp);
11014 MoleculeFlushFrames(mp);
11015 ip = (Int *)malloc(sizeof(Int) * nframes);
11017 return -1; /* Out of memory */
11018 memset(ip, 0, sizeof(Int) * nframes);
11019 /* Check the argument */
11020 for (i = 0; i < nframes; i++) {
11022 if (j < 0 || j >= nframes || ip[j] != 0) {
11024 return -2; /* Bad argument */
11029 dp = (Double *)malloc(sizeof(Double) * nframes * 12);
11030 for (i = 0, ap = mp->atoms, prp = mp->molprops; i <= mp->natoms + mp->nmolprops; i++) {
11031 for (j = 0; j < nframes; j++) {
11033 if (i < mp->natoms) {
11034 ((Vector *)dp)[j] = (n < ap->nframes ? ap->frames[n] : ap->r);
11035 } else if (i == mp->natoms) {
11036 if (mp->cell != NULL) {
11037 if (n < mp->nframe_cells && mp->frame_cells != NULL)
11038 memmove(dp + j * 12, mp->frame_cells + n * 4, sizeof(Vector) * 4);
11040 ((Vector *)dp)[j * 4] = mp->cell->axes[0];
11041 ((Vector *)dp)[j * 4] = mp->cell->axes[1];
11042 ((Vector *)dp)[j * 4] = mp->cell->axes[2];
11043 ((Vector *)dp)[j * 4] = mp->cell->origin;
11047 dp[j] = prp->propvals[n];
11050 for (j = 0; j < nframes; j++) {
11051 if (i < mp->natoms) {
11052 if (ap->nframes <= j)
11053 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes - 1, NULL);
11054 ap->frames[j] = ((Vector *)dp)[j];
11055 } else if (i == mp->natoms) {
11056 if (mp->cell != NULL) {
11057 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, nframes - 1, NULL);
11058 memmove(mp->frame_cells + j * 4, dp + j * 12, sizeof(Vector) * 4);
11061 prp->propvals[j] = dp[j];
11064 if (i < mp->natoms)
11065 ap = ATOM_NEXT(ap);
11066 else if (i > mp->natoms)
11070 MoleculeSelectFrame(mp, mp->cframe, 0);
11074 #pragma mark ====== Molecule Propeties ======
11077 MoleculeCreateProperty(Molecule *mp, const char *name)
11081 for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
11082 if (strcmp(prp->propname, name) == 0)
11085 prp = (MolProp *)calloc(sizeof(MolProp), 1);
11088 prp->propname = strdup(name);
11089 if (prp->propname == NULL)
11091 i = MoleculeGetNumberOfFrames(mp);
11092 prp->propvals = (Double *)calloc(sizeof(Double), i);
11093 if (prp->propvals == NULL)
11095 AssignArray(&mp->molprops, &mp->nmolprops, sizeof(MolProp), mp->nmolprops, prp);
11097 return mp->nmolprops - 1;
11101 MoleculeLookUpProperty(Molecule *mp, const char *name)
11105 for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
11106 if (strcmp(prp->propname, name) == 0)
11113 MoleculeDeletePropertyAtIndex(Molecule *mp, int idx)
11115 if (idx >= 0 && idx < mp->nmolprops) {
11116 free(mp->molprops[idx].propname);
11117 free(mp->molprops[idx].propvals);
11118 DeleteArray(&mp->molprops, &mp->nmolprops, sizeof(MolProp), idx, 1, NULL);
11125 MoleculeSetProperty(Molecule *mp, int idx, IntGroup *ig, const Double *values)
11127 IntGroupIterator iter;
11129 if (idx < 0 || idx >= mp->nmolprops)
11131 IntGroupIteratorInit(ig, &iter);
11132 nframes = MoleculeGetNumberOfFrames(mp);
11134 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
11137 mp->molprops[idx].propvals[i] = values[n];
11140 IntGroupIteratorRelease(&iter);
11145 MoleculeGetProperty(Molecule *mp, int idx, IntGroup *ig, Double *outValues)
11147 IntGroupIterator iter;
11149 if (idx < 0 || idx >= mp->nmolprops)
11151 IntGroupIteratorInit(ig, &iter);
11152 nframes = MoleculeGetNumberOfFrames(mp);
11154 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
11157 outValues[n] = mp->molprops[idx].propvals[i];
11160 IntGroupIteratorRelease(&iter);
11164 #pragma mark ====== Pi Atoms ======
11167 sMoleculeCalculatePiAnchorPosition(Atom *ap, Atom *atoms)
11171 cp = AtomConnectData(&ap->anchor->connect);
11172 n = ap->anchor->connect.count;
11174 for (j = 0; j < n; j++) {
11175 Double w = ap->anchor->coeffs[j];
11176 ap2 = ATOM_AT_INDEX(atoms, cp[j]);
11177 VecScaleInc(ap->r, ap2->r, w);
11182 MoleculeUpdatePiAnchorPositions(Molecule *mol)
11186 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
11187 if (ap->anchor == NULL)
11189 sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
11194 MoleculeCalculatePiAnchorPosition(Molecule *mol, int idx)
11197 if (mol == NULL || idx < 0 || idx >= mol->natoms)
11199 ap = ATOM_AT_INDEX(mol->atoms, idx);
11200 if (ap->anchor == NULL)
11202 sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
11206 MoleculeSetPiAnchorList(Molecule *mol, Int idx, Int nentries, Int *entries, Double *weights, Int *nUndoActions, struct MolAction ***undoActions)
11209 Int *ip, i, j, n, *np;
11211 if (mol == NULL || idx < 0 || idx >= mol->natoms || nentries <= 1)
11212 return -1; /* Invalid argument */
11213 if (weights != NULL) {
11215 for (i = 0; i < nentries; i++) {
11216 if (weights[i] <= 0.0) {
11217 return 10; /* Weights must be positive */
11222 } else d = 1.0 / nentries;
11223 ap = ATOM_AT_INDEX(mol->atoms, idx);
11224 if (ap->anchor != NULL) {
11225 /* Already an anchor: check if bonds/angles/dihedrals have entries related to this anchor */
11226 IntGroup *bg, *ag, *dg, *ig;
11227 Int *ibuf, ibufsize;
11229 bg = ag = dg = ig = NULL;
11230 ip = AtomConnectData(&ap->anchor->connect);
11231 for (i = 0; i < ap->anchor->connect.count; i++) {
11233 for (j = 0; j < nentries; j++) {
11234 if (n == entries[j])
11237 if (j == nentries) {
11238 /* This entry will disappear: if any bond/angle/dihedral has idx-n pair, that should be removed. */
11239 for (j = 0, np = mol->bonds; j < mol->nbonds; j++, np += 2) {
11240 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[0])) {
11242 bg = IntGroupNew();
11243 IntGroupAdd(bg, j, 1);
11246 for (j = 0, np = mol->angles; j < mol->nangles; j++, np += 3) {
11247 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) ||
11248 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1])) {
11250 ag = IntGroupNew();
11251 IntGroupAdd(ag, j, 1);
11254 for (j = 0, np = mol->dihedrals; j < mol->ndihedrals; j++, np += 4) {
11255 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) || (idx == np[2] && n == np[3]) ||
11256 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[3] && n == np[2])) {
11258 dg = IntGroupNew();
11259 IntGroupAdd(dg, j, 1);
11262 for (j = 0, np = mol->impropers; j < mol->nimpropers; j++, np += 4) {
11263 if ((idx == np[0] && n == np[2]) || (idx == np[1] && n == np[2]) || (idx == np[3] && n == np[2]) ||
11264 (idx == np[2] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[2] && n == np[3])) {
11266 ig = IntGroupNew();
11267 IntGroupAdd(ig, j, 1);
11275 /* Delete impropers (with undo info) */
11276 i = IntGroupGetCount(ig);
11277 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
11278 MoleculeDeleteImpropers(mol, ibuf, ig);
11279 if (nUndoActions != NULL && undoActions != NULL) {
11280 act = MolActionNew(gMolActionAddImpropers, i * 4, ibuf, ig);
11281 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11283 IntGroupRelease(ig);
11286 /* Delete dihedrals (with undo info) */
11287 i = IntGroupGetCount(dg);
11288 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
11289 MoleculeDeleteDihedrals(mol, ibuf, dg);
11290 if (nUndoActions != NULL && undoActions != NULL) {
11291 act = MolActionNew(gMolActionAddDihedrals, i * 4, ibuf, dg);
11292 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11294 IntGroupRelease(dg);
11297 /* Delete angles (with undo info) */
11298 i = IntGroupGetCount(ag);
11299 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 3 - 1, NULL);
11300 MoleculeDeleteAngles(mol, ibuf, ag);
11301 if (nUndoActions != NULL && undoActions != NULL) {
11302 act = MolActionNew(gMolActionAddAngles, i * 3, ibuf, ag);
11303 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11305 IntGroupRelease(ag);
11308 /* Delete bonds (with undo info) */
11309 i = IntGroupGetCount(bg);
11310 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 2 - 1, NULL);
11311 MoleculeDeleteBonds(mol, ibuf, bg, NULL, NULL);
11312 if (nUndoActions != NULL && undoActions != NULL) {
11313 act = MolActionNew(gMolActionAddBondsForUndo, i * 2, ibuf, bg);
11314 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11316 IntGroupRelease(bg);
11319 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
11321 AtomConnectResize(&ap->anchor->connect, nentries);
11322 memmove(AtomConnectData(&ap->anchor->connect), entries, sizeof(Int) * nentries);
11323 AssignArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), nentries - 1, NULL);
11324 if (weights != NULL) {
11325 memmove(ap->anchor->coeffs, weights, sizeof(Double) * nentries);
11326 for (i = 0; i < nentries; i++)
11327 ap->anchor->coeffs[i] *= d; /* Normalize weight */
11329 for (i = 0; i < nentries; i++)
11330 ap->anchor->coeffs[i] = d;
11332 MoleculeCalculatePiAnchorPosition(mol, idx);
11336 #pragma mark ====== MO calculation ======
11338 /* Calculate an MO value for a single point. */
11339 /* Index is the MO number (1-based); 0 denotes "arbitrary vector" */
11340 /* tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom. */
11342 sCalcMOPoint(Molecule *mp, const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
11346 Double val, tval, *cnp, *tmpp, *mobasep, *mop;
11348 /* Cache dr and |dr|^2 */
11350 index = bset->nmos + 1;
11351 for (i = 0; i < mp->natoms; i++) {
11353 r = ATOM_AT_INDEX(mp->atoms, i)->r;
11354 tmp[i * 4] = r.x = (vp->x - r.x) * kAngstrom2Bohr;
11355 tmp[i * 4 + 1] = r.y = (vp->y - r.y) * kAngstrom2Bohr;
11356 tmp[i * 4 + 2] = r.z = (vp->z - r.z) * kAngstrom2Bohr;
11357 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
11359 /* Iterate over all shells */
11361 mobasep = bset->mo + (index - 1) * bset->ncomps;
11362 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
11363 pp = bset->priminfos + sp->p_idx;
11364 cnp = bset->cns + sp->cn_idx;
11365 if (sp->a_idx >= mp->natoms)
11366 return 0.0; /* This may happen when molecule is edited after setting up MO info */
11367 tmpp = tmp + sp->a_idx * 4;
11368 mop = mobasep + sp->m_idx;
11372 for (j = 0; j < sp->nprim; j++) {
11373 tval += *cnp++ * exp(-pp->A * tmpp[3]);
11376 val += mop[0] * tval;
11382 for (j = 0; j < sp->nprim; j++) {
11383 tval = exp(-pp->A * tmpp[3]);
11384 x += *cnp++ * tval;
11385 y += *cnp++ * tval;
11386 z += *cnp++ * tval;
11389 x *= mop[0] * tmpp[0];
11390 y *= mop[1] * tmpp[1];
11391 z *= mop[2] * tmpp[2];
11395 case kGTOType_SP: {
11398 for (j = 0; j < sp->nprim; j++) {
11399 tval = exp(-pp->A * tmpp[3]);
11400 t += *cnp++ * tval;
11401 x += *cnp++ * tval;
11402 y += *cnp++ * tval;
11403 z += *cnp++ * tval;
11407 x *= mop[1] * tmpp[0];
11408 y *= mop[2] * tmpp[1];
11409 z *= mop[3] * tmpp[2];
11410 val += t + x + y + z;
11414 Double xx, yy, zz, xy, xz, yz;
11415 xx = yy = zz = xy = xz = yz = 0;
11416 for (j = 0; j < sp->nprim; j++) {
11417 tval = exp(-pp->A * tmpp[3]);
11418 xx += *cnp++ * tval;
11419 yy += *cnp++ * tval;
11420 zz += *cnp++ * tval;
11421 xy += *cnp++ * tval;
11422 xz += *cnp++ * tval;
11423 yz += *cnp++ * tval;
11426 xx *= mop[0] * tmpp[0] * tmpp[0];
11427 yy *= mop[1] * tmpp[1] * tmpp[1];
11428 zz *= mop[2] * tmpp[2] * tmpp[2];
11429 xy *= mop[3] * tmpp[0] * tmpp[1];
11430 xz *= mop[4] * tmpp[0] * tmpp[2];
11431 yz *= mop[5] * tmpp[1] * tmpp[2];
11432 val += xx + yy + zz + xy + xz + yz;
11435 case kGTOType_D5: {
11436 Double d0, d1p, d1n, d2p, d2n;
11437 d0 = d1p = d1n = d2p = d2n = 0;
11438 for (j = 0; j < sp->nprim; j++) {
11439 tval = exp(-pp->A * tmpp[3]);
11440 d0 += *cnp++ * tval;
11441 d1p += *cnp++ * tval;
11442 d1n += *cnp++ * tval;
11443 d2p += *cnp++ * tval;
11444 d2n += *cnp++ * tval;
11447 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
11448 d1p *= mop[1] * tmpp[0] * tmpp[2];
11449 d1n *= mop[2] * tmpp[1] * tmpp[2];
11450 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
11451 d2n *= mop[4] * tmpp[0] * tmpp[1];
11452 val += d0 + d1p + d1n + d2p + d2n;
11455 /* TODO: Support F/F7 and G/G9 type orbitals */
11461 /* Calculate one MO. The input vectors are angstrom unit (changed from bohr unit: 20140520) */
11462 /* mono is the MO number (1-based); 0 denotes "arbitrary vector" */
11464 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)
11466 int ix, iy, iz, n, nn;
11469 if (mp == NULL || mp->bset == NULL)
11471 if (mp->bset->cns == NULL) {
11472 if (sSetupGaussianCoefficients(mp->bset) != 0)
11475 if (mp->bset->natoms_bs > mp->natoms)
11476 return -3; /* Number of atoms is smaller than expected (internal error) */
11478 cp = (Cube *)calloc(sizeof(Cube), 1);
11482 cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
11483 if (cp->dp == NULL) {
11496 /* TODO: use multithread */
11497 tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms_bs * 4);
11504 for (ix = 0; ix < nx; ix++) {
11506 for (iy = 0; iy < ny; iy++) {
11507 for (iz = 0; iz < nz; iz++) {
11508 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
11509 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
11510 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
11511 cp->dp[n++] = sCalcMOPoint(mp, mp->bset, mono, &p, tmp);
11513 if (callback != NULL && n - nn > 100) {
11515 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
11519 return -2; /* User interrupt */
11526 AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
11527 return mp->bset->ncubes - 1;
11530 /* Output values are in angstrom unit (changed from bohr unit: 20140520) */
11532 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
11535 Vector rmin, rmax, r;
11536 Double dr, dx, dy, dz;
11538 if (mp == NULL || mp->bset == NULL)
11542 rmin.x = rmin.y = rmin.z = 1e10;
11543 rmax.x = rmax.y = rmax.z = -1e10;
11544 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
11545 dr = RadiusForAtomicNumber(ap->atomicNumber);
11549 dr = dr * 3.0 + 2.0;
11550 if (rmin.x > r.x - dr)
11552 if (rmin.y > r.y - dr)
11554 if (rmin.z > r.z - dr)
11556 if (rmax.x < r.x + dr)
11558 if (rmax.y < r.y + dr)
11560 if (rmax.z < r.z + dr)
11563 dx = rmax.x - rmin.x;
11564 dy = rmax.y - rmin.y;
11565 dz = rmax.z - rmin.z;
11566 dr = pow(dx * dy * dz / npoints, 1.0/3.0);
11567 *nx = floor(dx / dr + 0.5);
11568 *ny = floor(dy / dr + 0.5);
11569 *nz = floor(dz / dr + 0.5);
11577 xp->x = yp->y = zp->z = dr;
11578 xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
11583 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
11585 if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
11587 return mp->bset->cubes[index];
11591 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
11594 if (mp == NULL || mp->bset == NULL)
11596 for (i = 0; i < mp->bset->ncubes; i++) {
11597 if (mp->bset->cubes[i]->idn == mono)
11604 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
11607 if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
11609 CubeRelease(mp->bset->cubes[index]);
11611 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
11612 if (--(mp->bset->ncubes) == 0) {
11613 free(mp->bset->cubes);
11614 mp->bset->cubes = NULL;
11616 return mp->bset->ncubes;
11620 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
11625 if (mp == NULL || mp->bset == NULL)
11626 return -1; /* Molecule or the basis set information is empty */
11627 cp = MoleculeGetCubeAtIndex(mp, index);
11629 return -2; /* MO not yet calculated */
11630 fp = fopen(fname, "wb");
11632 return -3; /* Cannot create file */
11634 /* Comment lines */
11635 fprintf(fp, "%s MO=%d\n", comment, cp->idn);
11636 fprintf(fp, " MO coefficients\n");
11638 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms_bs),
11639 cp->origin.x * kAngstrom2Bohr, cp->origin.y * kAngstrom2Bohr, cp->origin.z * kAngstrom2Bohr);
11640 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx,
11641 cp->dx.x * kAngstrom2Bohr, cp->dx.y * kAngstrom2Bohr, cp->dx.z * kAngstrom2Bohr);
11642 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny,
11643 cp->dy.x * kAngstrom2Bohr, cp->dy.y * kAngstrom2Bohr, cp->dy.z * kAngstrom2Bohr);
11644 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz,
11645 cp->dz.x * kAngstrom2Bohr, cp->dz.y * kAngstrom2Bohr, cp->dz.z * kAngstrom2Bohr);
11647 /* Atomic information */
11648 for (i = 0; i < mp->natoms; i++) {
11649 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
11650 /* The second number should actually be the effective charge */
11651 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber,
11652 ap->r.x * kAngstrom2Bohr, ap->r.y * kAngstrom2Bohr, ap->r.z * kAngstrom2Bohr);
11654 fprintf(fp, "%5d%5d\n", 1, 1);
11657 for (i = n = 0; i < cp->nx; i++) {
11658 for (j = 0; j < cp->ny; j++) {
11659 for (k = 0; k < cp->nz; k++) {
11660 /* On Windows, the "%e" format writes the exponent in 3 digits, but
11661 this is not standard. So we avoid using %e */
11662 Double d = cp->dp[n++];
11665 if (d >= -1.0e-90 && d <= 1.0e-90) {
11669 exponent = (int)floor(log10(fabs(d)));
11670 base = d * pow(10, -1.0 * exponent);
11672 fprintf(fp, " %8.5fe%+03d", base, exponent);
11673 /* fprintf(fp, " %12.5e", d); */
11674 if (k == cp->nz - 1 || k % 6 == 5)
11683 #pragma mark ====== Marching Cube (for isosurface) ======
11686 MoleculeClearMCube(Molecule *mol, Int nx, Int ny, Int nz, const Vector *origin, Double dx, Double dy, Double dz)
11688 MCube *mc = mol->mcube;
11690 float rgba[8] = { 1, 1, 1, 0.6, 0, 0, 1, 0.6 };
11695 free(mc->c[0].cubepoints);
11696 free(mc->c[0].triangles);
11698 free(mc->c[1].cubepoints);
11699 free(mc->c[1].triangles);
11700 memmove(rgba, mc->c[0].rgba, sizeof(float) * 4);
11701 memmove(rgba + 4, mc->c[1].rgba, sizeof(float) * 4);
11705 if (nx > 0 && ny > 0 && nz > 0) {
11706 mc = (MCube *)calloc(sizeof(MCube), 1);
11708 /* round up to nearest 4N+1 integer */
11712 mc->nx = (nx + 2) / 4 * 4 + 1;
11713 mc->ny = (ny + 2) / 4 * 4 + 1;
11714 mc->nz = (nz + 2) / 4 * 4 + 1;
11715 mc->dx = dx / mc->nx;
11716 mc->dy = dy / mc->ny;
11717 mc->dz = dz / mc->nz;
11718 mc->origin = *origin;
11719 mc->dp = (Double *)malloc(sizeof(Double) * mc->nx * mc->ny * mc->nz);
11720 if (mc->dp == NULL) {
11724 mc->radii = (Double *)calloc(sizeof(Double), mol->natoms);
11725 if (mc->radii == NULL) {
11730 mc->nradii = mol->natoms;
11731 mc->c[0].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
11732 mc->c[1].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
11733 if (mc->c[0].fp == NULL || mc->c[1].fp == NULL) {
11741 for (i = 0; i < mc->nx * mc->ny * mc->nz; i++) {
11742 mc->dp[i] = DBL_MAX;
11744 memmove(mc->c[0].rgba, rgba, sizeof(float) * 4);
11745 memmove(mc->c[1].rgba, rgba + 4, sizeof(float) * 4);
11748 MoleculeCallback_notifyModification(mol, 0);
11752 static int sMarchingCubeTable[256][16] = {
11753 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11754 {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11755 {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11756 {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11757 {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11758 {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11759 {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11760 {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
11761 {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11762 {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11763 {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11764 {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
11765 {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11766 {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
11767 {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
11768 {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11769 {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11770 {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11771 {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11772 {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
11773 {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11774 {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
11775 {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
11776 {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
11777 {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11778 {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
11779 {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
11780 {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
11781 {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
11782 {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
11783 {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
11784 {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
11785 {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11786 {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11787 {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11788 {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
11789 {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11790 {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
11791 {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
11792 {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
11793 {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11794 {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
11795 {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
11796 {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
11797 {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
11798 {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
11799 {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
11800 {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
11801 {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11802 {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
11803 {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
11804 {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11805 {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
11806 {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
11807 {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
11808 {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
11809 {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
11810 {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
11811 {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
11812 {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
11813 {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
11814 {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
11815 {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
11816 {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11817 {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11818 {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11819 {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11820 {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
11821 {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11822 {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
11823 {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
11824 {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
11825 {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11826 {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
11827 {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
11828 {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
11829 {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
11830 {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
11831 {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
11832 {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
11833 {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11834 {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
11835 {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
11836 {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
11837 {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
11838 {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
11839 {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
11840 {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
11841 {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
11842 {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
11843 {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
11844 {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
11845 {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
11846 {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
11847 {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
11848 {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
11849 {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11850 {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
11851 {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
11852 {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
11853 {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
11854 {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
11855 {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11856 {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
11857 {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
11858 {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
11859 {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
11860 {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
11861 {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
11862 {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
11863 {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
11864 {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11865 {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
11866 {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
11867 {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
11868 {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
11869 {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
11870 {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
11871 {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
11872 {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11873 {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
11874 {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
11875 {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
11876 {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
11877 {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
11878 {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11879 {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
11880 {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11881 {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11882 {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11883 {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11884 {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
11885 {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11886 {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
11887 {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
11888 {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
11889 {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11890 {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
11891 {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
11892 {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
11893 {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
11894 {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
11895 {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
11896 {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
11897 {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11898 {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
11899 {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
11900 {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
11901 {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
11902 {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
11903 {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
11904 {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
11905 {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
11906 {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11907 {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
11908 {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
11909 {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
11910 {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
11911 {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
11912 {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11913 {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11914 {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
11915 {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
11916 {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
11917 {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
11918 {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
11919 {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
11920 {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
11921 {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
11922 {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
11923 {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
11924 {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
11925 {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
11926 {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
11927 {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
11928 {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
11929 {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
11930 {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
11931 {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
11932 {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
11933 {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
11934 {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
11935 {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
11936 {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
11937 {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
11938 {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
11939 {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
11940 {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11941 {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
11942 {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
11943 {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11944 {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11945 {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11946 {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
11947 {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
11948 {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
11949 {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
11950 {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
11951 {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
11952 {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
11953 {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
11954 {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
11955 {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
11956 {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
11957 {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11958 {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
11959 {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
11960 {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11961 {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
11962 {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
11963 {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
11964 {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
11965 {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
11966 {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
11967 {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
11968 {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11969 {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
11970 {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
11971 {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
11972 {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
11973 {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
11974 {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11975 {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
11976 {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11977 {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
11978 {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
11979 {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
11980 {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
11981 {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
11982 {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
11983 {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
11984 {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
11985 {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
11986 {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
11987 {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
11988 {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11989 {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
11990 {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
11991 {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11992 {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11993 {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11994 {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
11995 {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
11996 {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11997 {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
11998 {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
11999 {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12000 {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12001 {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
12002 {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12003 {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
12004 {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12005 {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12006 {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12007 {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12008 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
12011 /* Recalculate the MCube */
12012 /* If idn < 0, then the current grid settings and values are unchanged, and */
12013 /* only the marching cubes are regenerated. */
12015 MoleculeUpdateMCube(Molecule *mol, int idn)
12017 Int retval, step, sn;
12018 Int n, ix, iy, iz, nx, ny, nz;
12019 Int nn, iix, iiy, iiz;
12020 Int ncubepoints, c1, c2, c3;
12022 Double thres, *tmp, dd;
12028 if (mol == NULL || mol->bset == NULL || mol->mcube == NULL)
12030 if (mol->bset->cns == NULL) {
12031 if (sSetupGaussianCoefficients(mol->bset) != 0)
12034 if (mol->bset->natoms_bs > mol->natoms)
12035 return -1; /* Number of atoms is smaller than expected */
12040 Double *mobasep, *mop, mopmax;
12041 Double xmin, xmax, ymin, ymax, zmin, zmax;
12042 /* Clear mcube values */
12043 for (ix = 0; ix < mc->nx * mc->ny * mc->nz; ix++) {
12044 mc->dp[ix] = DBL_MAX;
12045 mc->c[0].fp[ix] = 0;
12046 mc->c[1].fp[ix] = 0;
12049 /* Estimate the orbital sizes */
12050 mc->radii = (Double *)realloc(mc->radii, sizeof(Double) * mol->natoms);
12051 if (mc->radii == NULL)
12052 return -2; /* Out of memory */
12053 mc->nradii = mol->natoms;
12054 if (mc->idn == mol->bset->nmos + 1) {
12055 /* Total electron density */
12056 for (ix = 0; ix < mol->natoms; ix++)
12057 mc->radii[ix] = 1.0;
12060 memset(mc->radii, 0, sizeof(Double) * mc->nradii);
12061 mobasep = mol->bset->mo + (mc->idn == 0 ? mol->bset->nmos : mc->idn - 1) * mol->bset->ncomps;
12063 for (ix = 0, sp = mol->bset->shells; ix < mol->bset->nshells; ix++, sp++) {
12064 if (sp->a_idx >= mol->natoms)
12065 continue; /* This may happen when molecule is edited after setting up MO info */
12066 mop = mobasep + sp->m_idx;
12067 for (iy = 0; iy < sp->ncomp; iy++) {
12068 dd = fabs(mop[iy]);
12069 if (dd > mc->radii[sp->a_idx])
12070 mc->radii[sp->a_idx] = dd;
12076 xmin = ymin = zmin = 1e10;
12077 xmax = ymax = zmax = -1e10;
12078 for (ix = 0, ap = mol->atoms; ix < mol->natoms; ix++, ap = ATOM_NEXT(ap)) {
12079 dd = RadiusForAtomicNumber(ap->atomicNumber);
12080 dd = (dd * 2.0 + 1.0) * (mc->radii[ix] / mopmax) * (mc->expand > 0.0 ? mc->expand : 1.0);
12081 mc->radii[ix] = dd;
12084 if (p.x - dd < xmin)
12086 if (p.y - dd < ymin)
12088 if (p.z - dd < zmin)
12090 if (p.x + dd > xmax)
12092 if (p.y + dd > ymax)
12094 if (p.z + dd > zmax)
12097 mc->origin.x = xmin;
12098 mc->origin.y = ymin;
12099 mc->origin.z = zmin;
12100 mc->dx = (xmax - xmin) / mc->nx;
12101 mc->dy = (ymax - ymin) / mc->ny;
12102 mc->dz = (zmax - zmin) / mc->nz;
12105 /* Temporary work area */
12106 tmp = (Double *)calloc(sizeof(Double), mol->bset->natoms_bs * 4);
12110 /* TODO: use multithread */
12117 /* Calculate points within certain distances from atoms */
12118 for (nn = 0, ap = mol->atoms; nn < mol->natoms; nn++, ap = ATOM_NEXT(ap)) {
12119 /* dd = RadiusForAtomicNumber(ap->atomicNumber);
12122 dd = dd * 1.5 + 1.0; */
12123 dd = mc->radii[nn];
12124 p.x = ap->r.x - dd - mc->origin.x;
12125 p.y = ap->r.y - dd - mc->origin.y;
12126 p.z = ap->r.z - dd - mc->origin.z;
12130 iix = c1 + ceil(dd * 2.0 / mc->dx);
12131 iiy = c2 + ceil(dd * 2.0 / mc->dy);
12132 iiz = c3 + ceil(dd * 2.0 / mc->dz);
12145 for (ix = c1; ix <= iix; ix++) {
12146 p.x = mc->origin.x + mc->dx * ix;
12147 for (iy = c2; iy <= iiy; iy++) {
12148 p.y = mc->origin.y + mc->dy * iy;
12149 for (iz = c3; iz <= iiz; iz++) {
12150 n = (ix * ny + iy) * nz + iz;
12151 if (mc->dp[n] == DBL_MAX) {
12152 p.z = mc->origin.z + mc->dz * iz;
12153 if (mc->idn == mol->bset->nmos + 1) {
12154 /* Total electron density */
12155 Int ne_alpha, ne_beta;
12157 ne_alpha = mol->bset->ne_alpha;
12158 ne_beta = mol->bset->ne_beta;
12159 if (mol->bset->rflag == 2 && ne_alpha < ne_beta) {
12160 /* ROHF case: ensure ne_alpha >= ne_beta */
12161 ne_beta = ne_alpha;
12162 ne_alpha = mol->bset->ne_beta;
12164 for (sn = 1; sn <= ne_alpha; sn++) {
12165 dd = sCalcMOPoint(mol, mol->bset, sn, &p, tmp);
12167 if (mol->bset->rflag != 0 && sn <= ne_beta)
12171 if (mol->bset->rflag == 0) {
12172 for (sn = 1; sn <= ne_beta; sn++) {
12173 dd = sCalcMOPoint(mol, mol->bset, sn + mol->bset->ncomps, &p, tmp);
12174 mc->dp[n] += dd * dd;
12178 mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
12187 /* (i * step, j * step, k * step) */
12188 for (ix = 0; ix < nx; ix += step) {
12189 for (iy = 0; iy < ny; iy += step) {
12190 for (iz = 0; iz < nz; iz += step) {
12191 n = (ix * ny + iy) * nz + iz;
12192 if (mc->dp[n] == DBL_MAX) {
12193 p.x = mc->origin.x + mc->dx * ix;
12194 p.y = mc->origin.y + mc->dy * iy;
12195 p.z = mc->origin.z + mc->dz * iz;
12196 mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
12203 /* Intermediate points */
12204 for (step = 4; step > 1; step /= 2) {
12206 for (sn = 0; sn <= 1; sn++) {
12208 for (ix = 0; ix < nx - 1; ix += step) {
12209 for (iy = 0; iy < ny - 1; iy += step) {
12210 for (iz = 0; iz < nz - 1; iz += step) {
12212 thres = mc->thres * (sn == 0 ? 1 : -1);
12213 n = (ix * ny + iy) * nz + iz;
12214 if (mc->dp[n] == DBL_MAX || mc->dp[n + step * (nz * (ny + 1) + 1)] == DBL_MAX)
12217 if (mc->dp[n] >= thres)
12219 /* (ix + step, iy, iz) */
12220 if (mc->dp[n + step * ny * nz] >= thres)
12222 /* (ix, iy + step, iz) */
12223 if (mc->dp[n + step * nz] >= thres)
12225 /* (ix + 4, iy + step, iz) */
12226 if (mc->dp[n + step * nz * (ny + 1)] >= thres)
12228 /* (ix, iy, iz + step) */
12229 if (mc->dp[n + step] >= thres)
12231 if (mc->dp[n + step * (ny * nz + 1)] >= thres)
12233 /* (ix, iy + step, iz + step) */
12234 if (mc->dp[n + step * (nz + 1)] >= thres)
12236 /* (ix + step, iy + step, iz + step) */
12237 if (mc->dp[n + step * (nz * (ny + 1) + 1)] >= thres)
12239 if (flags != 0 && flags != 255) {
12240 /* Calc the intermediate points */
12241 for (iix = 0; iix <= step; iix += hstep) {
12242 for (iiy = 0; iiy <= step; iiy += hstep) {
12243 for (iiz = 0; iiz <= step; iiz += hstep) {
12244 if (iix % step == 0 && iiy % step == 0 && iiz % step == 0)
12246 nn = n + (iix * ny + iiy) * nz + iiz;
12247 if (mc->dp[nn] == DBL_MAX) {
12248 p.x = mc->origin.x + mc->dx * (ix + iix);
12249 p.y = mc->origin.y + mc->dy * (iy + iiy);
12250 p.z = mc->origin.z + mc->dz * (iz + iiz);
12251 mc->dp[nn] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
12267 /* Calculate vertex positions and normal vectors */
12268 for (sn = 0; sn <= 1; sn++) {
12270 thres = mc->thres * (sn == 0 ? 1 : -1);
12272 for (ix = 0; ix < nx - 1; ix++) {
12273 for (iy = 0; iy < ny - 1; iy++) {
12274 for (iz = 0; iz < nz - 1; iz++) {
12276 nn = (ix * ny + iy) * nz + iz;
12278 if (dd0 == DBL_MAX)
12281 dd1 = mc->dp[nn + ny * nz];
12282 if (dd1 != DBL_MAX)
12283 p.x = (dd1 - dd0) / mc->dx;
12284 else if (ix > 0 && (dd1 = mc->dp[nn - ny * nz]) != DBL_MAX)
12285 p.x = (dd0 - dd1) / mc->dx;
12286 else continue; /* Cannot define gradient */
12287 dd1 = mc->dp[nn + nz];
12288 if (dd1 != DBL_MAX)
12289 p.y = (dd1 - dd0) / mc->dy;
12290 else if (iy > 0 && (dd1 = mc->dp[nn - nz]) != DBL_MAX)
12291 p.y = (dd0 - dd1) / mc->dy;
12293 dd1 = mc->dp[nn + 1];
12294 if (dd1 != DBL_MAX)
12295 p.z = (dd1 - dd0) / mc->dz;
12296 else if (iz > 0 && (dd1 = mc->dp[nn - 1]) != DBL_MAX)
12297 p.z = (dd0 - dd1) / mc->dz;
12299 NormalizeVec(&p, &p);
12301 if (n + 3 >= mc->c[sn].ncubepoints) {
12302 /* Expand cubepoints[] array */
12303 mc->c[sn].cubepoints = (MCubePoint *)realloc(mc->c[sn].cubepoints, sizeof(MCubePoint) * (mc->c[sn].ncubepoints + 8192));
12304 if (mc->c[sn].cubepoints == NULL) {
12305 mc->c[sn].ncubepoints = 0;
12309 mc->c[sn].ncubepoints += 8192;
12311 mcp = mc->c[sn].cubepoints + n;
12312 iix = (dd0 >= thres ? 1 : -1);
12313 /* (x, y, z)->(x + 1, y, z) */
12314 dd1 = mc->dp[nn + ny * nz];
12315 if (dd1 != DBL_MAX) {
12316 iiy = (dd1 >= thres ? 1 : -1);
12320 mcp->d = (thres - dd0) / (dd1 - dd0);
12321 mcp->pos[0] = mc->origin.x + mc->dx * (ix + mcp->d);
12322 mcp->pos[1] = mc->origin.y + mc->dy * iy;
12323 mcp->pos[2] = mc->origin.z + mc->dz * iz;
12324 mcp->grad[0] = p.x;
12325 mcp->grad[1] = p.y;
12326 mcp->grad[2] = p.z;
12331 /* (x, y, z)->(x, y + 1, z) */
12332 dd1 = mc->dp[nn + nz];
12333 if (dd1 != DBL_MAX) {
12334 iiy = (dd1 >= thres ? 1 : -1);
12337 mcp->key = nn * 3 + 1;
12338 mcp->d = (thres - dd0) / (dd1 - dd0);
12339 mcp->pos[0] = mc->origin.x + mc->dx * ix;
12340 mcp->pos[1] = mc->origin.y + mc->dy * (iy + mcp->d);
12341 mcp->pos[2] = mc->origin.z + mc->dz * iz;
12342 mcp->grad[0] = p.x;
12343 mcp->grad[1] = p.y;
12344 mcp->grad[2] = p.z;
12349 /* (x, y, z)->(x, y, z + 1) */
12350 dd1 = mc->dp[nn + 1];
12351 if (dd1 != DBL_MAX) {
12352 iiy = (dd1 >= thres ? 1 : -1);
12355 mcp->key = nn * 3 + 2;
12356 mcp->d = (thres - dd0) / (dd1 - dd0);
12357 mcp->pos[0] = mc->origin.x + mc->dx * ix;
12358 mcp->pos[1] = mc->origin.y + mc->dy * iy;
12359 mcp->pos[2] = mc->origin.z + mc->dz * (iz + mcp->d);
12360 mcp->grad[0] = p.x;
12361 mcp->grad[1] = p.y;
12362 mcp->grad[2] = p.z;
12370 if (n < mc->c[sn].ncubepoints)
12371 mc->c[sn].cubepoints[n].key = -1; /* End mark */
12373 if (ncubepoints < 3) {
12374 /* Less than 3 points: no triangles */
12375 if (mc->c[sn].ntriangles > 0)
12376 mc->c[sn].triangles[0] = -1; /* End mark */
12380 /* Create triangle table */
12382 for (ix = 0; ix < nx - 1; ix++) {
12383 for (iy = 0; iy < ny - 1; iy++) {
12384 for (iz = 0; iz < nz - 1; iz++) {
12385 nn = (ix * ny + iy) * nz + iz;
12387 if ((dd = mc->dp[nn]) == DBL_MAX)
12389 else if (dd >= thres)
12391 if ((dd = mc->dp[nn + ny * nz]) == DBL_MAX)
12393 else if (dd >= thres)
12395 if ((dd = mc->dp[nn + ny * nz + nz]) == DBL_MAX)
12397 else if (dd >= thres)
12399 if ((dd = mc->dp[nn + nz]) == DBL_MAX)
12401 else if (dd >= thres)
12403 if ((dd = mc->dp[nn + 1]) == DBL_MAX)
12405 else if (dd >= thres)
12407 if ((dd = mc->dp[nn + ny * nz + 1]) == DBL_MAX)
12409 else if (dd >= thres)
12411 if ((dd = mc->dp[nn + ny * nz + nz + 1]) == DBL_MAX)
12413 else if (dd >= thres)
12415 if ((dd = mc->dp[nn + nz + 1]) == DBL_MAX)
12417 else if (dd >= thres)
12419 for (iiy = 0; iiy < 15; iiy++) {
12420 nn = sMarchingCubeTable[iix][iiy];
12423 /* key index for edges 0-11 */
12425 case 0: iiz = (( ix * ny + iy ) * nz + iz ) * 3; break;
12426 case 1: iiz = (((ix + 1) * ny + iy ) * nz + iz ) * 3 + 1; break;
12427 case 2: iiz = (( ix * ny + iy + 1) * nz + iz ) * 3; break;
12428 case 3: iiz = (( ix * ny + iy ) * nz + iz ) * 3 + 1; break;
12429 case 4: iiz = (( ix * ny + iy ) * nz + iz + 1) * 3; break;
12430 case 5: iiz = (((ix + 1) * ny + iy ) * nz + iz + 1) * 3 + 1; break;
12431 case 6: iiz = (( ix * ny + iy + 1) * nz + iz + 1) * 3; break;
12432 case 7: iiz = (( ix * ny + iy ) * nz + iz + 1) * 3 + 1; break;
12433 case 8: iiz = (( ix * ny + iy ) * nz + iz ) * 3 + 2; break;
12434 case 9: iiz = (((ix + 1) * ny + iy ) * nz + iz ) * 3 + 2; break;
12435 case 10: iiz = (((ix + 1) * ny + iy + 1) * nz + iz ) * 3 + 2; break;
12436 case 11: iiz = (( ix * ny + iy + 1) * nz + iz ) * 3 + 2; break;
12438 /* Skip this triangle */
12439 iiy = (iiy - iiy % 3) + 2;
12443 /* Look for the key index in cubepoints */
12445 c3 = ncubepoints - 1;
12446 mcp = mc->c[sn].cubepoints;
12449 /* c1 is always less than c3 */
12450 if (c1 + 1 == c3) {
12451 /* end of search */
12452 if (mcp[c1].key == iiz) {
12454 } else if (mcp[c3].key == iiz) {
12461 c2 = (c1 + c3) / 2;
12462 w = mcp[c2].key - iiz;
12472 /* Not found: skip this triangle */
12473 iiy = (iiy - iiy % 3) + 2;
12477 if (n + 1 >= mc->c[sn].ntriangles) {
12478 /* Expand triangles[] array */
12479 mc->c[sn].triangles = (Int *)realloc(mc->c[sn].triangles, sizeof(Int) * (mc->c[sn].ntriangles + 8192));
12480 if (mc->c[sn].triangles == NULL) {
12481 mc->c[sn].ntriangles = 0;
12485 mc->c[sn].ntriangles += 8192;
12487 mc->c[sn].triangles[n] = c2;
12493 if (n < mc->c[sn].ntriangles)
12494 mc->c[sn].triangles[n] = -1; /* End mark */
12496 /* Estimate the normal vector */
12497 for (n = 0, ip = mc->c[sn].triangles; ip[n] >= 0; n += 3) {
12499 for (ix = 0; ix < 3; ix++) {
12500 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
12501 v[ix].x = mcp->pos[0];
12502 v[ix].y = mcp->pos[1];
12503 v[ix].z = mcp->pos[2];
12505 VecDec(v[2], v[0]);
12506 VecDec(v[1], v[0]);
12507 VecCross(v[0], v[1], v[2]);
12508 NormalizeVec(v, v);
12509 for (ix = 0; ix < 3; ix++) {
12510 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
12511 mcp->grad[0] += v[0].x;
12512 mcp->grad[1] += v[0].y;
12513 mcp->grad[2] += v[0].z;
12516 for (n = 0, mcp = mc->c[sn].cubepoints; mcp->key >= 0; mcp++) {
12517 if (mcp->grad[0] != 0.0 || mcp->grad[1] != 0.0 || mcp->grad[2] != 0.0) {
12518 dd = 1.0 / sqrt(mcp->grad[0] * mcp->grad[0] + mcp->grad[1] * mcp->grad[1] + mcp->grad[2] * mcp->grad[2]);
12519 if (mc->thres < 0.0)
12521 mcp->grad[0] *= dd;
12522 mcp->grad[1] *= dd;
12523 mcp->grad[2] *= dd;
12528 MoleculeCallback_notifyModification(mol, 0);
12532 char *MyAppCallback_getDocumentHomeDir(void);
12536 asprintf(&s, "%s/%s", MyAppCallback_getDocumentHomeDir(), "mcube_log.txt");
12537 fp = fopen(s, "w");
12540 for (n = 0; n < mc->nx * mc->ny * mc->nz; n++) {
12541 if (mc->dp[n] == DBL_MAX)
12543 if (dmax < mc->dp[n])
12545 if (dmin > mc->dp[n])
12552 dmax = 1.001 * dmax;
12553 fprintf(fp, "thres = %g = 100\n", mc->thres);
12554 for (iz = 0; iz < mc->nz; iz++) {
12555 fprintf(fp, "z = %d\n", iz);
12556 for (iy = 0; iy < mc->ny; iy++) {
12557 for (ix = 0; ix < mc->nx; ix++) {
12558 n = (ix * ny + iy) * nz + iz;
12561 fprintf(fp, " XXX ");
12563 dd = dd * 100 / mc->thres;
12566 else if (dd < -999.0)
12568 fprintf(fp, "%4d ", (int)(dd));
12576 for (sn = 0; sn <= 1; sn++) {
12577 for (n = 0; n < mc->c[sn].ncubepoints; n++) {
12578 MCubePoint *mcp = mc->c[sn].cubepoints + n;
12583 iz = nn / 3 % mc->nz;
12584 iy = nn / (3 * mc->nz) % mc->ny;
12585 ix = nn / (3 * mc->nz * mc->ny);
12586 fprintf(fp, "%c%d:[%d,%d,%d,%d] (%g,[%g,%g,%g],[%g,%g,%g])\n", (sn == 0 ? 'p' : 'P'),
12587 n, ix, iy, iz, iix,
12588 mcp->d, mcp->pos[0], mcp->pos[1], mcp->pos[2], mcp->grad[0], mcp->grad[1], mcp->grad[2]);
12590 for (n = 0; n < mc->c[sn].ntriangles; n += 3) {
12591 if (mc->c[sn].triangles[n] < 0)
12593 fprintf(fp, "%c%d:(%d,%d,%d)\n", (sn == 0 ? 't' : 'T'), n / 3,
12594 mc->c[sn].triangles[n], mc->c[sn].triangles[n + 1], mc->c[sn].triangles[n + 2]);
12604 MoleculeDeallocateMCube(MCube *mcube)
12607 free(mcube->radii);
12608 free(mcube->c[0].cubepoints);
12609 free(mcube->c[0].triangles);
12610 free(mcube->c[1].cubepoints);
12611 free(mcube->c[1].triangles);