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]);
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)
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 /* Update the number of components (if not yet determined) */
2751 if (bset->ncomps < shellp->m_idx + shellp->ncomp)
2752 bset->ncomps = shellp->m_idx + shellp->ncomp;
2756 /* Add a set of gaussian primitive coefficients (not undoable) */
2758 MoleculeAddGaussianPrimitiveCoefficients(Molecule *mol, Double exponent, Double contraction, Double contraction_sp)
2763 return -1; /* Molecule is empty */
2766 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2768 return -2; /* Low memory */
2770 primp = AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), bset->npriminfos, NULL);
2772 return -2; /* Low memory */
2773 primp->A = exponent;
2774 primp->C = contraction;
2775 primp->Csp = contraction_sp;
2779 /* Get the shell information from the component index */
2780 /* The outLabel must have space for at least 23 non-Null characters */
2782 MoleculeGetGaussianComponentInfo(Molecule *mol, Int comp_idx, Int *outAtomIdx, char *outLabel, Int *outShellIdx)
2787 if (mol == NULL || (bset = mol->bset) == NULL)
2788 return -1; /* No basis set info */
2789 if (comp_idx < 0 || comp_idx >= bset->ncomps)
2790 return -2; /* Component index out of range */
2791 for (si = 0, shellp = bset->shells; si < bset->nshells; si++, shellp++) {
2792 if (comp_idx >= shellp->ncomp) {
2793 comp_idx -= shellp->ncomp;
2796 static const char *type_p = "xyz";
2797 static const char *type_d = "xxyyzzxyxzyz";
2798 static const char *type_d5[] = {"xy","yz","zz", "xz", "xx-yy"};
2799 static const char *type_f = "xxxyyyzzzxxyxxzxyyyyzxzzyzzxyz";
2800 static const char *type_f7[] = {"x3-3xy2", "x2z-y2z", "x(5z2-r2)", "z(5z2-3r2)", "y(5z2-r2)", "xyz", "3x2y-y3"};
2801 static const char *type_g[] = {"x4", "y4", "z4", "x3y", "x3z", "xy3", "y3z", "xz3", "yz3", "x2y2", "x2z2", "y2z2", "x2yz", "x2yz", "xyz2"};
2802 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)"};
2803 *outAtomIdx = shellp->a_idx;
2805 switch (shellp->sym) {
2807 strcpy(outLabel, "S");
2811 outLabel[1] = type_p[comp_idx];
2816 strcpy(outLabel, "S");
2819 outLabel[1] = type_p[comp_idx - 1];
2825 strncpy(outLabel + 1, type_d + comp_idx * 2, 2);
2830 strcpy(outLabel + 1, type_d5[comp_idx]);
2834 strncpy(outLabel + 1, type_f + comp_idx * 3, 3);
2839 strcpy(outLabel + 1, type_f7[comp_idx]);
2843 strcpy(outLabel + 1, type_g[comp_idx]);
2847 strcpy(outLabel + 1, type_g9[comp_idx]);
2850 return -3; /* Unsupported orbital type (internal error) */
2855 return -4; /* comp_idx out of range? (internal error) */
2858 /* Set MO coefficients for idx-th MO (1-based) */
2860 MoleculeSetMOCoefficients(Molecule *mol, Int idx, Double energy, Int ncomps, Double *coeffs)
2865 return -1; /* Molecule is empty */
2868 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2870 return -2; /* Low memory */
2872 if (bset->nmos == 0) {
2873 if (bset->nshells > 0) {
2874 /* Shell info is already set: calculate the number of MOs from there */
2875 for (i = n = 0; i < bset->nshells; i++)
2876 n += bset->shells[i].ncomp;
2878 } else if (ncomps > 0) {
2879 bset->ncomps = ncomps;
2881 if (bset->rflag == 0)
2882 bset->nmos = bset->ncomps * 2;
2884 bset->nmos = bset->ncomps;
2885 if (bset->nmos <= 0)
2886 return -3; /* Bad or inconsistent number of MOs */
2887 bset->mo = (Double *)calloc(sizeof(Double), (bset->nmos + 1) * bset->ncomps);
2888 bset->moenergies = (Double *)calloc(sizeof(Double), bset->nmos + 1);
2889 if (bset->mo == NULL || bset->moenergies == NULL) {
2890 if (bset->mo != NULL)
2892 if (bset->moenergies != NULL)
2893 free(bset->moenergies);
2895 bset->moenergies = NULL;
2897 return -2; /* Low memory */
2901 idx = -idx + bset->ncomps;
2902 if (idx < 0 || idx > bset->nmos)
2903 return -4; /* Bad MO index */
2905 idx = bset->nmos; /* Arbitrary vector */
2908 if (energy != -1000000)
2909 bset->moenergies[idx] = energy;
2910 if (ncomps < bset->ncomps)
2911 return -5; /* Insufficient number of data provided */
2912 memmove(bset->mo + (idx * bset->ncomps), coeffs, sizeof(Double) * bset->ncomps);
2913 if (bset->cns != NULL) {
2914 /* Clear the cached values */
2922 /* Get MO coefficients for idx-th MO (1-based) */
2923 /* Caution: *ncoeffs and *coeffs should be valid _before_ calling this function, i.e. */
2924 /* *ncoeffs = 0 && *coeffs = NULL or *coeffs is a valid memory pointer and *ncoeffs */
2925 /* properly designates the memory size as an array of Doubles. */
2927 MoleculeGetMOCoefficients(Molecule *mol, Int idx, Double *energy, Int *ncoeffs, Double **coeffs)
2931 return -1; /* Molecule is empty */
2933 if (bset == NULL || bset->ncomps <= 0)
2934 return -2; /* No basis set info */
2936 idx = -idx + bset->ncomps;
2937 if (idx < 0 || idx > bset->nmos)
2938 return -3; /* MO index out of range */
2940 idx = bset->nmos; /* Arbitrary vector */
2944 *energy = bset->moenergies[idx];
2945 if (ncoeffs != NULL && coeffs != NULL) {
2946 if (*ncoeffs < bset->ncomps || *coeffs == NULL) {
2947 if (*coeffs != NULL)
2948 free(*coeffs); /* Caution: possible cause of SIGBUS if *coeff is not initialized properly */
2949 *coeffs = (Double *)calloc(sizeof(Double), bset->ncomps);
2950 *ncoeffs = bset->ncomps;
2952 memmove(*coeffs, bset->mo + (idx * bset->ncomps), sizeof(Double) * bset->ncomps);
2957 /* Set Basic MO Info. rflag: 0, UHF; 1, RHF; 2, ROHF; -1, clear
2958 ne_alpha: number of alpha electrons, ne_beta: number of beta electrons */
2960 MoleculeSetMOInfo(Molecule *mol, Int rflag, Int ne_alpha, Int ne_beta)
2963 if (mol == NULL || mol->natoms == 0)
2964 return -1; /* Molecule is empty */
2966 if (mol->bset != NULL) {
2967 BasisSetRelease(mol->bset);
2974 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2976 return -2; /* Low memory */
2978 bset->natoms_bs = mol->natoms;
2979 bset->ne_alpha = ne_alpha;
2980 bset->ne_beta = ne_beta;
2981 bset->rflag = rflag;
2986 sSeparateTokens(char *inString, char **outPtr, int size)
2990 for (i = 0; i < size; i++) {
2991 p = strtok((i == 0 ? inString : NULL), " \r\n");
3002 sReadNumberArray(void *basep, Int *countp, Int size, Int num, FILE *fp, int *lnp)
3006 *((void **)basep) = NULL;
3008 if (AssignArray(basep, countp, size, num - 1, NULL) == NULL)
3009 return 4; /* Out of memory */
3011 while (ReadLine(buf, sizeof buf, fp, lnp) > 0) {
3012 char *tokens[16], *p;
3013 sSeparateTokens(buf, tokens, 16);
3014 for (i = 0; i < 16; i++) {
3015 if (tokens[i] == NULL)
3017 if (size == sizeof(Int)) {
3018 (*((Int **)basep))[n] = strtol(tokens[i], &p, 0);
3019 } else if (size == sizeof(Double)) {
3020 (*((Double **)basep))[n] = strtod(tokens[i], &p);
3021 } else return -1; /* Internal error */
3022 if (tokens[i] == p || *p != 0)
3023 return 1; /* Non-digit character */
3025 if (i < 15 && tokens[i + 1] != NULL)
3026 return 2; /* Too many data */
3027 return 0; /* All data are successfully read */
3031 return 3; /* Unexpected EOF */
3035 sSetupGaussianCoefficients(BasisSet *bset)
3042 /* Cache the contraction coefficients for efficient calculation */
3043 /* Sum up the number of components for all primitives */
3044 for (i = k = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3046 k += sp->nprim * sp->ncomp;
3048 /* Allocate memory for the cached values */
3049 if (AssignArray(&bset->cns, &bset->ncns, sizeof(Double), k - 1, NULL) == NULL)
3051 /* Iterate over all primitives */
3053 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3054 for (j = 0, pp = bset->priminfos + sp->p_idx; j < sp->nprim; j++, pp++) {
3057 // (8 alpha^3/pi^3)^0.25 exp(-alpha r^2)
3058 *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
3061 // (128 alpha^5/pi^3)^0.25 [x|y|z]exp(-alpha r^2)
3062 d = pp->C * pow(pp->A, 1.25) * 1.425410941;
3068 *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
3069 d = pp->Csp * pow(pp->A, 1.25) * 1.425410941;
3075 // xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2)
3076 // xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
3077 d = pp->C * pow(pp->A, 1.75);
3078 dp[0] = dp[1] = dp[2] = d * 1.645922781;
3079 dp[3] = dp[4] = dp[5] = d * 2.850821881;
3083 // 3zz-rr: (128 alpha^7/9pi^3)^0.25 (3zz-rr)exp(-alpha r^2)
3084 // xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
3085 // xx-yy: (128 alpha^7/pi^3)^0.25 (xx-yy)exp(-alpha r^2)
3086 d = pp->C * pow(pp->A, 1.75);
3087 dp[0] = d * 0.822961390;
3088 dp[1] = dp[2] = dp[4] = d * 2.850821881;
3089 dp[3] = d * 1.425410941;
3092 /* TODO: Support F/F7 and G/G9 type orbitals */
3100 MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char **errbuf)
3105 int natoms, nbasis, i, j, k, n, mxbond, retval, ncomps, nprims, nelec;
3119 bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
3123 fp = fopen(fname, "rb");
3125 s_append_asprintf(errbuf, "Cannot open file");
3129 natoms = nbasis = -1;
3137 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3140 if (lineNumber == 2) {
3143 bset->rflag = 0; /* UHF */
3144 else if (buf[11] == 'O')
3145 bset->rflag = 2; /* ROHF */
3146 else bset->rflag = 1; /* RHF */
3149 while (p > buf && *p == ' ')
3152 sSeparateTokens(buf + 42, tokens, 16);
3153 if (strcmp(buf, "Number of atoms") == 0) {
3154 if (tokens[1] == NULL || (natoms = atoi(tokens[1])) <= 0) {
3155 s_append_asprintf(errbuf, "Line %d: strange number of atoms: %s", lineNumber, tokens[1]);
3159 bset->natoms_bs = natoms;
3160 /* Allocate atom records (all are empty for now) */
3161 AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms - 1, NULL);
3162 /* Also allocate atom position array for MO calculations */
3163 /* AssignArray(&bset->pos, &bset->natoms, sizeof(Vector), natoms - 1, NULL); */
3164 /* Also allocate nuclear charge array */
3165 bset->nuccharges = (Double *)calloc(sizeof(Double), natoms);
3166 } else if (strcmp(buf, "Number of electrons") == 0) {
3167 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
3168 s_append_asprintf(errbuf, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
3173 } else if (strcmp(buf, "Number of alpha electrons") == 0) {
3174 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
3175 s_append_asprintf(errbuf, "Line %d: strange number of alpha electrons: %s", lineNumber, tokens[1]);
3180 } else if (strcmp(buf, "Number of beta electrons") == 0) {
3181 if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
3182 s_append_asprintf(errbuf, "Line %d: strange number of beta electrons: %s", lineNumber, tokens[1]);
3187 if (bset->ne_alpha + bset->ne_beta != nelec) {
3188 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);
3192 } else if (strcmp(buf, "Number of basis functions") == 0) {
3193 if (tokens[1] == NULL || (nbasis = atoi(tokens[1])) <= 0) {
3194 s_append_asprintf(errbuf, "Line %d: strange number of basis functions: %s", lineNumber, tokens[1]);
3198 } else if (strcmp(buf, "Atomic numbers") == 0) {
3199 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
3200 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
3204 if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms, fp, &lineNumber) != 0) {
3205 s_append_asprintf(errbuf, "Line %d: cannot read atomic numbers", lineNumber);
3209 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
3210 ap->atomicNumber = iary[i];
3211 bset->nuccharges[i] = iary[i];
3212 ElementToString(ap->atomicNumber, ap->element);
3213 memmove(ap->aname, ap->element, 4);
3214 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
3219 } else if (strcmp(buf, "Nuclear charges") == 0) {
3220 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
3221 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
3225 if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms, fp, &lineNumber) != 0) {
3226 s_append_asprintf(errbuf, "Line %d: cannot read nuclear charges", lineNumber);
3230 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
3231 bset->nuccharges[i] = dary[i];
3235 } else if (strcmp(buf, "Current cartesian coordinates") == 0) {
3236 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * 3) {
3237 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of cartesian coordinates: %s", lineNumber, tokens[2]);
3241 if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms * 3, fp, &lineNumber) != 0) {
3242 s_append_asprintf(errbuf, "Line %d: cannot read cartesian coordinates", lineNumber);
3246 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
3247 ap->r.x = dary[i * 3] * kBohr2Angstrom;
3248 ap->r.y = dary[i * 3 + 1] * kBohr2Angstrom;
3249 ap->r.z = dary[i * 3 + 2] * kBohr2Angstrom;
3253 } else if (strcmp(buf, "MxBond") == 0) {
3254 if (tokens[1] == NULL || (mxbond = atoi(tokens[1])) <= 0) {
3255 s_append_asprintf(errbuf, "Line %d: strange number of bonds per atom: %s", lineNumber, tokens[1]);
3259 } else if (strcmp(buf, "IBond") == 0) {
3261 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * mxbond) {
3262 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of bonds: %s", lineNumber, tokens[2]);
3266 if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms * mxbond, fp, &lineNumber) != 0) {
3267 s_append_asprintf(errbuf, "Line %d: cannot read bond information", lineNumber);
3271 bonds = (Int *)malloc(sizeof(Int) * (mxbond * 2 + 1));
3272 for (i = 0; i < natoms; i++) {
3273 for (j = k = 0; j < mxbond; j++) {
3274 n = iary[i * mxbond + j] - 1;
3276 /* Connect atom i and atom n */
3282 bonds[k] = kInvalidIndex;
3283 MoleculeAddBonds(mp, k / 2, bonds, NULL, 1);
3289 } else if (strcmp(buf, "Shell types") == 0) {
3290 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0) {
3291 s_append_asprintf(errbuf, "Line %d: wrong number of shell types: %s", lineNumber, tokens[2]);
3295 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
3296 s_append_asprintf(errbuf, "Line %d: cannot read shell types", lineNumber);
3300 /* Allocate ShellInfo table and store shell type information */
3301 AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), nary - 1, NULL);
3302 for (i = n = 0, sp = bset->shells; i < nary; i++, sp++) {
3304 case 0: sp->sym = kGTOType_S; sp->ncomp = 1; break;
3305 case 1: sp->sym = kGTOType_P; sp->ncomp = 3; break;
3306 case -1: sp->sym = kGTOType_SP; sp->ncomp = 4; break;
3307 case 2: sp->sym = kGTOType_D; sp->ncomp = 6; break;
3308 case -2: sp->sym = kGTOType_D5; sp->ncomp = 5; break;
3309 case 3: sp->sym = kGTOType_F; sp->ncomp = 10; break;
3310 case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break;
3311 case 4: sp->sym = kGTOType_G; sp->ncomp = 15; break;
3312 case -4: sp->sym = kGTOType_G9; sp->ncomp = 9; break;
3314 s_append_asprintf(errbuf, "Line %d: unsupported shell type %d", lineNumber, iary[i]);
3321 bset->ncomps = ncomps = n;
3324 } else if (strcmp(buf, "Number of primitives per shell") == 0) {
3325 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
3326 s_append_asprintf(errbuf, "Line %d: wrong size of the primitive table: %s", lineNumber, tokens[2]);
3330 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
3331 s_append_asprintf(errbuf, "Line %d: cannot read primitive table", lineNumber);
3335 for (i = n = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3336 sp->nprim = iary[i];
3343 } else if (strcmp(buf, "Shell to atom map") == 0) {
3344 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
3345 s_append_asprintf(errbuf, "Line %d: wrong size of the shell-to-atom map: %s", lineNumber, tokens[2]);
3349 if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
3350 s_append_asprintf(errbuf, "Line %d: cannot read shell-to-atom table", lineNumber);
3354 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3355 sp->a_idx = iary[i] - 1;
3359 } else if (strcmp(buf, "Primitive exponents") == 0) {
3360 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nprims) {
3361 s_append_asprintf(errbuf, "Line %d: wrong number of primitive exponents: %s", lineNumber, tokens[2]);
3365 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3366 s_append_asprintf(errbuf, "Line %d: cannot read primitive exponents", lineNumber);
3370 /* Allocate PrimInfo table */
3371 AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), nprims - 1, NULL);
3372 for (i = 0, pp = bset->priminfos; i < nprims; i++, pp++) {
3377 } else if (strcmp(buf, "Contraction coefficients") == 0) {
3378 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
3379 s_append_asprintf(errbuf, "Line %d: wrong number of contraction coefficients: %s", lineNumber, tokens[2]);
3383 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3384 s_append_asprintf(errbuf, "Line %d: cannot read contraction coefficients", lineNumber);
3388 for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
3393 } else if (strcmp(buf, "P(S=P) Contraction coefficients") == 0) {
3394 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
3395 s_append_asprintf(errbuf, "Line %d: wrong number of P(S=P) contraction coefficients: %s", lineNumber, tokens[2]);
3399 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3400 s_append_asprintf(errbuf, "Line %d: cannot read P(S=P) contraction coefficients", lineNumber);
3404 for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
3409 } else if (strcmp(buf, "Alpha Orbital Energies") == 0) {
3410 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
3411 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha orbitals: %s", lineNumber, tokens[2]);
3415 if (sReadNumberArray(&bset->moenergies, &bset->nmos, sizeof(Double), i, fp, &lineNumber) != 0) {
3416 s_append_asprintf(errbuf, "Line %d: cannot read alpha orbital energies", lineNumber);
3420 } else if (strcmp(buf, "Alpha MO coefficients") == 0) {
3421 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
3422 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha MO coefficients: %s", lineNumber, tokens[2]);
3426 if (sReadNumberArray(&bset->mo, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3427 s_append_asprintf(errbuf, "Line %d: cannot read MO coefficients", lineNumber);
3431 } else if (strcmp(buf, "Beta Orbital Energies") == 0) {
3432 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
3433 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta orbitals: %s", lineNumber, tokens[2]);
3437 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3438 s_append_asprintf(errbuf, "Line %d: cannot read beta orbital energies", lineNumber);
3442 bset->moenergies = (Double *)realloc(bset->moenergies, sizeof(Double) * 2 * ncomps);
3443 bset->nmos = ncomps * 2;
3444 bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);
3445 memmove(bset->moenergies + ncomps, dary, sizeof(Double) * ncomps);
3446 memset(bset->mo + ncomps * ncomps, 0, sizeof(Double) * ncomps * ncomps);
3449 } else if (strcmp(buf, "Beta MO coefficients") == 0) {
3450 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
3451 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta MO coefficients: %s", lineNumber, tokens[2]);
3455 if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3456 s_append_asprintf(errbuf, "Line %d: cannot read alpha MO coefficients", lineNumber);
3460 bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps); /* Should be unnecessary, just in case */
3461 memmove(bset->mo + ncomps * ncomps, dary, sizeof(Double) * ncomps * ncomps);
3464 } else if (strcmp(buf, "Total SCF Density") == 0) {
3465 if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * (ncomps + 1) / 2) {
3466 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of SCF densities: %s", lineNumber, tokens[2]);
3470 if (sReadNumberArray(&bset->scfdensities, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3471 s_append_asprintf(errbuf, "Line %d: cannot read SCF densities", lineNumber);
3477 if (mp->natoms == 0) {
3478 s_append_asprintf(errbuf, "Atom information is missing");
3482 if (bset->shells == NULL || bset->priminfos == NULL) {
3483 s_append_asprintf(errbuf, "Gaussian primitive information is missing");
3487 if (bset->mo == NULL) {
3488 s_append_asprintf(errbuf, "MO coefficients were not found");
3492 if (sSetupGaussianCoefficients(bset) != 0) {
3493 s_append_asprintf(errbuf, "Internal error during setup MO calculation");
3506 if (mp->bset != NULL) {
3507 BasisSetRelease(mp->bset);
3513 Panic("low memory while reading fchk file %s", fname);
3514 return -1; /* not reached */
3518 MoleculeLoadGamessDatFile(Molecule *mol, const char *fname, char **errbuf)
3523 int lineNumber, i, j, k, len, natoms = 0;
3530 Vector *vbuf = NULL;
3532 int optimizing = 0, status = 0;
3536 mol = MoleculeNew();
3538 if (mol->natoms == 0)
3541 fp = fopen(fname, "rb");
3543 s_append_asprintf(errbuf, "Cannot open file");
3547 /* ESP is cleared (not undoable!) */
3548 if (mol->elpots != NULL) {
3555 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3558 if (strncmp(buf, " $DATA", 6) == 0) {
3559 /* Initial geometry */
3561 vbuf = (Vector *)calloc(sizeof(Vector), mol->natoms);
3564 ReadLine(buf, sizeof buf, fp, &lineNumber); /* Title */
3565 ReadLine(buf, sizeof buf, fp, &lineNumber); /* Symmetry */
3566 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3567 if (strncmp(buf, " $END", 5) == 0)
3569 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3570 s_append_asprintf(errbuf, "Line %d: bad format in $DATA section", lineNumber);
3576 memset(&a, 0, sizeof(a));
3577 strncpy(a.aname, sval, 4);
3581 a.atomicNumber = (Int)dval[0];
3582 strncpy(a.element, ElementToString(a.atomicNumber, sval), 3);
3583 a.type = AtomTypeEncodeToUInt(a.element);
3584 a.weight = WeightForAtomicNumber(a.atomicNumber);
3585 MoleculeCreateAnAtom(mol, &a, mol->natoms);
3588 if (i >= mol->natoms) {
3589 s_append_asprintf(errbuf, "Line %d: too many atoms", lineNumber);
3593 if ((ap = ATOM_AT_INDEX(mol->atoms, i))->atomicNumber != dval[0]) {
3594 s_append_asprintf(errbuf, "Line %d: atomic number does not match", lineNumber);
3598 vbuf[i].x = dval[1];
3599 vbuf[i].y = dval[2];
3600 vbuf[i].z = dval[3];
3602 /* Skip until a blank line is found */
3603 /* 2013.6.11. Line including "PM3" is also recognized as the end of atom */
3604 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3605 for (j = 0; buf[j] == ' '; j++);
3606 if (buf[j] == '\n' || strncmp(buf + j, "PM3", 3) == 0)
3613 /* Set atom positions */
3615 if (natoms < mol->natoms) {
3616 s_append_asprintf(errbuf, "Line %d: too few atoms", lineNumber);
3620 ig = IntGroupNewWithPoints(0, natoms, -1);
3621 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, natoms, vbuf);
3622 IntGroupRelease(ig);
3625 vbuf = (Vector *)calloc(sizeof(Vector), natoms);
3626 nframes = MoleculeGetNumberOfFrames(mol);
3630 } else if (strstr(buf, "DATA FROM NSERCH") != NULL || (strstr(buf, "RESULTS FROM SUCCESSFUL") != NULL && (n1 = 1))) {
3631 /* Skip until the separator line is read (three or four lines) */
3635 s_append_asprintf(errbuf, "Line %d: the separator line at the top of the coordinates is not found: bad format?", lineNumber);
3639 ReadLine(buf, sizeof buf, fp, &lineNumber);
3640 } while (strstr(buf, "----------------------------") == NULL);
3641 for (i = 0; i < natoms; i++) {
3642 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3643 s_append_asprintf(errbuf, "Unexpected end of file in reading NSERCH data");
3647 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3648 s_append_asprintf(errbuf, "Line %d: bad format in NSERCH coordinate data", lineNumber);
3652 vbuf[i].x = dval[1];
3653 vbuf[i].y = dval[2];
3654 vbuf[i].z = dval[3];
3656 ig = IntGroupNewWithPoints(nframes, 1, -1);
3657 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, natoms, vbuf, 0, NULL);
3658 IntGroupRelease(ig);
3661 optimizing = 1; /* Flag to skip reading the VEC group */
3665 } else if (strstr(buf, "E(UHF)") != NULL || (strstr(buf, "E(RHF)") != NULL && (n1 = 1)) || (strstr(buf, "E(ROHF)") != NULL && (n1 = 2))) {
3666 if (mol->bset == NULL) {
3667 i = MoleculeSetMOInfo(mol, n1, 0, 0);
3669 s_append_asprintf(errbuf, "Line %d: cannot allocate basis set internal buffer", lineNumber);
3674 } else if (strncmp(buf, " $VEC", 5) == 0) {
3676 /* Read the vec group */
3677 if (mol->bset == NULL || mol->bset->ncomps == 0)
3678 continue; /* Just ignore */
3680 continue; /* Ignore VEC group during optimization */
3681 coeffs = (Double *)calloc(sizeof(Double), mol->bset->ncomps);
3682 if (coeffs == NULL) {
3683 s_append_asprintf(errbuf, "Line %d: low memory during $VEC", lineNumber);
3688 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3690 if (strncmp(buf, " $END", 5) == 0)
3692 while ((j = 5 + (k % 5) * 15) <= len && buf[j] != 0 && buf[j] != '\n') {
3693 strncpy(sval, buf + j, 15);
3695 coeffs[k] = strtod(sval, NULL);
3700 if (k < mol->bset->ncomps)
3702 j = MoleculeSetMOCoefficients(mol, i + 1, -1000000, k, coeffs);
3704 s_append_asprintf(errbuf, "Line %d: cannot set coefficients for MO %d", lineNumber, i + 1);
3715 } else if ((strstr(buf, "ELECTRIC POTENTIAL") != NULL || strstr(buf, "ELECTROSTATIC POTENTIAL") != NULL) && strstr(buf, "ELPOTT") != NULL) {
3717 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3719 if (strstr(buf, "TOTAL NUMBER OF GRID POINTS") != NULL)
3721 if (sscanf(buf, "%d %lf %lf %lf %lf", &ival[0], &dval[0], &dval[1], &dval[2], &dval[3]) < 5)
3723 ep = AssignArray(&mol->elpots, &mol->nelpots, sizeof(Elpot), i, NULL);
3724 ep->pos.x = dval[0];
3725 ep->pos.y = dval[1];
3726 ep->pos.z = dval[2];
3731 goto redo; /* This section has no end line, so the last line should be processed again */
3732 else break; /* End of file encountered or interrupted */
3733 } /* TODO: read MOLPLT info if present */
3736 s_append_asprintf(errbuf, "User interrupt at line %d", lineNumber);
3742 if (mol->natoms > 0)
3743 retval = 0; /* Return the partially constructed molecule */
3744 if (newmol && mol->nbonds == 0) {
3747 MoleculeGuessBonds(mol, 0.0, &nbonds, &bonds);
3749 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
3757 MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
3760 if (ftype == NULL || *ftype == 0) {
3762 cp = strrchr(fname, '.');
3766 cp = guessMoleculeType(fname);
3767 if (strcmp(cp, "???") != 0)
3771 if (strcasecmp(ftype, "pdb") == 0) {
3772 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3775 /* Try all formats once again */
3776 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3782 MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char **errbuf)
3788 int i, j, new_unit, retval;
3796 fp = fopen(fname, "rb");
3798 s_append_asprintf(errbuf, "Cannot open file");
3801 /* flockfile(fp); */
3802 if (mp->natoms == 0)
3805 /* Allocate buffer for undo-capable modification */
3806 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
3807 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3808 /* Retain current position if the atom info is missing in the input file */
3814 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3815 if (strncmp(buf, "END", 3) == 0)
3817 if (strncmp(buf, "HETATM", 6) == 0 || strncmp(buf, "ATOM", 4) == 0) {
3819 Int serial, intCharge, resSeq;
3822 char segName[5], resName[4], atomName[5], resSeqStr[5], atomType[3], element[3], occStr[6];
3824 memset(&w, 0, sizeof(w));
3825 ReadFormat(buf, "x6 I5 x1 S4 x1 S3 x1 x1 S4 x1 x3 F8 F8 F8 S6 F6 x6 S4 S2 I2",
3826 &w.serial, w.atomName, w.resName, w.resSeqStr, &w.r.x, &w.r.y, &w.r.z,
3827 w.occStr, &w.temp, w.segName, w.element, &w.intCharge);
3828 if (w.atomName[0] == 0) {
3829 continue; /* Atom name is empty */
3831 /* A workaround for residue number >= 10000 (XPLOR style) */
3832 if (w.resSeqStr[0] >= 'A' && w.resSeqStr[0] <= 'Z') {
3833 w.resSeq = (w.resSeqStr[0] - 'A' + 10) * 1000 + atoi(w.resSeqStr + 1);
3835 w.resSeq = atoi(w.resSeqStr);
3837 if (w.element[0] == 0) {
3838 /* $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl */
3839 for (p = w.atomName; *p != 0; p++) {
3840 if (isalpha(*p) && *p != '_') {
3841 w.element[0] = toupper(*p);
3842 if (isalpha(p[1]) && p[1] != '_') {
3843 w.element[1] = toupper(p[1]);
3852 if (w.occStr[0] == 0)
3855 w.occ = atof(w.occStr);
3856 if (w.serial <= 0) {
3857 s_append_asprintf(errbuf, "line %d: non-positive atom number %d", lineNumber, w.serial);
3861 w.serial--; /* The internal atom number is 0-based */
3862 if (w.serial >= mp->natoms) {
3864 /* Create a new atom entry */
3865 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, w.serial, NULL);
3867 s_append_asprintf(errbuf, "line %d: the atom number %d does not exist in the structure file", lineNumber, w.serial+1);
3873 ap = ATOM_AT_INDEX(mp->atoms, w.serial);
3875 ap->occupancy = w.occ;
3876 ap->tempFactor = w.temp;
3877 if (w.segName[0] == 0)
3878 strncpy(w.segName, "MAIN", 4);
3879 strncpy(ap->segName, w.segName, 4);
3880 ap->resSeq = w.resSeq;
3881 strncpy(ap->resName, w.resName, 4);
3882 strncpy(ap->aname, w.atomName, 4);
3883 strncpy(ap->element, w.element, 2);
3885 ap->atomicNumber = ElementToInt(ap->element);
3886 ap->type = AtomTypeEncodeToUInt(ap->element);
3887 ap->weight = WeightForAtomicNumber(ap->atomicNumber);
3888 ap->intCharge = w.intCharge;
3889 if (ap->resSeq > 0) {
3890 if (ap->resSeq < mp->nresidues) {
3891 /* Update the resName according to residues[] */
3892 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
3894 /* Register the resName to residues[] */
3895 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, w.resName);
3899 strcpy(ap->resName, "XXX");
3900 if (mp->nresidues == 0)
3901 AssignArray(&mp->residues, &mp->nresidues, 4, 0, ap->resName);
3903 i = ElementToInt(ap->element);
3905 ap->weight = gElementParameters[i].weight;
3907 /* Not a new unit: only the atom position is updated */
3911 } else if (strncmp(buf, "CONECT", 6) == 0 && new_unit) {
3912 i = ReadFormat(buf, "x6 I5I5I5I5I5I5I5I5I5I5I5I5",
3913 ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
3914 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7,
3915 ibuf + 8, ibuf + 9, ibuf + 10, ibuf + 11);
3919 for (j = 0; j < i; j++) {
3920 if (ibuf[j] < 0 || ibuf[j] > mp->natoms) {
3921 s_append_asprintf(errbuf, "line %d: The CONECT record contains non-existent atom %d", lineNumber, ibuf[j]);
3924 } else if (ibuf[j] == 0)
3930 for (j = 1, bi = 0; j < i; j++) {
3931 if (ibuf[0] < ibuf[j]) {
3932 if (MoleculeLookupBond(mp, ibuf[0], ibuf[j]) >= 0) {
3933 s_append_asprintf(errbuf, "line %d: warning: duplicate bond %d-%d\n", lineNumber, ibuf[0], ibuf[j]);
3935 bbuf[bi * 2] = ibuf[0] - 1;
3936 bbuf[bi * 2 + 1] = ibuf[j] - 1;
3944 retval = MoleculeAddBonds(mp, bi, bbuf, NULL, 1);
3946 s_append_asprintf(errbuf, "line %d: bad bond specification", lineNumber);
3953 /* funlockfile(fp); */
3956 /* Renumber atoms if some atom number is unoccupied */
3957 int *old2new, oldidx, newidx;
3958 old2new = (int *)calloc(sizeof(int), mp->natoms);
3959 if (old2new == NULL) {
3960 s_append_asprintf(errbuf, "Out of memory");
3964 for (oldidx = newidx = 0; oldidx < mp->natoms; oldidx++) {
3965 ap = ATOM_AT_INDEX(mp->atoms, oldidx);
3966 if (ap->aname[0] != 0) {
3967 old2new[oldidx] = newidx;
3968 if (oldidx > newidx)
3969 memmove(ATOM_AT_INDEX(mp->atoms, newidx), ap, gSizeOfAtomRecord);
3973 mp->natoms = newidx;
3974 if (oldidx > newidx) {
3975 /* Renumber the connects and bonds */
3977 for (i = 0; i < mp->natoms; i++) {
3978 ap = ATOM_AT_INDEX(mp->atoms, i);
3979 cp = AtomConnectData(&ap->connect);
3980 for (j = 0; j < ap->connect.count; j++) {
3981 cp[j] = old2new[cp[j]];
3984 for (i = 0; i < mp->nbonds * 2; i++) {
3985 mp->bonds[i] = old2new[mp->bonds[i]];
3988 retval = MoleculeRebuildTablesFromConnects(mp);
3990 /* This error may not happen */
3991 s_append_asprintf(errbuf, "Cannot build angle/dihedral/improper tables");
3995 /* Undo action: delete all atoms */
3998 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3999 act = MolActionNew(gMolActionUnmergeMolecule, ig);
4000 act->frame = mp->cframe;
4001 MolActionCallback_registerUndo(mp, act);
4002 MolActionRelease(act);
4003 IntGroupRelease(ig);
4006 /* Set the new atom positions */
4007 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
4008 MolActionCreateAndPerform(mp, gMolActionSetAtomPositions, ig, mp->natoms, vp);
4009 IntGroupRelease(ig);
4013 mp->nframes = -1; /* Should be recalculated later */
4015 return 1; /* No atoms */
4019 /* funlockfile(fp); */
4025 return 1; /* Maybe different format? */
4030 MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char **errbuf)
4033 SFloat32 *xp, *yp, *zp;
4036 int n, errcount = 0;
4038 if (mp == NULL || mp->natoms == 0) {
4039 s_append_asprintf(errbuf, "Molecule is empty");
4042 n = DcdOpen(fname, &dcd);
4045 case -2: s_append_asprintf(errbuf, "Cannot open file"); break;
4046 case 1: s_append_asprintf(errbuf, "Premature EOF encountered"); break;
4047 case 2: s_append_asprintf(errbuf, "Bad block length of the first section"); break;
4048 case 3: s_append_asprintf(errbuf, "\"CORD\" signature is missing"); break;
4049 case 4: s_append_asprintf(errbuf, "Bad termination of the first section"); break;
4050 case 5: s_append_asprintf(errbuf, "The title section is not correct"); break;
4051 case 6: s_append_asprintf(errbuf, "The atom number section is not correct"); break;
4052 default: s_append_asprintf(errbuf, "Read error in dcd file"); break;
4056 if (dcd.natoms == 0) {
4057 s_append_asprintf(errbuf, "No atoms were found in the dcd file");
4059 } else if (dcd.nframes == 0) {
4060 s_append_asprintf(errbuf, "No frames were found in the dcd file");
4070 vp = (Vector *)calloc(sizeof(Vector), mp->natoms * dcd.nframes);
4072 cp = (Vector *)calloc(sizeof(Vector), dcd.nframes * 4);
4074 xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4075 yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4076 zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4077 ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mp), dcd.nframes, -1);
4078 if (vp == NULL || xp == NULL || yp == NULL || zp == NULL || ig == NULL) {
4079 s_append_asprintf(errbuf, "Cannot allocate memory");
4085 if (ig) IntGroupRelease(ig);
4088 for (n = 0; n < dcd.nframes; n++) {
4091 SFloat32 dcdcell[6];
4092 if (DcdReadFrame(&dcd, n, xp, yp, zp, dcdcell)) {
4093 s_append_asprintf(errbuf, "Read error in dcd file");
4096 for (i = 0, vpp = &vp[n * mp->natoms]; i < dcd.natoms && i < mp->natoms; i++, vpp++) {
4104 /* dcdcell = {a, gamma, b, beta, alpha, c} */
4105 /* angles are described either in cosines (Charmm and NAMD > 2.5) or degrees (NAMD 2.5) */
4106 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) {
4107 dcdcell[4] = cos(dcdcell[4] * kDeg2Rad); /* cos(alpha) */
4108 dcdcell[3] = cos(dcdcell[3] * kDeg2Rad); /* cos(beta) */
4109 dcdcell[1] = cos(dcdcell[1] * kDeg2Rad); /* cos(gamma) */
4111 /* a axis lies along the cartesian x axis */
4112 sing = sqrt(1 - dcdcell[1] * dcdcell[1]);
4113 vpp[0].x = dcdcell[0];
4116 vpp[1].x = dcdcell[2] * dcdcell[1];
4117 vpp[1].y = dcdcell[2] * sing;
4119 vpp[2].x = dcdcell[5] * dcdcell[3];
4120 vpp[2].y = dcdcell[5] * (dcdcell[4] - dcdcell[3] * dcdcell[1]) / sing;
4121 vpp[2].z = sqrt(dcdcell[5] * dcdcell[5] - vpp[2].x * vpp[2].x - vpp[2].y * vpp[2].y);
4122 vpp[3].x = vpp[3].y = vpp[3].z = 0.0;
4123 if (mp->cell == NULL) {
4124 /* Create periodicity if not present */
4125 MolActionCreateAndPerform(mp, gMolActionSetBox, &vpp[0], &vpp[1], &vpp[2], &vpp[3], 7, 0);
4129 if (MolActionCreateAndPerform(mp, gMolActionInsertFrames, ig, mp->natoms * dcd.nframes, vp, (cp == NULL ? 0 : dcd.nframes * 4), cp) != 0)
4130 s_append_asprintf(errbuf, "Cannot insert frames");
4131 mp->startStep = dcd.nstart;
4132 mp->stepsPerFrame = dcd.ninterval;
4133 mp->psPerStep = dcd.delta;
4142 IntGroupRelease(ig);
4149 MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
4160 fp = fopen(fname, "rb");
4162 s_append_asprintf(errbuf, "Cannot open file");
4168 flags[0] = flags[1] = flags[2] = 0;
4169 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
4170 if (strncmp(buf, "Bounding box:", 13) == 0) {
4171 for (i = 0; i < 3; i++) {
4172 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
4173 s_append_asprintf(errbuf, "line %d: missing %d component of the bounding box", lineNumber, i + 1);
4177 n = sscanf(buf, "%lf %lf %lf %d", &d[0], &d[1], &d[2], &flag);
4179 vv.x = vv.y = vv.z = 0.0;
4181 case 0: vv.x = d[0]; break;
4182 case 1: vv.y = d[0]; break;
4183 case 2: vv.z = d[0]; break;
4185 if (n == 1 || (n == 2 && d[1] != 0.0))
4192 flags[i] = (flag != 0);
4194 flags[i] = (VecLength2(vv) != 0);
4198 if (mp->cell != NULL)
4199 vv = mp->cell->origin;
4201 vv.x = vv.y = vv.z = 0.0;
4202 MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
4203 } else if (strncmp(buf, "Bounding box origin:", 20) == 0) {
4204 if (mp->cell != NULL) {
4205 v[0] = mp->cell->axes[0];
4206 v[1] = mp->cell->axes[1];
4207 v[2] = mp->cell->axes[2];
4208 memmove(flags, mp->cell->flags, 3);
4210 v[0].x = 1.0; v[0].y = v[0].z = 0.0;
4211 v[1].y = 1.0; v[1].x = v[1].z = 0.0;
4212 v[2].z = 1.0; v[2].x = v[2].y = 0.0;
4213 flags[0] = flags[1] = flags[2] = 1.0;
4215 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0 || (n = sscanf(buf, "%lf %lf %lf", &d[0], &d[1], &d[2]) < 3)) {
4216 s_append_asprintf(errbuf, "line %d: wrong format for the bounding box origin", lineNumber);
4223 MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
4235 MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
4239 if (ftype == NULL || *ftype == 0) {
4241 cp = strrchr(fname, '.');
4245 cp = guessMoleculeType(fname);
4246 if (strcmp(cp, "???") != 0)
4250 if (strcasecmp(ftype, "psf") == 0) {
4251 retval = MoleculeWriteToPsfFile(mp, fname, errbuf);
4252 } else if (strcasecmp(ftype, "pdb") == 0) {
4253 retval = MoleculeWriteToPdbFile(mp, fname, errbuf);
4254 } else if (strcasecmp(ftype, "tep") == 0) {
4255 retval = MoleculeWriteToTepFile(mp, fname, errbuf);
4257 s_append_asprintf(errbuf, "The file format should be specified");
4261 MoleculeSetPath(mp, fname);
4266 MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char **errbuf)
4269 Int i, j, k, n1, n2, n3, n_aniso, nframes, nanchors, n_uff;
4275 fp = fopen(fname, "wb");
4277 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4282 nframes = MoleculeFlushFrames(mp);
4284 fprintf(fp, "!:atoms\n");
4285 fprintf(fp, "! idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge\n");
4286 n1 = n2 = n3 = n_aniso = nanchors = n_uff = 0;
4287 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4288 strncpy(bufs[0], ap->segName, 4);
4290 strncpy(bufs[1], ap->resName, 4);
4292 strncpy(bufs[2], ap->aname, 4);
4294 AtomTypeDecodeToString(ap->type, bufs[3]);
4296 strncpy(bufs[4], ap->element, 4);
4298 for (j = 0; j < 5; j++) {
4299 if (bufs[j][0] == 0) {
4303 for (k = 0; k < 6; k++) {
4304 if (bufs[j][k] == 0)
4306 if (bufs[j][k] > 0 && bufs[j][k] < ' ')
4310 if (SYMOP_ALIVE(ap->symop))
4312 if (ap->fix_force != 0)
4314 if (ap->mm_exclude || ap->periodic_exclude)
4316 if (ap->aniso != NULL)
4318 if (ap->anchor != NULL)
4320 if (ap->uff_type[0] != 0)
4322 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);
4327 fprintf(fp, "!:uff_type\n");
4328 fprintf(fp, "! idx uff_type\n");
4329 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4330 fprintf(fp, "%d %.5s\n", i, ap->uff_type);
4336 fprintf(fp, "!:atoms_symop\n");
4337 fprintf(fp, "! idx symop symbase\n");
4338 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4340 n = ap->symop.sym * 1000000 + ap->symop.dx * 10000 + ap->symop.dy * 100 + ap->symop.dz;
4341 fprintf(fp, "%d %d %d\n", i, n, ap->symbase);
4347 fprintf(fp, "!:atoms_fix\n");
4348 fprintf(fp, "! idx fix_force fix_pos\n");
4349 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4350 fprintf(fp, "%d %f %f %f %f\n", i, ap->fix_force, ap->fix_pos.x, ap->fix_pos.y, ap->fix_pos.z);
4356 fprintf(fp, "!:mm_exclude\n");
4357 fprintf(fp, "! idx mm_exclude periodic_exclude\n");
4358 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4359 fprintf(fp, "%d %d %d\n", i, ap->mm_exclude, ap->periodic_exclude);
4365 fprintf(fp, "!:pi_anchor\n");
4366 fprintf(fp, "! idx count; n1 weight1; n2 weight2; ...; nN weightN\n");
4367 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4369 if (ap->anchor == NULL)
4371 k = ap->anchor->connect.count;
4372 ip = AtomConnectData(&ap->anchor->connect);
4373 fprintf(fp, "%d %d\n", i, k);
4374 for (j = 0; j < k; j++) {
4375 fprintf(fp, "%d %f\n", ip[j], ap->anchor->coeffs[j]);
4386 for (i = 0; (i == n2 || i < n1); i++) {
4387 fprintf(fp, "!:positions ; frame %d\n", i);
4388 fprintf(fp, "! idx x y z [sx sy sz]\n");
4389 for (j = 0, ap = mp->atoms; j < mp->natoms; j++, ap = ATOM_NEXT(ap)) {
4392 if (i != n2 && i < ap->nframes)
4393 vp = ap->frames + i;
4396 if (ap->sigma.x != 0.0 || ap->sigma.y != 0.0 || ap->sigma.z != 0.0)
4399 fprintf(fp, "%d %.8f %.8f %.8f", j, vp->x, vp->y, vp->z);
4401 fprintf(fp, " %.8f %.8f %.8f", ap->sigma.x, ap->sigma.y, ap->sigma.z);
4408 if (mp->nbonds > 0) {
4409 fprintf(fp, "!:bonds\n");
4410 fprintf(fp, "! from1 to1 from2 to2 from3 to3 from4 to4\n");
4411 for (i = 0; i < mp->nbonds; i++) {
4412 fprintf(fp, "%d %d%c", mp->bonds[i * 2], mp->bonds[i * 2 + 1], (i % 4 == 3 || i == mp->nbonds - 1 ? '\n' : ' '));
4417 if (mp->nbondOrders > 0) {
4418 fprintf(fp, "!:bond_orders\n");
4419 fprintf(fp, "! order1 order2 order3 order4\n");
4420 for (i = 0; i < mp->nbondOrders; i++) {
4421 fprintf(fp, "%.6f%c", mp->bondOrders[i], (i % 4 == 3 || i == mp->nbondOrders - 1 ? '\n' : ' '));
4426 if (mp->nangles > 0) {
4427 fprintf(fp, "!:angles\n");
4428 fprintf(fp, "! a1 b1 c1 a2 b2 c2 a3 b3 c3\n");
4429 for (i = 0; i < mp->nangles; i++) {
4430 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' : ' '));
4435 if (mp->ndihedrals > 0) {
4436 fprintf(fp, "!:dihedrals\n");
4437 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
4438 for (i = 0; i < mp->ndihedrals; i++) {
4439 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' : ' '));
4444 if (mp->nimpropers > 0) {
4445 fprintf(fp, "!:impropers\n");
4446 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
4447 for (i = 0; i < mp->nimpropers; i++) {
4448 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' : ' '));
4453 if (mp->cell != NULL) {
4454 fprintf(fp, "!:xtalcell\n");
4455 fprintf(fp, "! a b c alpha beta gamma\n");
4456 fprintf(fp, "! This information is redundant and overridden by the following periodic_box info\n");
4457 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]);
4460 fprintf(fp, "!:periodic_box\n");
4461 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");
4462 for (i = 0; i < 3; i++)
4463 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->axes[i].x, mp->cell->axes[i].y, mp->cell->axes[i].z);
4464 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->origin.x, mp->cell->origin.y, mp->cell->origin.z);
4465 fprintf(fp, "%d %d %d%s\n", mp->cell->flags[0], mp->cell->flags[1], mp->cell->flags[2], (mp->cell->has_sigma ? " 1" : ""));
4466 if (mp->cell->has_sigma) {
4467 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]);
4472 if (mp->nframe_cells > 0) {
4473 fprintf(fp, "!:frame_periodic_boxes\n");
4474 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz\n");
4475 for (i = 0; i < mp->nframe_cells * 4; i++) {
4476 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->frame_cells[i].x, mp->frame_cells[i].y, mp->frame_cells[i].z);
4481 if (mp->nsyms > 0) {
4482 fprintf(fp, "!:symmetry_operations\n");
4483 fprintf(fp, "! a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3\n");
4484 for (i = 0; i < mp->nsyms; i++) {
4485 Transform *tp = mp->syms + i;
4486 const unsigned char s_index_order[12] = {0, 3, 6, 1, 4, 7, 2, 5, 8, 9, 10, 11};
4487 for (j = 0; j < 12; j++)
4488 fprintf(fp, "%11.6f%c", (*tp)[s_index_order[j]], (j % 3 == 2 ? '\n' : ' '));
4494 fprintf(fp, "!:anisotropic_thermal_parameters\n");
4495 fprintf(fp, "! b11 b22 b33 b12 b13 b23 [sigma; sb11 sb22 sb33 sb12 sb13 sb23]\n");
4496 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4497 if (ap->aniso != NULL) {
4498 Double *bp = ap->aniso->bij;
4499 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" : ""));
4500 if (ap->aniso->has_bsig) {
4501 bp = ap->aniso->bsig;
4502 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]);
4505 fprintf(fp, "0 0 0 0 0 0\n");
4511 if (mp->arena != NULL) {
4512 MDArena *arena = mp->arena;
4513 fprintf(fp, "!:md_parameters\n");
4514 fprintf(fp, "log_file %s\n", arena->log_result_name);
4515 fprintf(fp, "coord_file %s\n", arena->coord_result_name);
4516 fprintf(fp, "vel_file %s\n", arena->vel_result_name);
4517 fprintf(fp, "force_file %s\n", arena->force_result_name);
4518 fprintf(fp, "debug_file %s\n", arena->debug_result_name);
4519 fprintf(fp, "debug_output_level %d\n", arena->debug_output_level);
4520 fprintf(fp, "step %d\n", arena->step);
4521 fprintf(fp, "coord_output_freq %d\n", arena->coord_output_freq);
4522 fprintf(fp, "energy_output_freq %d\n", arena->energy_output_freq);
4523 fprintf(fp, "coord_frame %d\n", arena->coord_result_frame);
4524 fprintf(fp, "timestep %g\n", arena->timestep);
4525 fprintf(fp, "cutoff %g\n", arena->cutoff);
4526 fprintf(fp, "electro_cutoff %g\n", arena->electro_cutoff);
4527 fprintf(fp, "pairlist_distance %g\n", arena->pairlist_distance);
4528 fprintf(fp, "switch_distance %g\n", arena->switch_distance);
4529 fprintf(fp, "temperature %g\n", arena->temperature);
4530 fprintf(fp, "andersen_freq %d\n", arena->andersen_thermo_freq);
4531 fprintf(fp, "andersen_coupling %g\n", arena->andersen_thermo_coupling);
4532 fprintf(fp, "random_seed %d\n", arena->random_seed);
4533 fprintf(fp, "dielectric %g\n", arena->dielectric);
4534 fprintf(fp, "gradient_convergence %g\n", arena->gradient_convergence);
4535 fprintf(fp, "coordinate_convergence %g\n", arena->coordinate_convergence);
4536 fprintf(fp, "use_xplor_shift %d\n", arena->use_xplor_shift);
4537 fprintf(fp, "scale14_vdw %g\n", arena->scale14_vdw);
4538 fprintf(fp, "scale14_elect %g\n", arena->scale14_elect);
4539 fprintf(fp, "relocate_center %d\n", arena->relocate_center);
4540 fprintf(fp, "surface_probe_radius %g\n", arena->probe_radius);
4541 fprintf(fp, "surface_tension %g\n", arena->surface_tension);
4542 fprintf(fp, "surface_potential_freq %d\n", arena->surface_potential_freq);
4543 fprintf(fp, "use_graphite %d\n", arena->use_graphite);
4544 fprintf(fp, "alchemical_lambda %g\n", arena->alchem_lambda);
4545 fprintf(fp, "alchemical_delta_lambda %g\n", arena->alchem_dlambda);
4546 if (arena->nalchem_flags > 0) {
4547 fprintf(fp, "alchem_flags %d", arena->nalchem_flags);
4548 for (i = 0; i < arena->nalchem_flags; i++) {
4551 else if (i % 10 == 0)
4553 fputc('0' + arena->alchem_flags[i], fp);
4557 if (arena->pressure != NULL) {
4559 fprintf(fp, "pressure_freq %d\n", arena->pressure->freq);
4560 fprintf(fp, "pressure_coupling %g\n", arena->pressure->coupling);
4561 dp = arena->pressure->apply;
4562 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]);
4563 dp = arena->pressure->cell_flexibility;
4564 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]);
4565 fprintf(fp, "pressure_fluctuate_cell_origin %g\n", arena->pressure->fluctuate_cell_origin);
4566 fprintf(fp, "pressure_fluctuate_cell_orientation %g\n", arena->pressure->fluctuate_cell_orientation);
4570 if (mp->par != NULL) {
4571 Parameter *par = mp->par;
4572 fprintf(fp, "!:parameters\n");
4573 ParameterAppendToFile(par, fp);
4577 fprintf(fp, "!:velocity\n");
4578 fprintf(fp, "! idx vx vy vz\n");
4579 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4580 fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->v.x, ap->v.y, ap->v.z);
4584 fprintf(fp, "!:force\n");
4585 fprintf(fp, "! idx fx fy fz\n");
4586 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4587 fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->f.x, ap->f.y, ap->f.z);
4592 if (mp->mview != NULL) {
4594 if (mp->mview->track != NULL) {
4595 fprintf(fp, "!:trackball\n");
4596 fprintf(fp, "! scale; trx try trz; theta_deg x y z\n");
4597 f[0] = TrackballGetScale(mp->mview->track);
4598 fprintf(fp, "%f\n", f[0]);
4599 TrackballGetTranslate(mp->mview->track, f);
4600 fprintf(fp, "%f %f %f\n", f[0], f[1], f[2]);
4601 TrackballGetRotate(mp->mview->track, f);
4602 fprintf(fp, "%f %f %f %f\n", f[0], f[1], f[2], f[3]);
4605 fprintf(fp, "!:view\n");
4606 fprintf(fp, "show_unit_cell %d\n", mp->mview->showUnitCell);
4607 fprintf(fp, "show_periodic_box %d\n", mp->mview->showPeriodicBox);
4608 fprintf(fp, "show_expanded_atoms %d\n", mp->mview->showExpandedAtoms);
4609 fprintf(fp, "show_ellipsoids %d\n", mp->mview->showEllipsoids);
4610 fprintf(fp, "show_hydrogens %d\n", mp->mview->showHydrogens);
4611 fprintf(fp, "show_dummy_atoms %d\n", mp->mview->showDummyAtoms);
4612 fprintf(fp, "show_rotation_center %d\n", mp->mview->showRotationCenter);
4613 fprintf(fp, "show_graphite_flag %d\n", mp->mview->showGraphiteFlag);
4614 fprintf(fp, "show_graphite %d\n", mp->mview->showGraphite);
4615 fprintf(fp, "show_periodic_image_flag %d\n", mp->mview->showPeriodicImageFlag);
4616 fprintf(fp, "show_periodic_image %d %d %d %d %d %d\n",
4617 mp->mview->showPeriodicImage[0], mp->mview->showPeriodicImage[1],
4618 mp->mview->showPeriodicImage[2], mp->mview->showPeriodicImage[3],
4619 mp->mview->showPeriodicImage[4], mp->mview->showPeriodicImage[5]);
4620 if (mp->mview->atomRadius != 0.2)
4621 fprintf(fp, "atom_radius %f\n", mp->mview->atomRadius);
4622 if (mp->mview->bondRadius != 0.1)
4623 fprintf(fp, "bond_radius %f\n", mp->mview->bondRadius);
4624 if (mp->mview->atomResolution != 12)
4625 fprintf(fp, "atom_resolution %d\n", mp->mview->atomResolution);
4626 if (mp->mview->bondResolution != 8)
4627 fprintf(fp, "bond_resolution %d\n", mp->mview->bondResolution);
4631 if (mp->nmolprops > 0) {
4633 for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
4634 /* Encode the property name if necessary */
4637 for (p = prp->propname; *p != 0 && n1 < 900; p++) {
4638 if (*p > ' ' && *p != '%' && *p < 0x7f) {
4642 sprintf(enc + n1, "%%%02x", *p);
4649 enc[n2] = 0; /* Truncate after last ASCII character */
4653 sprintf(enc, "prop_%d", i + 1);
4656 fprintf(fp, "!:property ; %s\n", enc);
4657 for (j = 0; j < nframes; j++) {
4658 fprintf(fp, "%.18g\n", prp->propvals[j]);
4664 if (mp->bset != NULL) {
4665 /* Gaussian primitive info */
4668 fprintf(fp, "!:gaussian_primitives\n");
4669 fprintf(fp, "! sym nprims a_idx; A C Csp\n");
4670 for (i = 0, sp = mp->bset->shells; i < mp->bset->nshells; i++, sp++) {
4672 case kGTOType_S: p = "S"; break;
4673 case kGTOType_P: p = "P"; break;
4674 case kGTOType_SP: p = "SP"; break;
4675 case kGTOType_D: p = "D"; break;
4676 case kGTOType_D5: p = "D5"; break;
4677 case kGTOType_F: p = "F"; break;
4678 case kGTOType_F7: p = "F7"; break;
4679 case kGTOType_G: p = "G"; break;
4680 case kGTOType_G9: p = "G9"; break;
4681 default: snprintf(bufs[0], 8, "X%d", sp->sym); p = bufs[0]; break;
4683 fprintf(fp, "%s %d %d\n", p, sp->nprim, sp->a_idx);
4684 pp = mp->bset->priminfos + sp->p_idx;
4685 for (j = 0; j < sp->nprim; j++, pp++) {
4686 fprintf(fp, "%.18g %.18g %.18g\n", pp->A, pp->C, pp->Csp);
4692 fprintf(fp, "!:mo_info\n");
4693 fprintf(fp, "! uhf|rhf|rohf ne_alpha ne_beta\n");
4694 switch (mp->bset->rflag) {
4695 case 0: p = "UHF"; break;
4696 case 1: p = "RHF"; break;
4697 case 2: p = "ROHF"; break;
4698 default: p = "(unknown)"; break;
4700 fprintf(fp, "%s %d %d\n", p, mp->bset->ne_alpha, mp->bset->ne_beta);
4703 /* MO coefficients */
4704 fprintf(fp, "!:mo_coefficients\n");
4705 for (i = 0; i < mp->bset->nmos; i++) {
4706 fprintf(fp, "MO %d %.18g\n", i + 1, mp->bset->moenergies[i]);
4707 for (j = 0; j < mp->bset->ncomps; j++) {
4708 fprintf(fp, "%.18g%c", mp->bset->mo[i * mp->bset->ncomps + j], (j % 6 == 5 || j == mp->bset->ncomps - 1 ? '\n' : ' '));
4714 if (mp->mview != NULL && mp->mview->ngraphics > 0) {
4715 MainViewGraphic *gp;
4716 fprintf(fp, "!:graphics\n");
4717 for (i = 0; i < mp->mview->ngraphics; i++) {
4718 gp = mp->mview->graphics + i;
4720 case kMainViewGraphicLine: fprintf(fp, "line\n"); break;
4721 case kMainViewGraphicPoly: fprintf(fp, "poly\n"); break;
4722 case kMainViewGraphicCylinder: fprintf(fp, "cylinder\n"); break;
4723 case kMainViewGraphicCone: fprintf(fp, "cone\n"); break;
4724 case kMainViewGraphicEllipsoid: fprintf(fp, "ellipsoid\n"); break;
4725 default: fprintf(fp, "unknown\n"); break;
4727 fprintf(fp, "%d %d\n", gp->closed, gp->visible);
4728 fprintf(fp, "%.4f %.4f %.4f %.4f\n", gp->rgba[0], gp->rgba[1], gp->rgba[2], gp->rgba[3]);
4729 fprintf(fp, "%d\n", gp->npoints);
4730 for (j = 0; j < gp->npoints; j++)
4731 fprintf(fp, "%.6f %.6f %.6f\n", gp->points[j * 3], gp->points[j * 3 + 1], gp->points[j * 3 + 2]);
4732 fprintf(fp, "%d\n", gp->nnormals);
4733 for (j = 0; j < gp->nnormals; j++)
4734 fprintf(fp, "%.6f %.6f %.6f\n", gp->normals[j * 3], gp->normals[j * 3 + 1], gp->normals[j * 3 + 2]);
4739 /* Plug-in in the Ruby world */
4742 if (MolActionCreateAndPerform(mp, SCRIPT_ACTION(";s"),
4743 "proc { savembsf_plugin rescue \"Plug-in error: #{$!.to_s}\" }", &outMessage) == 0) {
4744 if (outMessage[0] != 0) {
4745 if (strncmp(outMessage, "Plug-in", 7) == 0) {
4746 s_append_asprintf(errbuf, "%s", outMessage);
4748 fprintf(fp, "%s\n", outMessage);
4760 MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char **errbuf)
4766 fp = fopen(fname, "wb");
4768 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4771 fprintf(fp, "PSF\n\n");
4772 fprintf(fp, " 1 !NTITLE\n");
4773 fprintf(fp, " REMARKS FILENAME=\n");
4777 fprintf(fp, "%8d !NATOM\n", mp->natoms);
4778 for (i = 0; i < mp->natoms; i++) {
4780 ap = ATOM_AT_INDEX(mp->atoms, i);
4781 fprintf(fp, "%8d ", i + 1);
4782 if (ap->resSeq >= 10000) {
4783 fmt = "%-3.3s %-5d ";
4785 fmt = "%-4.4s %-4d ";
4787 fprintf(fp, fmt, ap->segName, ap->resSeq);
4788 fprintf(fp, "%-3.3s %-4.4s %-4.4s %12.6f %8.4f 0\n",
4789 ap->resName, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->charge, ap->weight);
4794 fprintf(fp, "%8d !NBOND: bonds\n", mp->nbonds);
4795 for (i = 0; i < mp->nbonds * 2; i++) {
4796 fprintf(fp, "%8d", mp->bonds[i] + 1);
4805 fprintf(fp, "%8d !NTHETA: angles\n", mp->nangles);
4806 for (i = 0; i < mp->nangles * 3; i++) {
4807 fprintf(fp, "%8d", mp->angles[i] + 1);
4816 fprintf(fp, "%8d !NPHI: dihedrals\n", mp->ndihedrals);
4817 for (i = 0; i < mp->ndihedrals * 4; i++) {
4818 fprintf(fp, "%8d", mp->dihedrals[i] + 1);
4827 fprintf(fp, "%8d !NIMPHI: impropers\n", mp->nimpropers);
4828 for (i = 0; i < mp->nimpropers * 4; i++) {
4829 fprintf(fp, "%8d", mp->impropers[i] + 1);
4837 fprintf(fp, "%8d !NDON: donors\n\n", 0);
4838 fprintf(fp, "%8d !NACC: acceptors\n\n", 0);
4839 fprintf(fp, "%8d !NNB: non-bonding exclusions\n\n", 0);
4840 for (i = 0; i < mp->natoms; i++) {
4841 fprintf(fp, "%8d", 0);
4848 fprintf(fp, "%8d !NGRP: groups\n", 1);
4849 fprintf(fp, " 0 0 0\n");
4853 if (i > 5 && strcmp(fname + i - 5, ".psfx") == 0) {
4854 /* Extended psf (with coordinates and other info) */
4855 fprintf(fp, "%8d !COORD: coordinates\n", mp->natoms);
4856 for (i = 0; i < mp->natoms; i++) {
4858 ap = ATOM_AT_INDEX(mp->atoms, i);
4860 fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->aname);
4870 MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char **errbuf)
4876 fp = fopen(fname, "wb");
4878 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4881 for (i = 0; i < mp->natoms; i++) {
4883 ap = ATOM_AT_INDEX(mp->atoms, i);
4884 if (ap->resSeq >= 10000) {
4885 snprintf(buf, sizeof buf, "%c%03d", 'A' + (ap->resSeq - 10000) / 1000, ap->resSeq % 1000);
4887 snprintf(buf, sizeof buf, "%4d", ap->resSeq);
4889 fprintf(fp, "ATOM %5d %-4.4s%1.1s%-3.3s %1.1s%4.4s%1.1s "
4890 "%8.3f%8.3f%8.3f %5.2f %5.2f "
4891 "%-4.4s%-2.2s%-2d\n",
4892 i + 1, ap->aname, " ", ap->resName, " ", buf, " ",
4893 ap->r.x, ap->r.y, ap->r.z, ap->occupancy, ap->tempFactor,
4894 ap->segName, ap->element, ap->intCharge);
4896 for (i = 0; i < mp->natoms; i++) {
4898 ap = ATOM_AT_INDEX(mp->atoms, i);
4899 cp = AtomConnectData(&ap->connect);
4900 for (j = 0; j < ap->connect.count; j++) {
4904 fprintf(fp, "CONECT%5d", i + 1);
4906 fprintf(fp, "%5d", cp[j] + 1);
4911 fprintf(fp, "END\n");
4917 MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char **errbuf)
4920 SFloat32 *xp, *yp, *zp;
4923 if (mp == NULL || mp->natoms == 0) {
4924 s_append_asprintf(errbuf, "Molecule is empty");
4927 memset(&dcd, 0, sizeof(dcd));
4928 dcd.natoms = mp->natoms;
4929 dcd.nframes = MoleculeGetNumberOfFrames(mp);
4930 if (dcd.nframes == 0) {
4931 s_append_asprintf(errbuf, "no frame is present");
4934 dcd.nstart = mp->startStep;
4935 dcd.ninterval = mp->stepsPerFrame;
4936 if (dcd.ninterval == 0)
4938 dcd.nend = dcd.nstart + (dcd.nframes - 1) * dcd.ninterval;
4939 if (mp->cell != NULL)
4941 dcd.delta = mp->psPerStep;
4942 if (dcd.delta == 0.0)
4945 n = DcdCreate(fname, &dcd);
4948 s_append_asprintf(errbuf, "Cannot create dcd file");
4950 s_append_asprintf(errbuf, "Cannot write dcd header");
4955 xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4956 yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4957 zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4958 if (xp == NULL || yp == NULL || zp == NULL) {
4959 s_append_asprintf(errbuf, "Cannot allocate memory");
4966 for (n = 0; n < dcd.nframes; n++) {
4969 for (i = 0, ap = mp->atoms; i < dcd.natoms && mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4971 if (ap->frames == NULL || n >= ap->nframes)
4979 if (i < dcd.natoms) {
4980 size_t sz = (dcd.natoms - i) * sizeof(SFloat32);
4981 memset(xp + i, 0, sz);
4982 memset(yp + i, 0, sz);
4983 memset(zp + i, 0, sz);
4985 if (n < mp->nframe_cells && mp->frame_cells != NULL) {
4986 Vector *cp = &(mp->frame_cells[n * 4]);
4987 dcd.globalcell[0] = VecLength(cp[0]);
4988 dcd.globalcell[2] = VecLength(cp[1]);
4989 dcd.globalcell[5] = VecLength(cp[2]);
4990 dcd.globalcell[1] = VecDot(cp[0], cp[1]) / (dcd.globalcell[0] * dcd.globalcell[2]);
4991 dcd.globalcell[3] = VecDot(cp[0], cp[2]) / (dcd.globalcell[0] * dcd.globalcell[5]);
4992 dcd.globalcell[4] = VecDot(cp[1], cp[2]) / (dcd.globalcell[2] * dcd.globalcell[5]);
4994 if (DcdWriteFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
4995 s_append_asprintf(errbuf, "Write error in dcd file");
5011 MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
5017 fp = fopen(fname, "wb");
5019 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
5022 if (mp->cell != NULL) {
5023 fprintf(fp, "Bounding box:\n");
5024 for (i = 0; i < 3; i++) {
5025 v = mp->cell->axes[i];
5026 fprintf(fp, "%.3f %.3f %.3f %d\n", v.x, v.y, v.z, mp->cell->flags[i]);
5028 fprintf(fp, "Bounding box origin:\n");
5029 v = mp->cell->origin;
5030 fprintf(fp, "%.3f %.3f %.3f\n", v.x, v.y, v.z);
5037 sCompareByElement(const void *ap, const void *bp)
5039 return ((*(Atom **)bp)->atomicNumber - (*(Atom **)ap)->atomicNumber);
5043 sMakeAdc(int n, int base, Symop symop)
5046 if (SYMOP_ALIVE(symop)) {
5048 sym = (symop.dx + 5) * 10000 + (symop.dy + 5) * 1000 + (symop.dz + 5) * 100 + symop.sym + 1;
5053 return (an + 1) * 100000 + sym;
5057 sCompareAdc(const void *ap, const void *bp)
5059 int n = *((Int *)ap) % 100000 - *((Int *)bp) % 100000;
5061 n = *((Int *)ap) / 100000 - *((Int *)bp) / 100000;
5066 sOutputAtomListInstructions(FILE *fp, int natoms, Atom *atoms)
5068 int i, j, k, an, sym;
5071 adc = (Int *)malloc(sizeof(Int) * natoms);
5074 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
5075 if (ap->exflags & kAtomHiddenFlag)
5077 adc[i] = sMakeAdc(i, ap->symbase, ap->symop);
5079 mergesort(adc, natoms, sizeof(Int), sCompareAdc);
5081 /* Create the atom list */
5083 for (i = j = k = 0; i < natoms; i++) {
5084 int an1 = adc[i] / 100000;
5085 int sym1 = adc[i] % 100000;
5086 if (sym == sym1 && an1 == an + 1) {
5093 /* Output the last atom with a minus sign */
5094 adc[j++] = -(an * 100000 + sym);
5095 /* Output this atom */
5102 adc[j++] = -(an * 100000 + sym);
5104 /* Create the instruction cards */
5105 for (i = k = 0; i < j; i++) {
5107 fprintf(fp, " 401");
5108 fprintf(fp, "%9d", adc[i]);
5110 if (i == j - 1 || k == 6 || (k == 5 && i < j - 2 && adc[i + 2] < 0)) {
5119 sEllipsoidType(int an)
5121 return (an >= 18 ? 3 : (an >= 2 && an != 6 ? 2 : (an > 0 ? 1 : 0)));
5125 sOutputAtomTypeInstructions(FILE *fp, int natoms, Atom *atoms)
5129 int etype, elast, istart, ilast, n1, n2;
5130 elast = istart = ilast = -1;
5131 for (i = 0, ap = atoms; i <= natoms; i++, ap++) {
5133 if (SYMOP_ALIVE(ap->symop))
5135 if (ap->exflags & kAtomHiddenFlag)
5137 etype = sEllipsoidType(ap->atomicNumber);
5142 } else if (elast == etype && ilast == i - 1) {
5147 /* Output the instruction card for the 'last' block of atoms */
5150 n1 = 4; n2 = 0; break;
5152 n1 = 4; n2 = 5; break;
5154 n1 = 1; n2 = 0; break;
5156 fprintf(fp, " 1 715 %8d 0 %8d 0 0.100 0.000 0.000\n", n1, n2);
5157 fprintf(fp, " %9d%9d\n", istart + 1, ilast + 1);
5164 sCompareBondType(const void *ap, const void *bp)
5166 /* Descending order */
5167 return *((int *)bp) - *((int *)ap);
5171 sOutputBondInstructions(FILE *fp, int natoms, Atom *atoms, int overlap_correction)
5175 int i, j, n[5], an, count, n1, n2, k;
5179 static const float sBondRad[4] = {0.060, 0.060, 0.060, 0.040};
5180 static const int sBondShade[4] = {5, 3, 1, 1};
5182 n[0] = n[1] = n[2] = n[3] = 0; /* Start index of 3rd row atoms (and higher), 2nd row, 1st row, and H */
5184 for (i = natoms - 1, ap = atoms + i; i >= 0; i--, ap--) {
5185 an = ap->atomicNumber;
5197 if (overlap_correction)
5198 strcpy(buf, " 2 1001 0.000\n");
5200 strcpy(buf, " 2 812\n");
5202 for (i = 0; i < 4; i++) {
5203 for (j = i; j < 4; j++) {
5204 /* Examine bonds between "group i" and "group j" */
5207 double min_bond = 10000.0; /* Minimum distance between bound atoms */
5208 double min_nonbond = 10000.0; /* Minimum distance between non-bound atoms */
5209 double max_bond = -10000.0; /* Maximum distance between bound atoms */
5210 int count_exbond = 0; /* Number of explicit bonds in this group */
5211 for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
5212 for (n2 = n[j], ap2 = atoms + n2; n2 < n[j + 1]; n2++, ap2++) {
5215 VecSub(dr, ap->r, ap2->r);
5217 cp = AtomConnectData(&ap->connect);
5218 for (k = ap->connect.count - 1; k >= 0; k--) {
5223 /* n1 and n2 are bound */
5229 /* n1 and n2 are not bound */
5230 if (d < min_nonbond)
5235 if (min_bond == 10000.0)
5236 continue; /* No bonds between these groups */
5238 if (max_bond + 0.002 < min_nonbond)
5241 max_bond = min_nonbond - 0.002;
5242 /* Some bonds may be omitted, so scan all bonds again */
5243 for (n1 = n[i], ap = ATOM_AT_INDEX(atoms, n1); n1 < n[i + 1]; n1++, ap = ATOM_NEXT(ap)) {
5244 cp = AtomConnectData(&ap->connect);
5245 for (k = ap->connect.count - 1; k >= 0; k--) {
5247 if (n2 < n[j] || n2 >= n[j + 1])
5250 VecSub(dr, ap->r, ap2->r);
5253 /* This bond should be explicitly defined */
5255 if (count_exbond == 0) {
5256 adc1 = -(i + 1); /* Bond type */
5257 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
5259 adc1 = sMakeAdc(n1, ap->symbase, ap->symop);
5260 adc2 = sMakeAdc(n2, ap2->symbase, ap2->symop);
5261 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
5262 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc2);
5268 /* Output the last instruction card */
5270 /* Make a new trailer card */
5271 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]);
5276 /* Output the last trailer card */
5281 if (count == 0 && overlap_correction) {
5282 /* 1001 card is not yet written, so write it */
5286 snprintf(buf, sizeof(buf), " 1 %3d", (overlap_correction ? 821 : 811));
5287 k = -exbonds[0] - 1; /* Bond type for the first block */
5288 i = 1; /* Index for exbonds[] */
5289 j = 0; /* Count in this block */
5290 while (i <= nexbonds) {
5291 if (j >= 29 || i == nexbonds || exbonds[i] < 0) {
5295 /* The trailer card */
5296 fprintf(fp, " %3d %6.3f\n", sBondShade[k], sBondRad[k]);
5300 k = -exbonds[i++] - 1; /* The new bond type */
5302 } else if (j > 0 && j % 3 == 0) {
5308 snprintf(buf + 9 + (j % 3) * 18, sizeof(buf) - 9 - (j % 3) * 18, "%9d%9d\n", n1, n2);
5316 MoleculeWriteToTepFile(Molecule *mp, const char *fname, char **errbuf)
5319 int i, j, natoms, *ip;
5321 Atom *ap, *atoms, **app;
5323 static Double sUnit[] = {1, 1, 1, 90, 90, 90};
5327 /* Create sorted array of atoms */
5328 natoms = mp->natoms;
5329 atoms = (Atom *)calloc(sizeof(Atom), natoms);
5330 app = (Atom **)calloc(sizeof(Atom *), natoms);
5331 ip = (int *)calloc(sizeof(int), natoms);
5332 if (atoms == NULL || app == NULL || ip == NULL) {
5333 s_append_asprintf(errbuf, "Cannot allocate memory");
5336 /* Sort the atom pointer by atomic number */
5337 for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
5339 mergesort(app, natoms, sizeof(Atom *), sCompareByElement);
5340 for (i = 0; i < natoms; i++) {
5341 /* ip[old_index] is new_index */
5342 ip[app[i] - mp->atoms] = i;
5344 /* Copy the atom record to atoms[] */
5345 /* The 'v' member contains crystallographic coordinates */
5346 /* The connection table and symbase are renumbered */
5347 /* Hidden flags are modified to reflect the visibility in the MainView */
5348 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
5349 AtomDuplicateNoFrame(ap, app[i]);
5350 /* memmove(ap, app[i], gSizeOfAtomRecord); */
5351 MoleculeCartesianToXtal(mp, &(ap->v), &(ap->r));
5352 cp = AtomConnectData(&ap->connect);
5353 for (j = ap->connect.count - 1; j >= 0; j--) {
5356 if (SYMOP_ALIVE(ap->symop))
5357 ap->symbase = ip[ap->symbase];
5358 if (MainView_isAtomHidden(mp->mview, i)) {
5359 ap->exflags |= kAtomHiddenFlag;
5361 ap->exflags &= ~kAtomHiddenFlag;
5367 fp = fopen(fname, "wb");
5369 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
5374 fprintf(fp, "Generated by Molby\n");
5377 if (mp->cell != NULL) {
5378 dp = mp->cell->cell;
5382 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]);
5384 /* Symmetry operations */
5385 if (mp->nsyms > 0) {
5386 for (i = 0; i < mp->nsyms; i++) {
5388 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]);
5391 fprintf(fp, "1 0 1 0 0 0 0 1 0 0 0 0 1\n");
5395 for (i = 0, ap = atoms; i < natoms; i++, ap++) {
5396 /* The 'v' field contains crystallographic coordinates */
5397 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);
5398 if (ap->aniso != NULL) {
5399 dp = ap->aniso->bij;
5400 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);
5402 Double temp = ap->tempFactor;
5405 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", temp, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5408 /* Special points */
5410 Vector camera, lookat, up, xvec, yvec, zvec;
5411 MainView_getCamera(mp->mview, &camera, &lookat, &up);
5412 VecSub(zvec, lookat, camera);
5413 VecCross(xvec, zvec, up);
5414 NormalizeVec(&xvec, &xvec);
5415 NormalizeVec(&yvec, &up);
5416 VecInc(xvec, lookat);
5417 VecInc(yvec, lookat);
5418 MoleculeCartesianToXtal(mp, &lookat, &lookat);
5419 MoleculeCartesianToXtal(mp, &xvec, &xvec);
5420 MoleculeCartesianToXtal(mp, &yvec, &yvec);
5421 fprintf(fp, " ORGN %9g%9g%9g 0\n", 0.0, 0.0, 0.0);
5422 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5423 fprintf(fp, " CNTR %9g%9g%9g 0\n", lookat.x, lookat.y, lookat.z);
5424 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5425 fprintf(fp, " X %9g%9g%9g 0\n", xvec.x, xvec.y, xvec.z);
5426 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5427 fprintf(fp, " Y %9g%9g%9g 0\n", yvec.x, yvec.y, yvec.z);
5428 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);
5432 fprintf(fp, " 201\n");
5433 fprintf(fp, " 205 12\n");
5434 fprintf(fp, " 301 6.6 6.6 0 0.8\n");
5435 sOutputAtomListInstructions(fp, natoms, atoms);
5436 fprintf(fp, " 501%4d55501%4d55501%4d55501%4d55501%4d55501 1\n", natoms + 2, natoms + 2, natoms + 3, natoms + 2, natoms + 4);
5437 fprintf(fp, " 502 1 0.0 2 0.0 3 0.0\n");
5438 fprintf(fp, " 604 1.538\n");
5440 sOutputBondInstructions(fp, natoms, atoms, 1);
5441 sOutputAtomTypeInstructions(fp, natoms, atoms);
5442 sOutputBondInstructions(fp, natoms, atoms, 0);
5444 for (i = 0; i < natoms; i++) {
5445 AtomClean(atoms + i);
5449 fprintf(fp, " 202\n");
5450 fprintf(fp, " 0 -1\n");
5456 MoleculeDump(Molecule *mol)
5461 for (i = 0; i < mol->natoms; i++) {
5463 ap = ATOM_AT_INDEX(mol->atoms, i);
5464 snprintf(buf1, sizeof buf1, "%3.4s.%d", ap->resName, ap->resSeq);
5465 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);
5466 cp = AtomConnectData(&ap->connect);
5467 for (j = 0; j < ap->connect.count; j++) {
5468 fprintf(stderr, "%s%d", (j > 0 ? "," : ""), cp[j]);
5470 fprintf(stderr, "]\n");
5474 #pragma mark ====== MD support (including modification of Molecule) ======
5476 /* Call md_prepare for the MDArena. If MDArena has not been created, a new arena is created.
5477 If something goes wrong, returns 1 (for missing parameters) or -1 (more serious error).
5478 If retmsg is not NULL, a message describing the problem is returned there. This message
5479 must be free'd by the caller. */
5481 MoleculePrepareMDArena(Molecule *mol, int check_only, char **retmsg)
5484 Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
5486 IntGroup *ig1, *ig2, *ig3;
5487 MDArena *arena = mol->arena;
5489 if (arena == NULL) {
5492 } else if (arena->xmol != mol)
5493 md_arena_set_molecule(arena, mol);
5495 arena->is_initialized = 0;
5497 /* Rebuild the tables */
5498 ig1 = ig2 = ig3 = NULL;
5499 nangles = MoleculeFindMissingAngles(mol, &angles);
5500 ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
5501 nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
5503 ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
5504 MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
5506 IntGroupRelease(ig1);
5508 if (ndihedrals > 0) {
5509 ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
5510 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
5512 IntGroupRelease(ig2);
5514 if (nimpropers > 0) {
5515 ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
5516 MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
5518 IntGroupRelease(ig3);
5522 /* Update the path information of the molecule before MD setup */
5523 char *buf = (char *)malloc(4096);
5524 MoleculeCallback_pathName(mol, buf, sizeof buf);
5525 MoleculeSetPath(mol, buf);
5529 /* Prepare parameters and internal information */
5530 msg = md_prepare(arena, check_only);
5532 /* Some parameters are missing? */
5534 if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
5538 asprintf(retmsg, "cannot initialize for MD: %s", msg);
5543 /* The local parameter list is updated */
5546 if (mol->par == NULL)
5547 mol->par = ParameterNew();
5548 for (parType = kFirstParType; parType <= kLastParType; parType++) {
5549 /* Delete global and undefined parameters */
5550 UnionPar *up, *upbuf;
5552 ig1 = IntGroupNew();
5553 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
5554 if (up->bond.src != 0)
5555 IntGroupAdd(ig1, idx, 1);
5557 if (IntGroupGetCount(ig1) > 0)
5558 MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
5559 IntGroupRelease(ig1);
5560 /* Copy global and undefined parameters from arena and insert to mol->par */
5561 nparams = ParameterGetCountForType(arena->par, parType);
5564 upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
5565 ig1 = IntGroupNew();
5566 ig2 = IntGroupNew();
5567 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
5568 if (up->bond.src > 0)
5569 IntGroupAdd(ig1, idx, 1); /* Global parameter */
5570 else if (up->bond.src < 0)
5571 IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
5573 if ((count = IntGroupGetCount(ig1)) > 0) {
5574 /* Insert global parameters (at the top) */
5575 ParameterCopy(arena->par, parType, upbuf, ig1);
5576 ig3 = IntGroupNewWithPoints(0, count, -1);
5577 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5578 IntGroupRelease(ig3);
5580 if ((count = IntGroupGetCount(ig2)) > 0) {
5581 /* Insert undefined parameters (at the bottom) */
5582 ParameterCopy(arena->par, parType, upbuf, ig2);
5583 idx = ParameterGetCountForType(mol->par, parType);
5584 ig3 = IntGroupNewWithPoints(idx, count, -1);
5585 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5586 IntGroupRelease(ig3);
5588 IntGroupRelease(ig2);
5589 IntGroupRelease(ig1);
5592 mol->needsMDRebuild = 0; /* We know the "modified" parameters are consistent with the MDArena */
5597 *retmsg = strdup(msg);
5602 #pragma mark ====== Serialize ======
5605 MoleculeDeserialize(const char *data, Int length, Int *timep)
5615 par = ParameterNew();
5619 while (length >= 12) {
5620 const char *ptr = data + 8 + sizeof(Int);
5621 int len = *((const Int *)(data + 8));
5623 if (strcmp(data, "ATOM") == 0) {
5624 n = len / gSizeOfAtomRecord;
5625 NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, n);
5626 memmove(mp->atoms, ptr, len);
5627 } else if (strcmp(data, "ANISO") == 0) {
5628 n = len / (sizeof(Int) + sizeof(Aniso));
5629 for (i = 0; i < n; i++) {
5630 j = *((const Int *)ptr);
5631 if (j < 0 || j >= mp->natoms)
5633 ap = ATOM_AT_INDEX(mp->atoms, j);
5634 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
5635 if (ap->aniso == NULL)
5637 *(ap->aniso) = *((Aniso *)(ptr + sizeof(Int)));
5638 ptr += sizeof(Int) + sizeof(Aniso);
5640 } else if (strcmp(data, "FRAME") == 0) {
5641 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5642 if (ap->nframes == 0)
5647 NewArray(&ap->frames, &ap->nframes, sizeof(Vector), n);
5648 if (ap->frames == NULL)
5650 memmove(ap->frames, ptr, sizeof(Vector) * ap->nframes);
5651 ptr += sizeof(Vector) * ap->nframes;
5653 } else if (strcmp(data, "EXTCON") == 0) {
5654 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5655 if (ap->connect.count <= ATOM_CONNECT_LIMIT)
5657 n = ap->connect.count;
5658 ap->connect.count = 0;
5659 ap->connect.u.ptr = NULL;
5660 NewArray(&(ap->connect.u.ptr), &(ap->connect.count), sizeof(Int), n);
5661 memmove(ap->connect.u.ptr, ptr, sizeof(Int) * n);
5662 ptr += sizeof(Int) * n;
5664 } else if (strcmp(data, "BOND") == 0) {
5665 n = len / (sizeof(Int) * 2);
5666 NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, n);
5667 memmove(mp->bonds, ptr, len);
5668 } else if (strcmp(data, "ANGLE") == 0) {
5669 n = len / (sizeof(Int) * 3);
5670 NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, n);
5671 memmove(mp->angles, ptr, len);
5672 } else if (strcmp(data, "DIHED") == 0) {
5673 n = len / (sizeof(Int) * 4);
5674 NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, n);
5675 memmove(mp->dihedrals, ptr, len);
5676 } else if (strcmp(data, "IMPROP") == 0) {
5677 n = len / (sizeof(Int) * 4);
5678 NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, n);
5679 memmove(mp->impropers, ptr, len);
5680 } else if (strcmp(data, "RESIDUE") == 0) {
5682 NewArray(&mp->residues, &mp->nresidues, 4, n);
5683 memmove(mp->residues, ptr, len);
5684 } else if (strcmp(data, "CELL") == 0) {
5685 mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
5686 if (mp->cell == NULL)
5688 memmove(mp->cell, ptr, sizeof(XtalCell));
5689 } else if (strcmp(data, "SYMOP") == 0) {
5690 n = len / sizeof(Transform);
5691 NewArray(&mp->syms, &mp->nsyms, sizeof(Transform), n);
5692 memmove(mp->syms, ptr, len);
5693 } else if (strcmp(data, "ANCHOR") == 0) {
5694 const char *ptr2 = ptr + len;
5695 while (ptr < ptr2) {
5697 memset(&an, 0, sizeof(an));
5699 if (i >= 0 && i < mp->natoms) {
5700 n = *((Int *)(ptr + sizeof(Int)));
5701 AtomConnectResize(&(an.connect), n);
5702 memmove(AtomConnectData(&(an.connect)), ptr + sizeof(Int) * 2, sizeof(Int) * n);
5703 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), n);
5704 memmove(an.coeffs, ptr + sizeof(Int) * (2 + n), sizeof(Double) * n);
5705 ap = ATOM_AT_INDEX(mp->atoms, i);
5706 ap->anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
5707 memmove(ap->anchor, &an, sizeof(PiAnchor));
5709 ptr += sizeof(Int) * (2 + n) + sizeof(Double) * n;
5711 } else if (strcmp(data, "TIME") == 0) {
5713 *timep = *((Int *)ptr);
5714 } else if (strcmp(data, "BONDPAR") == 0) {
5716 n = len / sizeof(BondPar);
5717 NewArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), n);
5718 memmove(par->bondPars, ptr, len);
5719 } else if (strcmp(data, "ANGPAR") == 0) {
5721 n = len / sizeof(AnglePar);
5722 NewArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), n);
5723 memmove(par->anglePars, ptr, len);
5724 } else if (strcmp(data, "DIHEPAR") == 0) {
5726 n = len / sizeof(TorsionPar);
5727 NewArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), n);
5728 memmove(par->dihedralPars, ptr, len);
5729 } else if (strcmp(data, "IMPRPAR") == 0) {
5731 n = len / sizeof(TorsionPar);
5732 NewArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), n);
5733 memmove(par->improperPars, ptr, len);
5734 } else if (strcmp(data, "VDWPAR") == 0) {
5736 n = len / sizeof(VdwPar);
5737 NewArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), n);
5738 memmove(par->vdwPars, ptr, len);
5739 } else if (strcmp(data, "VDWPPAR") == 0) {
5741 n = len / sizeof(VdwPairPar);
5742 NewArray(&par->vdwpPars, &par->nvdwpPars, sizeof(VdwPairPar), n);
5743 memmove(par->vdwpPars, ptr, len);
5744 } else if (strcmp(data, "VCUTPAR") == 0) {
5746 n = len / sizeof(VdwCutoffPar);
5747 NewArray(&par->vdwCutoffPars, &par->nvdwCutoffPars, sizeof(VdwCutoffPar), n);
5748 memmove(par->vdwCutoffPars, ptr, len);
5750 len += 8 + sizeof(Int);
5754 if (mp->par == NULL)
5755 ParameterRelease(par);
5756 /* result = MoleculeRebuildTablesFromConnects(mp);
5762 Panic("Low memory while deserializing molecule data");
5763 return NULL; /* Not reached */
5766 Panic("internal error: bad format during deserializing molecule data");
5767 return NULL; /* Not reached */
5771 MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep)
5774 int len, len_all, i, naniso, nframes, nconnects, nanchors;
5777 /* Array of atoms */
5778 len = 8 + sizeof(Int) + gSizeOfAtomRecord * mp->natoms;
5779 ptr = (char *)malloc(len);
5782 memmove(ptr, "ATOM\0\0\0\0", 8);
5783 *((Int *)(ptr + 8)) = gSizeOfAtomRecord * mp->natoms;
5784 p = ptr + 8 + sizeof(Int);
5785 memmove(p, mp->atoms, gSizeOfAtomRecord * mp->natoms);
5786 naniso = nframes = nconnects = nanchors = 0;
5787 for (i = 0; i < mp->natoms; i++) {
5788 ap = ATOM_AT_INDEX(p, i);
5789 if (ap->aniso != NULL) {
5793 if (ap->frames != NULL) {
5794 nframes += ap->nframes;
5797 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5798 nconnects += ap->connect.count;
5799 ap->connect.u.ptr = NULL;
5801 if (ap->anchor != NULL) {
5808 /* Array of aniso */
5810 len = 8 + sizeof(Int) + (sizeof(Int) + sizeof(Aniso)) * naniso;
5811 ptr = (char *)realloc(ptr, len_all + len);
5815 memmove(p, "ANISO\0\0\0", 8);
5816 *((Int *)(p + 8)) = (sizeof(Int) + sizeof(Aniso)) * naniso;
5817 p += 8 + sizeof(Int);
5818 for (i = 0; i < mp->natoms; i++) {
5819 ap = ATOM_AT_INDEX(mp->atoms, i);
5820 if (ap->aniso != NULL) {
5822 *((Aniso *)(p + sizeof(Int))) = *(ap->aniso);
5823 p += sizeof(Int) + sizeof(Aniso);
5829 /* Array of frames */
5831 len = 8 + sizeof(Int) + sizeof(Vector) * nframes;
5832 ptr = (char *)realloc(ptr, len_all + len);
5836 memmove(p, "FRAME\0\0\0", 8);
5837 *((Int *)(p + 8)) = sizeof(Vector) * nframes;
5838 p += 8 + sizeof(Int);
5839 for (i = 0; i < mp->natoms; i++) {
5840 ap = ATOM_AT_INDEX(mp->atoms, i);
5841 if (ap->frames != NULL) {
5842 memmove(p, ap->frames, sizeof(Vector) * ap->nframes);
5843 p += sizeof(Vector) * ap->nframes;
5849 /* Array of connects */
5850 if (nconnects > 0) {
5851 len = 8 + sizeof(Int) + sizeof(Int) * nconnects;
5852 ptr = (char *)realloc(ptr, len_all + len);
5856 memmove(p, "EXTCON\0\0", 8);
5857 *((Int *)(p + 8)) = sizeof(Int) * nconnects;
5858 p += 8 + sizeof(Int);
5859 for (i = 0; i < mp->natoms; i++) {
5860 ap = ATOM_AT_INDEX(mp->atoms, i);
5861 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5862 memmove(p, ap->connect.u.ptr, sizeof(Int) * ap->connect.count);
5863 p += sizeof(Int) * ap->connect.count;
5869 /* Bonds, angles, dihedrals, impropers */
5870 if (mp->nbonds > 0) {
5871 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nbonds;
5872 ptr = (char *)realloc(ptr, len_all + len);
5876 memmove(p, "BOND\0\0\0\0", 8);
5877 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nbonds;
5878 p += 8 + sizeof(Int);
5879 memmove(p, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
5882 if (mp->nangles > 0) {
5883 len = 8 + sizeof(Int) + sizeof(Int) * 3 * mp->nangles;
5884 ptr = (char *)realloc(ptr, len_all + len);
5888 memmove(p, "ANGLE\0\0\0", 8);
5889 *((Int *)(p + 8)) = sizeof(Int) * 3 * mp->nangles;
5890 p += 8 + sizeof(Int);
5891 memmove(p, mp->angles, sizeof(Int) * 3 * mp->nangles);
5894 if (mp->ndihedrals > 0) {
5895 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->ndihedrals;
5896 ptr = (char *)realloc(ptr, len_all + len);
5900 memmove(p, "DIHED\0\0\0", 8);
5901 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->ndihedrals;
5902 p += 8 + sizeof(Int);
5903 memmove(p, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
5906 if (mp->nimpropers > 0) {
5907 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->nimpropers;
5908 ptr = (char *)realloc(ptr, len_all + len);
5912 memmove(p, "IMPROP\0\0", 8);
5913 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->nimpropers;
5914 p += 8 + sizeof(Int);
5915 memmove(p, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
5919 /* Array of residues */
5920 if (mp->nresidues > 0) {
5921 len = 8 + sizeof(Int) + 4 * mp->nresidues;
5922 ptr = (char *)realloc(ptr, len_all + len);
5926 memmove(p, "RESIDUE\0", 8);
5927 *((Int *)(p + 8)) = 4 * mp->nresidues;
5928 p += 8 + sizeof(Int);
5929 memmove(p, mp->residues, 4 * mp->nresidues);
5934 if (mp->cell != NULL) {
5935 len = 8 + sizeof(Int) + sizeof(XtalCell);
5936 ptr = (char *)realloc(ptr, len_all + len);
5940 memmove(p, "CELL\0\0\0\0", 8);
5941 *((Int *)(p + 8)) = sizeof(XtalCell);
5942 p += 8 + sizeof(Int);
5943 memmove(p, mp->cell, sizeof(XtalCell));
5947 /* Symmetry operations */
5948 if (mp->nsyms > 0) {
5949 len = 8 + sizeof(Int) + sizeof(Transform) * mp->nsyms;
5950 ptr = (char *)realloc(ptr, len_all + len);
5954 memmove(p, "SYMOP\0\0\0", 8);
5955 *((Int *)(p + 8)) = sizeof(Transform) * mp->nsyms;
5956 p += 8 + sizeof(Int);
5957 memmove(p, mp->syms, sizeof(Transform) * mp->nsyms);
5963 /* Estimate the necessary storage first */
5964 /* One entry consists of { atom_index (Int), number_of_connects (Int), connects (Int's), weights (Double's) } */
5965 len = 8 + sizeof(Int);
5966 for (i = 0; i < mp->natoms; i++) {
5967 ap = ATOM_AT_INDEX(mp->atoms, i);
5968 if (ap->anchor != NULL)
5969 len += sizeof(Int) * 2 + (sizeof(Int) + sizeof(Double)) * ap->anchor->connect.count;
5971 ptr = (char *)realloc(ptr, len_all + len);
5975 memmove(p, "ANCHOR\0\0", 8);
5976 *((Int *)(p + 8)) = len - (8 + sizeof(Int));
5977 p += 8 + sizeof(Int);
5978 for (i = 0; i < mp->natoms; i++) {
5980 ap = ATOM_AT_INDEX(mp->atoms, i);
5981 if (ap->anchor != NULL) {
5982 count = ap->anchor->connect.count;
5984 *((Int *)(p + sizeof(Int))) = count;
5985 p += sizeof(Int) * 2;
5986 ip = AtomConnectData(&(ap->anchor->connect));
5987 memmove(p, ip, sizeof(Int) * count);
5988 p += sizeof(Int) * count;
5989 memmove(p, ap->anchor->coeffs, sizeof(Double) * count);
5990 p += sizeof(Double) * count;
5997 if (mp->par != NULL) {
5999 for (type = kFirstParType; type <= kLastParType; type++) {
6000 const char *parname;
6001 Int parsize, parcount;
6005 parname = "BONDPAR\0";
6006 parsize = sizeof(BondPar);
6007 parcount = mp->par->nbondPars;
6008 parptr = mp->par->bondPars;
6011 parname = "ANGPAR\0\0";
6012 parsize = sizeof(AnglePar);
6013 parcount = mp->par->nanglePars;
6014 parptr = mp->par->anglePars;
6016 case kDihedralParType:
6017 parname = "DIHEPAR\0";
6018 parsize = sizeof(TorsionPar);
6019 parcount = mp->par->ndihedralPars;
6020 parptr = mp->par->dihedralPars;
6022 case kImproperParType:
6023 parname = "IMPRPAR\0";
6024 parsize = sizeof(TorsionPar);
6025 parcount = mp->par->nimproperPars;
6026 parptr = mp->par->improperPars;
6029 parname = "VDWPAR\0\0";
6030 parsize = sizeof(VdwPar);
6031 parcount = mp->par->nvdwPars;
6032 parptr = mp->par->vdwPars;
6034 case kVdwPairParType:
6035 parname = "VDWPPAR\0";
6036 parsize = sizeof(VdwPairPar);
6037 parcount = mp->par->nvdwpPars;
6038 parptr = mp->par->vdwpPars;
6040 case kVdwCutoffParType:
6041 parname = "VCUTPAR\0";
6042 parsize = sizeof(VdwCutoffPar);
6043 parcount = mp->par->nvdwCutoffPars;
6044 parptr = mp->par->vdwCutoffPars;
6050 len = 8 + sizeof(Int) + parsize * parcount;
6051 ptr = (char *)realloc(ptr, len_all + len);
6055 memmove(p, parname, 8);
6056 *((Int *)(p + 8)) = parsize * parcount;
6057 p += 8 + sizeof(Int);
6058 memmove(p, parptr, parsize * parcount);
6066 time_t tm = time(NULL);
6067 len = 8 + sizeof(Int) + sizeof(Int);
6068 ptr = (char *)realloc(ptr, len_all + len);
6072 memmove(p, "TIME\0\0\0\0", 8);
6073 *((Int *)(p + 8)) = sizeof(Int);
6074 p += 8 + sizeof(Int);
6075 *((Int *)p) = (Int)tm;
6081 if (outLength != NULL)
6082 *outLength = len_all;
6086 Panic("Low memory while serializing a molecule data");
6087 return NULL; /* Not reached */
6090 #pragma mark ====== Search for bonds, angles, dihedrals, impropers ======
6093 sMoleculeSearchIncludingAtoms(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
6097 IntGroup *gp = NULL;
6098 if (atomgroup == NULL)
6100 for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
6101 for (j = 0; j < nsize; j++) {
6102 if (IntGroupLookup(atomgroup, ip[j], NULL) != 0) {
6105 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
6106 Panic("Low memory while searching %s", msg);
6115 MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6119 return sMoleculeSearchIncludingAtoms(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
6123 MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6127 return sMoleculeSearchIncludingAtoms(mp->nangles, mp->angles, 3, atomgroup, "angles");
6131 MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6135 return sMoleculeSearchIncludingAtoms(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
6139 MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6143 return sMoleculeSearchIncludingAtoms(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
6147 sMoleculeSearchAcrossAtomGroup(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
6151 IntGroup *gp = NULL;
6152 if (atomgroup == NULL)
6154 for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
6156 for (j = 0; j < nsize; j++) {
6158 kk = (IntGroupLookup(atomgroup, ip[j], NULL) != 0);
6162 /* This bond etc. crosses the atom group border */
6165 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
6166 Panic("Low memory while searching %s", msg);
6175 MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6179 return sMoleculeSearchAcrossAtomGroup(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
6183 MoleculeSearchAnglesAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6187 return sMoleculeSearchAcrossAtomGroup(mp->nangles, mp->angles, 3, atomgroup, "angles");
6191 MoleculeSearchDihedralsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6195 return sMoleculeSearchAcrossAtomGroup(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
6199 MoleculeSearchImpropersAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6203 return sMoleculeSearchAcrossAtomGroup(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
6206 /* Subroutine for MoleculeGuessBonds. It can be also used independently, but make sure that *outNbonds / *outBonds
6207 _correctly_ represents an array of two integers (as in mp->nbonds/mp->bonds). */
6208 /* Find atoms within the given "distance" from the given position. */
6209 /* If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
6210 the threshold distance is given by the sum of van der Waals radii times limit, and radius is
6211 the van der Waals radius of the atom at the given position. */
6212 /* Index is the atom index of the given atom; it is only used in returning the "bond" array
6213 to the caller. If index is negative, then (-index) is the real atom index, and
6214 only atoms with lower indices than (-index) are looked for. */
6216 MoleculeFindCloseAtoms(Molecule *mp, const Vector *vp, Double radius, Double limit, Int *outNbonds, Int **outBonds, Int index)
6218 Int n2, j, nlim, newbond[2];
6222 nlim = index = -index;
6226 for (j = 0; j < nlim; j++) {
6227 Atom *bp = ATOM_AT_INDEX(mp->atoms, j);
6230 n2 = bp->atomicNumber;
6231 if (n2 >= 0 && n2 < gCountElementParameters)
6232 a2 = gElementParameters[n2].radius;
6233 else a2 = gElementParameters[6].radius;
6235 VecSub(dr, *vp, r2);
6239 alim = limit * (radius + a2);
6240 if (VecLength2(dr) < alim * alim) {
6243 /* MoleculeAddBonds(mp, 1, newbonds); */
6244 AssignArray(outBonds, outNbonds, sizeof(Int) * 2, *outNbonds, newbond);
6250 /* Guess the bonds from the coordinates */
6251 /* If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
6252 the threshold distance is given by the sum of van der Waals radii times limit. */
6254 MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds)
6256 Int nbonds, *bonds, i, newbond[2];
6262 for (i = 1, ap = ATOM_NEXT(mp->atoms); i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6264 Int an = ap->atomicNumber;
6266 if (an >= 0 && an < gCountElementParameters)
6267 rad = gElementParameters[an].radius;
6268 else rad = gElementParameters[6].radius;
6269 MoleculeFindCloseAtoms(mp, &r, rad, limit, &nbonds, &bonds, -i);
6272 newbond[0] = kInvalidIndex;
6274 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
6277 if (outNbonds != NULL)
6278 *outNbonds = nbonds;
6279 if (outBonds != NULL)
6284 /* Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information */
6286 MoleculeRebuildTablesFromConnects(Molecule *mp)
6288 int i, j, k, retval;
6295 if (mp->nbonds == 0) {
6296 for (i = 0; i < mp->natoms; i++) {
6297 ap = ATOM_AT_INDEX(mp->atoms, i);
6298 cp = AtomConnectData(&ap->connect);
6299 for (j = 0; j < ap->connect.count; j++) {
6305 /* MoleculeAddBonds() should not be used, because it assumes connects[] and
6306 bonds are already in sync */
6307 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
6308 /* retval = MoleculeAddBonds(mp, 1, ibuf);
6316 if (mp->nangles == 0) {
6317 for (i = 0; i < mp->natoms; i++) {
6318 ap = ATOM_AT_INDEX(mp->atoms, i);
6319 cp = AtomConnectData(&ap->connect);
6320 for (j = 0; j < ap->connect.count; j++) {
6321 for (k = j + 1; k < ap->connect.count; k++) {
6326 retval = MoleculeAddAngles(mp, ibuf, NULL);
6334 /* Find dihedrals */
6335 if (mp->ndihedrals == 0) {
6336 for (i = 0; i < mp->natoms; i++) {
6337 ap = ATOM_AT_INDEX(mp->atoms, i);
6338 cp = AtomConnectData(&ap->connect);
6339 for (j = 0; j < ap->connect.count; j++) {
6346 apjj = ATOM_AT_INDEX(mp->atoms, jj);
6347 cpjj = AtomConnectData(&apjj->connect);
6348 for (k = 0; k < ap->connect.count; k++) {
6352 for (m = 0; m < apjj->connect.count; m++) {
6354 if (mm == i || mm == kk)
6361 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
6370 /* Find impropers */
6371 if (mp->nimpropers == 0) {
6372 for (i = 0; i < mp->natoms; i++) {
6373 int i1, i2, i4, n1, n2, n4;
6374 ap = ATOM_AT_INDEX(mp->atoms, i);
6375 cp = AtomConnectData(&ap->connect);
6376 for (i1 = 0; i1 < ap->connect.count; i1++) {
6378 for (i2 = i1 + 1; i2 < ap->connect.count; i2++) {
6380 for (i4 = i2 + 1; i4 < ap->connect.count; i4++) {
6387 retval = MoleculeAddImpropers(mp, ibuf, NULL);
6396 mp->needsMDRebuild = 1;
6397 __MoleculeUnlock(mp);
6401 __MoleculeUnlock(mp);
6406 MoleculeAreAtomsConnected(Molecule *mol, int idx1, int idx2)
6408 Atom *ap1 = ATOM_AT_INDEX(mol->atoms, idx1);
6409 if (AtomConnectHasEntry(&ap1->connect, idx2))
6411 else if (ap1->anchor != NULL && AtomConnectHasEntry(&(ap1->anchor->connect), idx2))
6416 #pragma mark ====== Atom names ======
6418 /* Look for the n1-th atom in resno-th residue (n1 is 0-based) */
6420 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
6424 if (mp == NULL || mp->natoms == 0)
6427 for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6428 if (ap->resSeq == resno) {
6435 return lasti; /* max */
6440 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
6444 n = strtol(s, &p, 0);
6456 if ((p = strchr(s, ':')) != NULL) {
6457 /* Residue is specified */
6459 if ((pp = strchr(s, '.')) != NULL && pp < p) {
6460 /* Residue number is also specified */
6463 *resSeq = strtol(pp + 1, &ppp, 0);
6465 return -2; /* Bad format */
6466 while (isspace(*ppp))
6469 return -2; /* Bad format */
6472 /* Check whether the "residue name" is an integer */
6473 n = strtol(s, &pp, 0);
6475 while (isspace(*pp))
6477 if (*pp == 0 || *pp == ':') {
6480 return -2; /* Bad format */
6488 if (n >= sizeof(resName))
6489 n = sizeof(resName) - 1;
6490 strncpy(resName, s, n);
6498 strncpy(atomName, p, 4);
6503 /* Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer */
6505 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
6512 n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
6513 if (atomName[0] == 0) {
6514 if (n >= mp->natoms)
6515 n = -1; /* Out of range */
6518 for (n = 0; n < mp->natoms; n++) {
6519 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
6520 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
6521 && (resSeq < 0 || ap->resSeq == resSeq)
6522 && strncmp(atomName, ap->aname, 4) == 0) {
6526 return -1; /* Not found */
6530 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
6534 if (mp == NULL || index < 0 || index >= mp->natoms) {
6538 ap = mp->atoms + index;
6539 if (ap->resSeq != 0) {
6540 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
6544 snprintf(buf, bufsize, "%.4s", ap->aname);
6547 #pragma mark ====== Selection ======
6550 sMoleculeNotifyChangeSelection(Molecule *mp)
6552 /* TODO: Finer control of notification types may be necessary */
6553 MoleculeCallback_notifyModification(mp, 0);
6557 MoleculeSetSelection(Molecule *mp, IntGroup *select)
6562 IntGroupRetain(select);
6563 if (mp->selection != NULL)
6564 IntGroupRelease(mp->selection);
6565 mp->selection = select;
6566 sMoleculeNotifyChangeSelection(mp);
6570 MoleculeGetSelection(Molecule *mp)
6574 else return mp->selection;
6578 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
6580 if (mp->selection == NULL)
6581 mp->selection = IntGroupNew();
6583 IntGroupClear(mp->selection);
6584 IntGroupAdd(mp->selection, n1, 1);
6585 sMoleculeNotifyChangeSelection(mp);
6589 MoleculeUnselectAtom(Molecule *mp, int n1)
6591 if (mp->selection != NULL)
6592 IntGroupRemove(mp->selection, n1, 1);
6593 sMoleculeNotifyChangeSelection(mp);
6597 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
6599 if (mp->selection == NULL)
6600 mp->selection = IntGroupNew();
6601 IntGroupReverse(mp->selection, n1, 1);
6602 sMoleculeNotifyChangeSelection(mp);
6606 MoleculeIsAtomSelected(Molecule *mp, int n1)
6608 if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
6614 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
6616 if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
6622 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
6625 IntGroup *remain, *ig1, *ig2;
6627 remain = IntGroupNewFromIntGroup(remove);
6631 status = IntGroupReverse(remain, 0, mp->natoms);
6633 ig1 = IntGroupNew();
6637 status = IntGroupDifference(selection, remove, ig1);
6640 ig2 = IntGroupNew();
6644 status = IntGroupDeconvolute(ig1, remain, ig2);
6647 IntGroupRelease(remain);
6649 IntGroupRelease(ig1);
6654 IntGroupRelease(ig2);
6659 #pragma mark ====== Atom Equivalence ======
6663 struct sEqList *next;
6664 struct sEqList *link;
6667 static struct sEqList *sListBase = NULL;
6668 static struct sEqList *sListFree = NULL;
6670 static struct sEqList *
6674 if (sListFree != NULL) {
6676 sListFree = lp->next;
6677 lp->i[0] = lp->i[1] = 0;
6681 lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
6682 lp->link = sListBase;
6688 sFreeEqList(struct sEqList *list)
6690 list->next = sListFree;
6695 sDeallocateEqLists(void)
6697 struct sEqList *lp, *lp_link;
6698 for (lp = sListBase; lp != NULL; lp = lp_link) {
6707 sExistInEqList(int i, int idx, struct sEqList *list)
6709 while (list != NULL) {
6710 if (list->i[idx] == i)
6717 static struct sEqList *
6718 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
6721 struct sEqList *list1, *list2;
6722 Int ii, jj, ni, nj, *cpi, *cpj;
6723 api = ATOM_AT_INDEX(mol->atoms, i);
6724 apj = ATOM_AT_INDEX(mol->atoms, j);
6725 if (api->atomicNumber != apj->atomicNumber)
6727 list1 = sAllocEqList();
6733 if (i == j || (db[i] != NULL && db[i] == db[j]))
6735 cpi = AtomConnectData(&api->connect);
6736 cpj = AtomConnectData(&apj->connect);
6737 for (ni = 0; ni < api->connect.count; ni++) {
6739 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
6741 if (sExistInEqList(ii, 0, list1))
6744 for (nj = 0; nj < apj->connect.count; nj++) {
6746 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6748 if (sExistInEqList(jj, 1, list1))
6750 list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
6754 if (list2 == NULL) {
6756 return NULL; /* No equivalent to ii */
6758 list1 = list2; /* ii is OK, try next */
6764 sDBInclude(Int *ip, int i)
6769 for (j = ip[0] - 1; j >= 0; j--) {
6777 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
6779 Int **db; /* List of equivalents for each atom */
6781 Atom *api, *apj, *apk;
6782 Int *cpi, *cpj, *ibuf, nibuf;
6783 int i, j, k, ii, jj, kk;
6784 if (mol == NULL || mol->natoms == 0)
6786 db = (Int **)calloc(sizeof(Int *), mol->natoms);
6790 /* Find the equivalent univalent atoms */
6791 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6792 if (api->connect.count < 2)
6794 cpi = AtomConnectData(&api->connect);
6795 for (j = 0; j < api->connect.count; j++) {
6799 if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6801 AssignArray(&ibuf, &nibuf, sizeof(Int), n, &jj);
6803 apj = ATOM_AT_INDEX(mol->atoms, jj);
6804 if (apj->connect.count != 1 || db[jj] != NULL)
6806 cpj = AtomConnectData(&apj->connect);
6807 for (k = j + 1; k < api->connect.count; k++) {
6809 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
6811 apk = ATOM_AT_INDEX(mol->atoms, kk);
6812 if (apk->connect.count != 1 || db[kk] != NULL)
6814 if (apj->atomicNumber == apk->atomicNumber) {
6815 AssignArray(&ibuf, &nibuf, sizeof(Int), n, &kk);
6820 ip = (Int *)calloc(sizeof(Int), n + 1);
6824 memmove(ip + 1, ibuf, sizeof(Int) * n);
6825 for (k = 0; k < n; k++)
6835 /* Try matching (i,j) pair */
6836 for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6837 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
6839 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
6840 struct sEqList *list;
6841 if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
6843 if (api->atomicNumber != apj->atomicNumber)
6844 continue; /* Different elements do not match */
6845 if (db[i] != NULL && db[i] == db[j])
6846 continue; /* Already equivalent */
6847 list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
6849 continue; /* (i,j) do not match */
6850 while (list != NULL) {
6853 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
6854 /* Merge db[ii] and db[jj] */
6855 k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
6856 ip = (Int *)calloc(sizeof(Int), k + 1);
6858 return NULL; /* Out of memory */
6859 if (db[ii] == NULL) {
6863 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
6866 if (db[jj] == NULL) {
6869 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
6878 for (k = 0; k < ip[0]; k++)
6882 printf("(%d,%d) matched: ", ii, jj);
6883 for (k = 0; k < ip[0]; k++) {
6884 printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
6894 /* Record the equivalent atoms with the lowest index for each atom */
6895 result = (Int *)calloc(sizeof(Int), mol->natoms);
6896 for (i = 0; i < mol->natoms; i++)
6898 for (i = 0; i < mol->natoms; i++) {
6899 if (result[i] >= 0 || (ip = db[i]) == NULL)
6902 for (j = 0; j < ip[0]; j++) {
6907 for (j = 0; j < ip[0]; j++) {
6908 result[ip[j + 1]] = k;
6909 db[ip[j + 1]] = NULL;
6913 sDeallocateEqLists();
6917 #pragma mark ====== Symmetry expansion ======
6920 MoleculeGetTransformForSymop(Molecule *mp, Symop symop, Transform *tf, int is_cartesian)
6923 if (mp == NULL || mp->cell == NULL)
6925 if (symop.sym >= mp->nsyms && symop.sym != 0)
6927 memmove(*tf, SYMMETRY_AT_INDEX(mp->syms, symop.sym), sizeof(Transform));
6928 (*tf)[9] += symop.dx;
6929 (*tf)[10] += symop.dy;
6930 (*tf)[11] += symop.dz;
6932 TransformMul(t, *tf, mp->cell->rtr);
6933 TransformMul(*tf, mp->cell->tr, t);
6939 MoleculeGetSymopForTransform(Molecule *mp, const Transform tf, Symop *symop, int is_cartesian)
6943 if (mp == NULL || mp->cell == NULL)
6946 TransformMul(t, tf, mp->cell->tr);
6947 TransformMul(t, mp->cell->rtr, t);
6949 memmove(t, tf, sizeof(Transform));
6951 for (i = 0; i < mp->nsyms || i == 0; i++) {
6952 Transform *tp = &(SYMMETRY_AT_INDEX(mp->syms, i));
6953 for (j = 0; j < 9; j++) {
6954 if (fabs((*tp)[j] - t[j]) > 1e-4)
6958 for (j = 9; j < 12; j++) {
6959 double f1 = t[j] - (*tp)[j];
6960 double f2 = floor(f1 + 0.5);
6961 if (fabs(f1 - f2) > 1e-4)
6971 symop->alive = (SYMOP_ALIVE((*symop)) != 0);
6976 return -3; /* Not found */
6980 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
6984 if (symop.sym >= mp->nsyms && symop.sym != 0)
6986 if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
6987 TransformVec(vpout, mp->cell->rtr, vpin);
6988 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpout);
6989 vpout->x += symop.dx;
6990 vpout->y += symop.dy;
6991 vpout->z += symop.dz;
6992 TransformVec(vpout, mp->cell->tr, vpout);
6994 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpin);
6995 vpout->x += symop.dx;
6996 vpout->y += symop.dy;
6997 vpout->z += symop.dz;
7002 /* Add expanded atoms. Returns the number of newly created atoms.
7003 If indices is non-NULL, it should be an array of Int with at least
7004 IntGroupGetCount(group) entries, and on return it contains the
7005 indices of the expanded atoms (may be existing atoms if the expanded
7006 atoms are already present)
7007 If allowOverlap is non-zero, then the new atom is created even when the
7008 coordinates coincide with the some other atom (special position) of the
7009 same element; otherwise, such atom will not be created and the existing
7010 atom is returned in indices[]. */
7012 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group, Int *indices, Int allowOverlap)
7014 int i, n, n0, n1, n2, base, count, *table;
7016 IntGroupIterator iter;
7022 if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
7024 if (symop.sym != 0 && symop.sym >= mp->nsyms)
7027 /* Create atoms, with avoiding duplicates */
7028 n0 = n1 = mp->natoms;
7029 table = (int *)malloc(sizeof(int) * n0);
7032 for (i = 0; i < n0; i++)
7034 IntGroupIteratorInit(group, &iter);
7035 MoleculeGetTransformForSymop(mp, symop, &tr, 0);
7037 for (i = 0; i < count; i++) {
7038 n = IntGroupIteratorNext(&iter);
7039 ap = ATOM_AT_INDEX(mp->atoms, n);
7040 if (SYMOP_ALIVE(ap->symop)) {
7041 /* Calculate the cumulative symop */
7043 MoleculeGetTransformForSymop(mp, ap->symop, &t1, 0);
7044 TransformMul(tr2, tr, t1);
7045 if (MoleculeGetSymopForTransform(mp, tr2, &symop1, 0) != 0) {
7046 if (indices != NULL)
7048 continue; /* Skip this atom */
7056 /* Calculate the expande position */
7057 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
7059 /* Is this expansion already present? */
7060 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
7061 /* Symmetry operation and the base atom are the same */
7062 if (ap2->symbase == base && SYMOP_EQUAL(symop1, ap2->symop))
7064 /* Atomic number and the position are the same */
7065 if (ap2->atomicNumber == ap->atomicNumber && allowOverlap == 0) {
7066 VecSub(dr, ap2->r, nr);
7067 if (VecLength2(dr) < 1e-6)
7072 /* If yes, then skip it */
7073 if (indices != NULL)
7077 /* Create a new atom */
7079 AtomDuplicate(&newAtom, ap);
7080 MoleculeCreateAnAtom(mp, &newAtom, -1);
7081 AtomClean(&newAtom);
7082 ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
7084 ap2->symbase = base;
7085 ap2->symop = symop1;
7086 ap2->symop.alive = (symop1.dx != 0 || symop1.dy != 0 || symop1.dz != 0 || symop1.sym != 0);
7087 table[n] = n1; /* The index of the new atom */
7088 MoleculeSetAnisoBySymop(mp, n1); /* Recalculate anisotropic parameters according to symop */
7089 if (indices != NULL)
7094 IntGroupIteratorRelease(&iter);
7097 for (i = n0; i < n1; i++) {
7099 ap = ATOM_AT_INDEX(mp->atoms, i);
7100 if (SYMOP_ALIVE(ap->symop) && MoleculeGetTransformForSymop(mp, ap->symop, &tr, 1) == 0) {
7101 /* For each connected atom, look for the transformed atom */
7103 ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
7104 cp = AtomConnectData(&ap2->connect);
7105 n2 = ap2->connect.count;
7106 for (n = 0; n < n2; n++) {
7107 Atom *apn = ATOM_AT_INDEX(mp->atoms, cp[n]);
7109 TransformVec(&nr, tr, &nr);
7110 /* Look for the bonded atom transformed by ap->symop */
7111 for (j = 0, ap2 = mp->atoms; j < mp->natoms; j++, ap2 = ATOM_NEXT(ap2)) {
7112 if (ap2->symbase == cp[n] && SYMOP_EQUAL(ap->symop, ap2->symop))
7114 VecSub(dr, nr, ap2->r);
7115 if (ap2->atomicNumber == apn->atomicNumber && VecLength2(dr) < 1e-6)
7118 if (j < mp->natoms) {
7119 /* Bond i-j is created */
7122 if (MoleculeLookupBond(mp, b[0], b[1]) < 0)
7123 MoleculeAddBonds(mp, 1, b, NULL, 1);
7128 mp->needsMDRebuild = 1;
7129 __MoleculeUnlock(mp);
7131 return n1 - n0; /* The number of added atoms */
7134 /* Recalculate the coordinates of symmetry expanded atoms.
7135 (Also recalculate the positions of pi-anchor atoms)
7136 Returns the number of affected atoms.
7137 If group is non-NULL, only the expanded atoms whose base atoms are in the
7138 given group are considered.
7139 If groupout and vpout are non-NULL, the indices of the affected atoms
7140 and the original positions are returned (for undo operation).
7141 The pointers returned in *groupout and *vpout must be released and
7142 free()'ed by the caller */
7144 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
7149 IntGroup *ig = NULL;
7152 if (mp == NULL || mp->natoms == 0)
7157 if (mp->nsyms != 0) {
7158 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7159 if (!SYMOP_ALIVE(ap->symop))
7161 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
7163 bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
7164 MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
7165 VecSub(dr, nr, ap->r);
7166 if (VecLength2(dr) < 1e-20)
7168 if (groupout != NULL) {
7171 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
7174 IntGroupAdd(ig, i, 1);
7180 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7182 if (ap->anchor == NULL)
7184 if (group != NULL) {
7185 if (IntGroupLookup(group, i, NULL) == 0) {
7186 n = ap->anchor->connect.count;
7187 ip = AtomConnectData(&(ap->anchor->connect));
7188 for (j = 0; j < n; j++) {
7189 if (IntGroupLookup(group, ip[j], NULL) != 0)
7193 continue; /* This pi-anchor should not be modified */
7197 MoleculeCalculatePiAnchorPosition(mp, i);
7198 VecSub(dr, nr, ap->r);
7199 if (VecLength2(dr) < 1e-20) {
7200 ap->r = nr; /* No change */
7203 if (groupout != NULL) {
7206 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
7209 IntGroupAdd(ig, i, 1);
7213 mp->needsMDCopyCoordinates = 1;
7214 __MoleculeUnlock(mp);
7217 if (groupout != NULL && vpout != NULL) {
7219 *vpout = (Vector *)realloc(vp, sizeof(Vector) * count);
7221 IntGroupRelease(ig);
7225 if (groupout != NULL && vpout != NULL) {
7233 #pragma mark ====== Show/hide atoms ======
7236 sMoleculeNotifyChangeAppearance(Molecule *mp)
7238 /* TODO: Finer control of notification types may be necessary */
7239 MoleculeCallback_notifyModification(mp, 0);
7244 sMoleculeUnselectHiddenAtoms(Molecule *mp)
7247 if (mp == NULL || mp->selection == NULL)
7249 for (i = 0; i < mp->natoms; i++) {
7250 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7251 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
7252 IntGroupRemove(mp->selection, i, 1);
7254 sMoleculeNotifyChangeAppearance(mp);
7258 MoleculeShowAllAtoms(Molecule *mp)
7263 for (i = 0; i < mp->natoms; i++) {
7264 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7265 ap->exflags &= ~kAtomHiddenFlag;
7267 sMoleculeNotifyChangeAppearance(mp);
7272 MoleculeShowReverse(Molecule *mp)
7277 for (i = 0; i < mp->natoms; i++) {
7278 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7279 ap->exflags ^= kAtomHiddenFlag;
7281 sMoleculeUnselectHiddenAtoms(mp);
7282 sMoleculeNotifyChangeAppearance(mp);
7287 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
7290 if (mp == NULL || ig == NULL)
7292 for (i = 0; i < mp->natoms; i++) {
7293 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7294 if (ap->exflags & kAtomHiddenFlag)
7295 continue; /* Already hidden */
7296 if (IntGroupLookupPoint(ig, i) >= 0)
7297 ap->exflags |= kAtomHiddenFlag;
7299 sMoleculeUnselectHiddenAtoms(mp);
7300 sMoleculeNotifyChangeAppearance(mp);
7304 #pragma mark ====== Reversible Editing ======
7308 sMoleculeNotifyModification(Molecule *mp)
7310 ** TODO: Finer control of notification types may be necessary **
7311 MoleculeCallback_notifyModification(mp, 0);
7315 /* Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup */
7317 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
7320 if (where == NULL) {
7321 /* Append the new objects at the end */
7322 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
7325 n1 = IntGroupGetCount(where); /* Position to get new object */
7326 n2 = nobjs; /* Position to get old object */
7327 n3 = n1 + n2; /* Position to place new/old object */
7328 for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
7329 int start = IntGroupGetStartPoint(where, i);
7330 int end = IntGroupGetEndPoint(where, i);
7332 /* old[end-(n3-n2)..n2-1] is moved to old[end..n3-1] */
7333 memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
7334 n2 = end - (n3 - n2);
7337 /* new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1] */
7338 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
7345 /* Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
7347 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
7349 int n1, n2, n3, start, end, i;
7350 if (where == NULL || IntGroupGetCount(where) == 0)
7351 return 0; /* No operation */
7352 if (objs == NULL || nobjs == 0)
7353 return 1; /* Bad argument */
7354 n1 = 0; /* Position to move remaining elements to */
7355 n2 = 0; /* Position to move remaining elements from */
7356 n3 = 0; /* Position to move removed elements to */
7357 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
7358 end = IntGroupGetEndPoint(where, i);
7360 /* Move (start - n2) elements from objs[n2] to objs[n1] */
7362 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
7366 /* Move (end - start) elements from objs[n2] to clip[n3] */
7368 memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
7369 n3 += (end - start);
7370 n2 += (end - start);
7372 /* Move (nobjs - n2) elements from objs[n2] to objs[n1] */
7374 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
7378 /* Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup */
7380 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
7382 int n1, start, end, i;
7383 if (objs == NULL || where == NULL)
7384 return 1; /* Bad argument */
7385 n1 = 0; /* Position to move removed elements to */
7386 for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
7387 end = IntGroupGetEndPoint(where, i);
7388 /* Copy (end - start) elements from objs[start] to clip[n1] */
7390 memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
7391 n1 += (end - start);
7396 /* Create a new atom with no bonding information. ap must _not_ be inside the given molecule
7397 (Use AtomDuplicate() first) */
7399 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
7403 if (mp == NULL || ap == NULL || mp->noModifyTopology)
7406 if (pos < 0 || pos >= mp->natoms)
7408 ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
7410 goto error; /* Out of memory */
7411 ap1 = ATOM_AT_INDEX(mp->atoms, pos);
7412 if (pos < mp->natoms - 1) {
7413 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
7415 if (AtomDuplicate(ap1, ap) == NULL) {
7416 /* Cannot duplicate: restore the original state */
7417 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
7421 ap1->connect.count = 0;
7422 if (ap1->resSeq >= mp->nresidues)
7423 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
7424 if (ap1->resName[0] == 0)
7425 strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
7426 if (ap1->segName[0] == 0)
7427 strncpy(ap1->segName, "MAIN", 4);
7428 if (pos < mp->natoms - 1) {
7429 /* Renumber the connect table, bonds, angles, etc. */
7430 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
7433 cp = AtomConnectData(&api->connect);
7434 for (j = 0; j < api->connect.count; j++) {
7438 if (api->anchor != NULL) {
7439 cp = AtomConnectData(&api->anchor->connect);
7440 for (j = 0; j < api->anchor->connect.count; j++) {
7446 for (i = 0; i < mp->nbonds * 2; i++) {
7447 if (mp->bonds[i] >= pos)
7450 for (i = 0; i < mp->nangles * 3; i++) {
7451 if (mp->angles[i] >= pos)
7454 for (i = 0; i < mp->ndihedrals * 4; i++) {
7455 if (mp->dihedrals[i] >= pos)
7458 for (i = 0; i < mp->nimpropers * 4; i++) {
7459 if (mp->impropers[i] >= pos)
7463 mp->nframes = -1; /* Should be recalculated later */
7464 MoleculeIncrementModifyCount(mp);
7465 mp->needsMDRebuild = 1;
7466 __MoleculeUnlock(mp);
7469 __MoleculeUnlock(mp);
7475 static int s_error_count;
7478 s_fprintf(FILE *fp, const char *fmt, ...)
7483 return vfprintf(fp, fmt, va);
7487 MoleculeCheckSanity(Molecule *mol)
7489 const char *fail = "Sanity check failure";
7490 Int i, j, *ip, c[4];
7493 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7494 if (ap->resSeq >= mol->nresidues)
7495 s_fprintf(stderr, "%s: atom %d residue %d but nresidues %d\n", fail, i, ap->resSeq, mol->nresidues);
7496 if (ap->type != 0 && ap->type < kAtomTypeMinimum)
7497 s_fprintf(stderr, "%s: atom %d atom type %d less than minimum\n", fail, i, ap->type);
7498 if (ap->atomicNumber < 0 || ap->atomicNumber > 113)
7499 s_fprintf(stderr, "%s: atom %d atomic number %d\n", fail, i, ap->atomicNumber);
7500 ip = AtomConnectData(&ap->connect);
7501 for (j = 0; j < ap->connect.count; j++) {
7502 if (ip[j] < 0 || ip[j] >= mol->natoms)
7503 s_fprintf(stderr, "%s: atom %d connect[%d] = %d out of range\n", fail, i, j, ip[j]);
7504 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[j])->connect), i) == 0)
7505 s_fprintf(stderr, "%s: atom %d has connect %d but atom %d has no connect %d\n", fail, i, ip[j], ip[j], i);
7508 for (i = 0, ip = mol->bonds; i < mol->nbonds; i++, ip += 2) {
7509 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms)
7510 s_fprintf(stderr, "%s: bond %d %d-%d out of range\n", fail, i, ip[0], ip[1]);
7511 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[0])->connect), ip[1]) == 0)
7512 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]);
7514 for (i = 0, ip = mol->angles; i < mol->nangles; i++, ip += 3) {
7515 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms)
7516 s_fprintf(stderr, "%s: angle %d %d-%d-%d out of range\n", fail, i, ip[0], ip[1], ip[2]);
7517 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
7519 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]);
7520 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
7522 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]);
7523 if (c[0] == 2 && c[1] == 2)
7524 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]);
7526 for (i = 0, ip = mol->dihedrals; i < mol->ndihedrals; i++, ip += 4) {
7527 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)
7528 s_fprintf(stderr, "%s: dihedral %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
7529 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
7530 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
7531 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
7533 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]);
7535 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]);
7537 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]);
7539 for (i = 0, ip = mol->impropers; i < mol->nimpropers; i++, ip += 4) {
7540 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)
7541 s_fprintf(stderr, "%s: improper %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
7542 c[0] = MoleculeAreAtomsConnected(mol, ip[2], ip[0]);
7543 c[1] = MoleculeAreAtomsConnected(mol, ip[2], ip[1]);
7544 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
7546 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]);
7548 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]);
7550 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]);
7552 return s_error_count;
7556 /* Merge two molecules. We use this procedure for all add-atom operations. */
7557 /* resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
7558 /* If nactions and actions are non-NULL, then the corresponding undo actions are created and returned. */
7559 /* If forUndo is non-zero, then only the atoms are inserted; other information should be inserted
7560 separately by other undo actions. */
7562 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, Int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7565 Int i, j, n1, n2, n3, n4, *cp;
7566 Int *new2old, *old2new;
7571 if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
7572 return 0; /* Do nothing */
7574 if (dst->noModifyTopology)
7575 return 1; /* Prohibited operation */
7577 if (where != NULL && IntGroupGetCount(where) != src->natoms)
7578 return 1; /* Bad parameter */
7580 if (nactions != NULL)
7582 if (actions != NULL)
7586 __MoleculeLock(dst);
7590 if (resSeqOffset < 0)
7593 /* Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
7594 and ndst..ndst+nsrc-1 are for atoms in src. */
7595 new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
7596 if (new2old == NULL)
7598 old2new = new2old + ndst + nsrc;
7599 n1 = 0; /* dst index */
7600 n2 = 0; /* src index */
7601 n3 = 0; /* "merged" index */
7603 while (n1 < ndst || n2 < nsrc) {
7604 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
7607 /* n4 elements from dst[n1] will go to merged[n3] */
7608 for (j = 0; j < n4; j++) {
7609 old2new[n1 + j] = n3 + j;
7610 new2old[n3 + j] = n1 + j;
7614 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
7616 /* n4 elements from src[n2] will go to merged[n3] */
7617 for (j = 0; j < n4; j++) {
7618 old2new[ndst + n2 + j] = n3 + j;
7619 new2old[n3 + j] = ndst + n2 + j;
7626 /* Expand the destination array */
7627 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
7630 /* Move the atoms */
7631 if (where == NULL) {
7632 /* Duplicate atoms to the end of the destination array */
7633 for (i = 0; i < nsrc; i++) {
7634 ap = ATOM_AT_INDEX(dst->atoms, ndst + i);
7635 if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7637 if (forUndo) /* For undo action, all bonds come from another undo action, so connection info are cleared */
7638 AtomConnectResize(&ap->connect, 0);
7641 /* Duplicate to a temporary storage and then insert */
7642 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
7643 if (tempatoms == NULL)
7645 for (i = 0; i < nsrc; i++) {
7646 ap = ATOM_AT_INDEX(tempatoms, i);
7647 if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7649 if (forUndo) /* See above */
7650 AtomConnectResize(&ap->connect, 0);
7652 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
7656 dst->natoms = ndst + nsrc;
7658 /* Renumber the atom indices in connect[] and symbase, and modify the residue numbers */
7659 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7660 if (new2old[i] < ndst) {
7661 /* This atom is from dst */
7664 /* This atom is from src */
7665 n1 = ndst; /* Offset to the internal number */
7666 if (ap->resSeq != 0)
7667 ap->resSeq += resSeqOffset; /* Modify residue number */
7669 cp = AtomConnectData(&ap->connect);
7670 for (j = 0; j < ap->connect.count; j++)
7671 cp[j] = old2new[cp[j] + n1];
7672 if (SYMOP_ALIVE(ap->symop))
7673 ap->symbase = old2new[ap->symbase + n1];
7674 if (ap->anchor != NULL) {
7675 cp = AtomConnectData(&ap->anchor->connect);
7676 for (j = 0; j < ap->anchor->connect.count; j++)
7677 cp[j] = old2new[cp[j] + n1];
7681 /* Move the bonds, angles, dihedrals, impropers */
7682 for (i = 0; i < 4; i++) {
7683 Int *nitems, *nitems_src;
7684 Int **items, **items_src;
7685 Int nsize; /* Number of Ints in one element */
7688 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
7690 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
7692 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
7694 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
7696 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
7697 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
7699 /* During undo, no bonds etc. are copied from src; they will be taken care later
7704 /* Keep the old number of entries in dst, because it is updated by AssignArray() */
7706 /* Also keep the old number of entries in src, in case src and dst point the same molecule */
7708 /* Expand the array */
7709 if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
7711 /* Copy the items */
7712 memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
7714 /* Copy the bond order info if present */
7715 Int nn1 = dst->nbondOrders;
7716 if (dst->bondOrders != NULL || src->bondOrders != NULL) {
7717 if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), dst->nbonds - 1, NULL) == NULL)
7719 memset(dst->bondOrders + nn1, 0, sizeof(Double) * (dst->nbonds - nn1));
7720 if (src->bondOrders != NULL)
7721 memmove(dst->bondOrders + n1, src->bondOrders, sizeof(Double) * n2);
7726 for (j = 0; j < n1 * nsize; j++)
7727 (*items)[j] = old2new[(*items)[j]];
7728 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
7729 (*items)[j] = old2new[(*items)[j] + ndst];
7730 if (forUndo == 0 && actions != NULL) {
7731 ig = IntGroupNewWithPoints(n1, n2, -1);
7733 case 0: act = MolActionNew(gMolActionDeleteBonds, ig); break;
7734 case 1: act = MolActionNew(gMolActionDeleteAngles, ig); break;
7735 case 2: act = MolActionNew(gMolActionDeleteDihedrals, ig); break;
7736 case 3: act = MolActionNew(gMolActionDeleteImpropers, ig); break;
7738 IntGroupRelease(ig);
7739 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7744 /* Renumber existing parameters */
7745 if (dst->par != NULL) {
7747 for (type = kFirstParType; type <= kLastParType; type++) {
7749 n1 = ParameterGetCountForType(dst->par, type);
7750 for (i = 0; i < n1; i++) {
7751 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7752 ParameterRenumberAtoms(type, up1, ndst, old2new);
7757 /* Merge parameters from src */
7758 if (src->par != NULL && forUndo == 0) {
7759 UnionPar *up1, *up2;
7761 if (dst->par == NULL)
7762 dst->par = ParameterNew();
7764 /* Renumber existing parameters */
7765 for (type = kFirstParType; type <= kLastParType; type++) {
7766 n1 = ParameterGetCountForType(dst->par, type);
7767 for (i = 0; i < n1; i++) {
7768 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7769 ParameterRenumberAtoms(type, up1, ndst, old2new);
7774 for (type = kFirstParType; type <= kLastParType; type++) {
7775 n1 = ParameterGetCountForType(src->par, type);
7776 n2 = ParameterGetCountForType(dst->par, type);
7779 /* Determine which parameter should be copied from src to dst */
7780 for (i = 0; i < n1; i++) {
7782 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
7783 n3 = ParameterGetAtomTypes(type, up1, types);
7784 for (j = 0; j < n3; j++) {
7785 /* If it includes explicit atom index, then it should be copied */
7786 if (types[j] < kAtomTypeMinimum) {
7787 IntGroupAdd(ig, i, 1);
7792 for (j = 0; j < n2; j++) {
7793 up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
7794 if (ParameterCompare(up1, up2, type))
7798 /* This is an unknown parameter; should be copied */
7799 IntGroupAdd(ig, i, 1);
7802 n1 = IntGroupGetCount(ig);
7805 up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
7808 /* Copy parameters and renumber indices if necessary */
7809 for (i = j = 0; i < n1; i++) {
7810 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
7814 ParameterRenumberAtoms(type, up1 + j, nsrc, old2new + ndst);
7817 /* Merge parameters */
7819 IntGroupAdd(ig, n2, j);
7820 if (ParameterInsert(dst->par, type, up1, ig) < j)
7822 if (actions != NULL) {
7823 act = MolActionNew(gMolActionDeleteParameters, type, ig);
7824 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7830 IntGroupRelease(ig);
7833 /* Copy the residues if necessary */
7834 /* src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
7835 However, 1+resSeqOffset should not overwrite the existing residue in dst;
7836 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1]. */
7838 n1 = dst->nresidues;
7839 if (1 + resSeqOffset < n1) {
7841 } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
7842 if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
7843 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
7845 memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
7846 if (nactions != NULL) {
7847 act = MolActionNew(gMolActionChangeNumberOfResidues, n1);
7848 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7854 MoleculeCleanUpResidueTable(dst);
7857 dst->nframes = -1; /* Should be recalculated later */
7859 MoleculeIncrementModifyCount(dst);
7860 dst->needsMDRebuild = 1;
7861 __MoleculeUnlock(dst);
7865 __MoleculeUnlock(dst);
7866 Panic("Low memory while adding atoms");
7867 return 1; /* Not reached */
7870 /* Unmerge the molecule. If necessary, the undo actions are stored in nactions/actions array.
7871 (The nactions/actions array must be initialized by the caller) */
7873 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag, Int *nactions, MolAction ***actions, Int forUndo)
7875 Int nsrc, ndst, nsrcnew;
7876 Int i, j, n1, n2, n3, n4, *cp;
7877 Int *new2old, *old2new;
7878 IntGroup *move_g, *del_g, *remain_g, *dst_par_g, *remove_par_g;
7884 if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
7891 if (src->noModifyTopology && moveFlag)
7892 return 1; /* Prohibit editing */
7894 if ((ndst = IntGroupGetCount(where)) > src->natoms)
7895 return 1; /* Bad parameter */
7897 __MoleculeLock(src);
7902 nsrcnew = nsrc - ndst;
7903 if (resSeqOffset < 0)
7906 /* Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
7907 and nsrcnew..nsrc-1 are for atoms moved into dst. */
7908 new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
7909 if (new2old == NULL)
7911 old2new = new2old + nsrc;
7912 n1 = 0; /* src index */
7913 n2 = 0; /* dst index */
7914 n3 = 0; /* src index after "unmerge" */
7916 while (n1 < nsrc || n2 < ndst) {
7917 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
7920 /* n4 elements from src[n1] will go to unmerged[n3] */
7921 for (j = 0; j < n4; j++) {
7922 old2new[n1 + j] = n3 + j;
7923 new2old[n3 + j] = n1 + j;
7927 if ((n4 = IntGroupGetInterval(where, i)) < 0)
7929 /* n4 elements from src[n1] will go to dst[n2] */
7930 for (j = 0; j < n4; j++) {
7931 old2new[n1 + j] = nsrcnew + n2 + j;
7932 new2old[nsrcnew + n2 + j] = n1 + j;
7939 /* Atoms to remain in the source group */
7941 remain_g = IntGroupNewWithPoints(0, nsrc, -1);
7942 IntGroupRemoveIntGroup(remain_g, where);
7943 } else remain_g = NULL;
7945 /* Find parameters to be moved to the dst (dst_par_g), and to be removed from the src (remove_par_g) */
7946 if (src->par != NULL) {
7947 dst_par_g = IntGroupNew();
7949 remove_par_g = IntGroupNew();
7950 else remove_par_g = NULL;
7951 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7952 n2 = ParameterGetCountForType(src->par, n1);
7955 for (i = 0; i < n2; i++) {
7956 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7957 if (ParameterIsRelevantToAtomGroup(n1, up, src->atoms, where)) {
7958 /* This parameter is to be copied to dst */
7959 IntGroupAdd(dst_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7961 if (moveFlag && !ParameterIsRelevantToAtomGroup(n1, up, src->atoms, remain_g)) {
7962 /* This parameter is to be removed */
7963 IntGroupAdd(remove_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7967 } else dst_par_g = remove_par_g = NULL;
7969 /* Pi anchors should be modified if the anchor and its component atoms become separated between
7972 Int ibufsize, *ibuf, flag_i, flag_j;
7974 ibuf = (Int *)malloc(sizeof(Int) * ibufsize);
7975 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7976 if (ap->anchor == NULL)
7978 flag_i = (old2new[i] < nsrcnew);
7979 cp = AtomConnectData(&ap->anchor->connect);
7980 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7981 flag_j = (old2new[cp[j]] < nsrcnew);
7982 if (flag_i == flag_j) {
7983 if (n1 >= ibufsize) {
7985 ibuf = (Int *)realloc(ibuf, sizeof(Int) * ibufsize);
7991 /* Need to modify the pi anchor list */
7994 MolActionCreateAndPerform(src, SCRIPT_ACTION("isI"), "set_atom_attr", i, "anchor_list", n1, ibuf);
7999 /* Make a new molecule */
8001 dst = MoleculeNew();
8004 /* Expand the destination array */
8005 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
8007 dst_ap = dst->atoms;
8010 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
8015 /* Move the atoms */
8017 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
8019 src->natoms = nsrcnew;
8021 /* The atom record must be deallocated correctly */
8022 for (i = 0; i < ndst; i++)
8023 AtomClean(ATOM_AT_INDEX(dst_ap, i));
8027 for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
8028 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
8033 /* The dummy destination array is no longer needed */
8038 /* Renumber the atom indices in connect[] (src) */
8040 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
8041 cp = AtomConnectData(&ap->connect);
8042 for (j = n1 = 0; j < ap->connect.count; j++) {
8043 n2 = old2new[cp[j]];
8047 AtomConnectResize(&ap->connect, n1);
8048 if (ap->anchor != NULL) {
8049 cp = AtomConnectData(&ap->anchor->connect);
8050 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
8051 n2 = old2new[cp[j]];
8055 if (n1 != ap->anchor->connect.count) {
8056 /* This should not happen!! */
8057 AtomConnectResize(&ap->anchor->connect, n1);
8058 fprintf(stderr, "Internal error in sMoleculeUnmergeSub (line %d)\n", __LINE__);
8060 free(ap->anchor->coeffs);
8069 /* Renumber the atom indices in connect[] (dst) */
8071 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
8072 if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
8073 ap->resSeq -= resSeqOffset;
8074 else ap->resSeq = 0;
8075 cp = AtomConnectData(&ap->connect);
8076 for (j = n1 = 0; j < ap->connect.count; j++) {
8077 n2 = old2new[cp[j]] - nsrcnew;
8081 AtomConnectResize(&ap->connect, n1);
8082 if (ap->anchor != NULL) {
8083 cp = AtomConnectData(&ap->anchor->connect);
8084 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
8085 n2 = old2new[cp[j]] - nsrcnew;
8089 if (n1 != ap->anchor->connect.count) {
8090 /* This can happen, and the anchor info is silently modified */
8092 AtomConnectResize(&ap->anchor->connect, 0);
8093 free(ap->anchor->coeffs);
8098 AtomConnectResize(&ap->anchor->connect, n1);
8100 for (j = 0; j < n1; j++)
8101 d += ap->anchor->coeffs[j];
8102 for (j = 0; j < n1; j++)
8103 ap->anchor->coeffs[j] /= d;
8104 MoleculeCalculatePiAnchorPosition(dst, i);
8111 /* Separate the bonds, angles, dihedrals, impropers */
8112 /* TODO: Improper torsions should also be copied! */
8113 move_g = IntGroupNew();
8116 for (i = 3; i >= 0; i--) {
8117 Int *nitems, *nitems_dst;
8118 Int **items, **items_dst;
8119 Int nsize; /* Number of Ints in one element */
8120 unsigned char *counts;
8121 del_g = IntGroupNew();
8124 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
8126 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
8128 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
8130 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
8132 nitems = NULL; items = NULL; nsize = 0; break; /* Not reached */
8135 nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
8136 items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
8141 counts = (unsigned char *)calloc(1, *nitems);
8142 /* Find the entries that should be moved to dst */
8144 for (j = 0; j < *nitems * nsize; j++) {
8145 n1 = old2new[(*items)[j]];
8147 counts[j / nsize]++; /* Count the atom belonging to dst */
8149 for (j = n2 = n3 = 0; j < *nitems; j++) {
8150 if (counts[j] > 0) {
8151 /* Remove from src */
8153 if (IntGroupAdd(del_g, j, 1) != 0)
8155 if (counts[j] == nsize) {
8158 if (IntGroupAdd(move_g, j, 1) != 0)
8164 /* Expand the destination array */
8165 if (items_dst != NULL && n3 > 0) {
8166 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
8168 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
8170 if (i == 0 && src->bondOrders != NULL) {
8171 if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), n3 - 1, NULL) == NULL)
8173 if (sCopyElementsFromArrayAtPositions(src->bondOrders, src->nbondOrders, dst->bondOrders, sizeof(Double), move_g) != 0)
8177 /* Remove from src */
8178 if (moveFlag && forUndo == 0) {
8179 if (nactions != NULL) {
8182 ip = (Int *)malloc(sizeof(Int) * nsize * n2);
8183 for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
8184 memmove(ip + j * nsize, *items + k * nsize, sizeof(Int) * nsize);
8185 if (i == 0 && src->bondOrders != NULL) {
8186 dp = (Double *)malloc(sizeof(Double) * n2);
8187 for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
8188 dp[j] = src->bondOrders[k];
8192 act = MolActionNew(gMolActionAddBondsForUndo, n2 * nsize, ip, del_g); break;
8194 act = MolActionNew(gMolActionAddAngles, n2 * nsize, ip, del_g); break;
8196 act = MolActionNew(gMolActionAddDihedrals, n2 * nsize, ip, del_g); break;
8198 act = MolActionNew(gMolActionAddImpropers, n2 * nsize, ip, del_g); break;
8201 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8206 act = MolActionNew(gMolActionAssignBondOrders, n2, dp, del_g);
8207 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8212 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
8217 /* Renumber the entries */
8219 for (j = 0; j < *nitems * nsize; j++) {
8220 (*items)[j] = old2new[(*items)[j]];
8223 if (items_dst != NULL) {
8224 for (j = 0; j < *nitems_dst * nsize; j++) {
8225 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
8229 IntGroupClear(move_g);
8230 IntGroupRelease(del_g);
8232 IntGroupRelease(move_g);
8234 /* Copy the residues */
8236 /* src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset) */
8237 n1 = src->nresidues - resSeqOffset; /* This will be dst->nresidues (if >0) */
8238 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
8241 memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
8245 /* Copy the parameters to dst */
8246 if (dst != NULL && dst_par_g != NULL && (n2 = IntGroupGetCount(dst_par_g)) > 0) {
8247 IntGroup *dst_new_g = IntGroupNew();
8248 Int dst_par_count[kLastParType - kFirstParType + 1];
8249 if (dst_new_g == NULL)
8251 for (i = 0; i <= kLastParType - kFirstParType; i++)
8252 dst_par_count[i] = 0;
8253 up = (UnionPar *)calloc(sizeof(UnionPar), n2);
8256 if (ParameterCopy(src->par, kFirstParType, up, dst_par_g) < n2)
8258 /* Renumber the explicit atom indices */
8259 for (i = 0; i < nsrc; i++)
8260 old2new[i] -= nsrcnew; /* new indices for atoms in dst; otherwise negative numbers */
8261 for (i = 0; i < n2; i++) {
8262 /* Renumber the indices, and count the number of parameters for each type */
8263 n1 = kFirstParType + IntGroupGetNthPoint(dst_par_g, i) / kParameterIndexOffset;
8264 dst_par_count[n1 - kFirstParType]++;
8265 ParameterRenumberAtoms(n1, up + i, nsrc, old2new);
8267 for (i = 0; i < nsrc; i++)
8268 old2new[i] += nsrcnew;
8269 if (dst->par == NULL)
8270 dst->par = ParameterNew();
8271 for (i = 0; i <= kLastParType - kFirstParType; i++) {
8272 if (dst_par_count[i] > 0)
8273 IntGroupAdd(dst_new_g, i * kParameterIndexOffset, dst_par_count[i]);
8275 if (ParameterInsert(dst->par, kFirstParType, up, dst_new_g) < n2)
8278 IntGroupRelease(dst_new_g);
8280 IntGroupRelease(dst_par_g);
8282 /* Remove the unused parameter. Note: the parameters that are in remove_par_g and not in
8283 dst_par_g will disappear. To support undo, these parameters should be taken care separately. */
8284 if (forUndo == 0 && remove_par_g != NULL && (n2 = IntGroupGetCount(remove_par_g)) > 0) {
8285 UnionPar *up = (UnionPar *)malloc(sizeof(UnionPar) * n2);
8286 ParameterDelete(src->par, kFirstParType, up, remove_par_g);
8287 if (nactions != NULL) {
8288 act = MolActionNew(gMolActionAddParameters, kFirstParType, remove_par_g, n2, up);
8289 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8294 IntGroupRelease(remove_par_g);
8296 /* Renumber the parameter records remaining in the src */
8298 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
8299 n2 = ParameterGetCountForType(src->par, n1);
8300 for (i = 0; i < n2; i++) {
8301 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
8302 ParameterRenumberAtoms(n1, up, nsrc, old2new);
8308 IntGroupRelease(remain_g);
8309 MoleculeCleanUpResidueTable(src);
8311 MoleculeCleanUpResidueTable(dst);
8314 src->nframes = -1; /* Should be recalculated later */
8316 dst->nframes = -1; /* Should be recalculated later */
8322 MoleculeIncrementModifyCount(src);
8323 src->needsMDRebuild = 1;
8324 __MoleculeUnlock(src);
8329 __MoleculeUnlock(src);
8330 /* Panic("Low memory while removing atoms"); */
8334 /* Separate molecule into two parts. The atoms specified by 'where' are moved
8335 from src to a new molecule, which is returned as *dstp. Dstp can be NULL,
8336 in which case the moved atoms are discarded. */
8338 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
8340 return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1, nactions, actions, forUndo);
8343 /* Extract atoms from a given molecule into two parts. The atoms specified by
8344 'where' are copied from src to a new molecule, which is returned as *dstp.
8345 If dummyFlag is non-zero, then the atoms that are not included in the group
8346 but are connected to any atoms in the group are converted to "dummy" atoms
8347 (i.e. with element "Du" and names beginning with an underscore) and included
8348 in the new molecule object. */
8350 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
8354 /* Extract the fragment */
8355 retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0, NULL, NULL, 0);
8361 /* Search bonds crossing the molecule border */
8362 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
8364 IntGroupIterator iter;
8367 IntGroupIteratorInit(ig, &iter);
8368 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8369 /* The atoms at the border */
8372 n1 = src->bonds[i*2];
8373 n2 = src->bonds[i*2+1];
8374 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
8378 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
8379 continue; /* Actually this is an internal error */
8381 /* n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule */
8382 /* Create a new dummy atom with the same segment/residue info with n1
8383 and the same position as n2 */
8384 ap = ATOM_AT_INDEX(src->atoms, n1);
8385 memset(&a, 0, gSizeOfAtomRecord);
8386 a.segSeq = ap->segSeq;
8387 memmove(a.segName, ap->segName, 4);
8388 a.resSeq = ap->resSeq;
8389 memmove(a.resName, ap->resName, 4);
8390 ElementToString(0, a.element); /* "Du" */
8391 snprintf(a.aname, 4, "_%d", idx++);
8392 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
8393 /* Add the dummy atom to the new molecule; nn[1] is the index
8394 of the new dummy atom in the new molecule */
8395 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
8396 /* Connect nn1 and nn2 */
8397 nn[2] = kInvalidIndex;
8398 MoleculeAddBonds(*dstp, 1, nn, NULL, 1);
8400 IntGroupIteratorRelease(&iter);
8401 IntGroupRelease(ig);
8409 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds, IntGroup *where, Int autoGenerate)
8411 Int nangles, ndihedrals;
8412 Int *angles, *dihedrals;
8413 Int i, j, k, kk, n1, n2, cn1, cn2;
8416 Atom *ap1, *ap2, *ap3;
8418 if (mp == NULL || bonds == NULL || nbonds <= 0)
8420 if (mp->noModifyTopology)
8421 return -4; /* Prohibited operation */
8423 /* Note: Duplicates and validity are not checked (the caller must do that) */
8428 if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, n1 + nbonds - 1, NULL) == NULL
8429 || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nbonds, sizeof(Int) * 2, where) != 0) {
8430 __MoleculeUnlock(mp);
8431 return -4; /* Out of memory */
8433 if (mp->bondOrders != NULL) {
8434 /* Expand the bond order info (all new entries are zero) */
8435 Double *dp = (Double *)calloc(sizeof(Double), nbonds);
8438 if (AssignArray(&(mp->bondOrders), &(mp->nbondOrders), sizeof(Double), n1 + nbonds - 1, NULL) == NULL
8439 || sInsertElementsToArrayAtPositions(mp->bondOrders, n1, dp, nbonds, sizeof(Double), where) != 0) {
8440 __MoleculeUnlock(mp);
8447 angles = dihedrals = NULL;
8448 nangles = ndihedrals = 0;
8450 /* Add connects[], and angles/dihedrals (if autoGenerate is true) */
8451 for (i = 0; i < nbonds; i++) {
8453 /* One entry at time */
8454 /* (Otherwise, duplicate entries of angles and dihedrals result) */
8456 n2 = bonds[i * 2 + 1];
8458 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
8459 AtomConnectInsertEntry(&ap1->connect, -1, n2);
8460 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
8461 AtomConnectInsertEntry(&ap2->connect, -1, n1);
8463 /* Add angles and dihedrals */
8465 AtomConnect *ac1, *ac2;
8466 if (ap1->anchor == NULL || ap2->anchor == NULL) {
8467 /* N1-N2-{XY} or N2-N1-{XY} angles (X: connected atom, Y: constitute atom of pi-anchor) */
8468 for (j = 0; j < 4; j++) {
8470 case 0: temp[0] = n1; temp[1] = n2; ac1 = &ap2->connect; break; /* N1-N2-X */
8471 case 1: if (ap2->anchor == NULL) continue; else ac1 = &ap2->anchor->connect; break; /* N1-N2-Y */
8472 case 2: temp[0] = n2; temp[1] = n1; ac1 = &ap1->connect; break; /* N2-N1-X */
8473 case 3: if (ap1->anchor == NULL) continue; else ac1 = &ap1->anchor->connect; break; /* N2-N1-Y */
8475 cp1 = AtomConnectData(ac1);
8477 for (k = 0; k < cn1; k++) {
8479 if (temp[2] == temp[0])
8481 ap3 = ATOM_AT_INDEX(mp->atoms, temp[2]);
8482 if (ap3->anchor != NULL) {
8483 /* Avoid X-anchor-anchor angle (anchor-X-anchor is allowed) */
8484 if ((j < 2 && ap2->anchor != NULL) || (j >= 2 && ap1->anchor != NULL))
8487 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
8489 /* Dihedrals N1-N2-X-{XY} or N2-N1-X-{XY} */
8490 if (j == 1 || j == 3)
8492 cp2 = AtomConnectData(&ap3->connect);
8493 for (kk = 0; kk < ap3->connect.count; kk++) {
8495 if (temp[3] == temp[0] || temp[3] == temp[1])
8497 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8500 if (ap3->anchor != NULL) {
8501 /* N1-N2-X-Y or N2-N1-X-Y */
8502 /* for Y, only the first constitute atom is considered */
8503 cp2 = AtomConnectData(&ap3->anchor->connect);
8505 if (temp[3] == temp[0] || temp[3] == temp[1])
8507 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8513 /* X-N1-N2-X dihedrals */
8514 /* Y-N1-N2-anchor is allowed, but the force may be zero if the angle N1-N2-anchor is */
8515 /* close to 180 deg (e.g. in ferrocene, C-anchor-Fe-anchor dihedral should be k=0) */
8516 if (ap1->anchor == NULL) {
8517 ac1 = &ap1->connect;
8520 ac1 = &ap1->anchor->connect;
8521 cn1 = 1; /* Only the first constitute atom of pi-anchor is considered */
8523 if (ap2->anchor == NULL) {
8524 ac2 = &ap2->connect;
8527 ac2 = &ap2->anchor->connect;
8528 cn2 = 1; /* Only the first constitute atom of pi-anchor is considered */
8532 cp1 = AtomConnectData(ac1);
8533 cp2 = AtomConnectData(ac2);
8534 for (j = 0; j < cn1; j++) {
8536 if (temp[0] == temp[2])
8538 for (k = 0; k < cn2; k++) {
8540 if (temp[3] == temp[0] || temp[3] == temp[1])
8542 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8549 if (angles != NULL) {
8550 temp[0] = kInvalidIndex;
8551 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
8553 MoleculeAddAngles(mp, angles, NULL);
8556 if (dihedrals != NULL) {
8557 temp[0] = kInvalidIndex;
8558 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8560 MoleculeAddDihedrals(mp, dihedrals, NULL);
8564 MoleculeIncrementModifyCount(mp);
8565 mp->needsMDRebuild = 1;
8566 __MoleculeUnlock(mp);
8571 __MoleculeUnlock(mp);
8572 Panic("Low memory while adding bonds");
8573 return -1; /* Not reached */
8577 /* The deleted angles and dihedrals are stored in outRemoval. */
8578 /* (*outRemoval) is an array of integers, containing:
8579 [0..na*3-1]: the angle indices
8580 [na*3..na*3+nd*4-1]: the dihedral indices
8581 [na*3+nd*4..na*3+nd*4+ni*4-1]: the improper indices
8582 *outRemovedPos is an intgroup denoting the positions of the removed angles/dihedrals/impropers.
8583 the angle indices are included as they are,
8584 the dihedral indices are offset by ATOMS_MAX_NUMBER,
8585 the improper indices are offset by ATOMS_MAX_NUMBER*2.
8586 Note: the removed bond indices are not returned, because the caller should already know them. */
8588 MoleculeDeleteBonds(Molecule *mp, Int *bonds, IntGroup *where, Int **outRemoved, IntGroup **outRemovedPos)
8590 Int i, j, n1, n2, nw;
8591 Int *ip, *jp, na, nd, ni;
8592 IntGroup *ag, *dg, *ig;
8594 IntGroupIterator iter;
8598 if (mp->noModifyTopology)
8599 return -4; /* Prohibited operation */
8603 /* Update connects[] */
8604 IntGroupIteratorInit(where, &iter);
8605 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8606 n1 = mp->bonds[i * 2];
8607 n2 = mp->bonds[i * 2 + 1];
8608 ap = ATOM_AT_INDEX(mp->atoms, n1);
8609 ip = AtomConnectData(&ap->connect);
8610 for (j = 0; j < ap->connect.count; j++) {
8612 AtomConnectDeleteEntry(&ap->connect, j);
8616 ap = ATOM_AT_INDEX(mp->atoms, n2);
8617 ip = AtomConnectData(&ap->connect);
8618 for (j = 0; j < ap->connect.count; j++) {
8620 AtomConnectDeleteEntry(&ap->connect, j);
8626 /* Remove bonds, angles, dihedrals, impropers */
8631 nw = IntGroupGetCount(where);
8632 jp = (Int *)malloc(sizeof(Int) * nw * 2);
8634 IntGroupIteratorReset(&iter);
8635 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8636 jp[j++] = mp->bonds[i * 2];
8637 jp[j++] = mp->bonds[i * 2 + 1];
8639 IntGroupIteratorRelease(&iter);
8641 for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8642 for (j = 0; j < nw; j++) {
8645 if ((ip[0] == n1 && ip[1] == n2)
8646 || (ip[1] == n1 && ip[0] == n2)
8647 || (ip[1] == n1 && ip[2] == n2)
8648 || (ip[2] == n1 && ip[1] == n2)) {
8649 if (IntGroupAdd(ag, i, 1) != 0)
8656 for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8657 for (j = 0; j < nw; j++) {
8660 if ((ip[0] == n1 && ip[1] == n2)
8661 || (ip[1] == n1 && ip[0] == n2)
8662 || (ip[1] == n1 && ip[2] == n2)
8663 || (ip[2] == n1 && ip[1] == n2)
8664 || (ip[2] == n1 && ip[3] == n2)
8665 || (ip[3] == n1 && ip[2] == n2)) {
8668 if (IntGroupAdd(dg, i, 1) != 0)
8675 for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8676 for (j = 0; j < nw; j++) {
8679 if ((ip[0] == n1 && ip[2] == n2)
8680 || (ip[1] == n1 && ip[2] == n2)
8681 || (ip[3] == n1 && ip[2] == n2)
8682 || (ip[0] == n2 && ip[2] == n1)
8683 || (ip[1] == n2 && ip[2] == n1)
8684 || (ip[3] == n2 && ip[2] == n1)) {
8687 if (IntGroupAdd(ig, i, 1) != 0)
8696 if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, bonds, sizeof(Int) * 2, where) != 0)
8698 mp->nbonds -= IntGroupGetCount(where);
8699 if (mp->nbonds == 0) {
8703 if (mp->bondOrders != NULL) {
8704 if (sRemoveElementsFromArrayAtPositions(mp->bondOrders, mp->nbondOrders, NULL, sizeof(Double), where) != 0)
8706 mp->nbondOrders -= IntGroupGetCount(where);
8707 if (mp->nbondOrders == 0) {
8708 free(mp->bondOrders);
8709 mp->bondOrders = NULL;
8712 if (na == 0 && nd == 0 && ni == 0)
8715 ip = (Int *)malloc(sizeof(Int) * (na * 3 + nd * 4 + ni * 4));
8717 MoleculeDeleteAngles(mp, ip, ag);
8719 MoleculeDeleteDihedrals(mp, ip + na * 3, dg);
8721 MoleculeDeleteImpropers(mp, ip + na * 3 + nd * 4, ig);
8723 IntGroupOffset(dg, ATOMS_MAX_NUMBER);
8724 IntGroupOffset(ig, ATOMS_MAX_NUMBER * 2);
8725 IntGroupAddIntGroup(ag, dg);
8726 IntGroupAddIntGroup(ag, ig);
8727 IntGroupRelease(dg);
8728 IntGroupRelease(ig);
8731 if (IntGroupGetCount(ag) == 0) {
8732 IntGroupRelease(ag);
8737 *outRemovedPos = ag;
8739 MoleculeIncrementModifyCount(mp);
8740 mp->needsMDRebuild = 1;
8741 __MoleculeUnlock(mp);
8743 return na * 3 + nd * 4 + ni * 4;
8746 __MoleculeUnlock(mp);
8747 Panic("Low memory while removing bonds");
8748 return -1; /* Not reached */
8752 MoleculeAssignBondOrders(Molecule *mp, const Double *orders, IntGroup *where)
8755 IntGroupIterator iter;
8756 if (mp == NULL || orders == NULL || mp->nbonds == 0)
8758 if (mp->noModifyTopology)
8759 return -4; /* Prohibited operation */
8760 if (mp->bondOrders == NULL) {
8761 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbonds - 1, NULL);
8762 memset(mp->bondOrders, 0, sizeof(Double) * mp->nbondOrders);
8764 IntGroupIteratorInit(where, &iter);
8766 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8767 if (i >= mp->nbondOrders)
8769 mp->bondOrders[i] = orders[j++];
8771 IntGroupIteratorRelease(&iter);
8776 MoleculeGetBondOrders(Molecule *mp, Double *outOrders, IntGroup *where)
8779 IntGroupIterator iter;
8780 if (mp == NULL || mp->nbonds == 0)
8782 if (mp->bondOrders == NULL) {
8783 /* Returns all zero */
8784 i = IntGroupGetCount(where);
8785 for (j = 0; j < i; j++)
8788 IntGroupIteratorInit(where, &iter);
8790 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8791 if (i < mp->nbondOrders)
8792 outOrders[j] = mp->bondOrders[i];
8793 else outOrders[j] = 0.0;
8801 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
8804 if (mp == NULL || angles == NULL)
8806 if (mp->noModifyTopology)
8807 return -4; /* Prohibited operation */
8811 nc = IntGroupGetCount(where);
8813 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
8819 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
8820 || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
8821 __MoleculeUnlock(mp);
8822 Panic("Low memory while adding angles");
8825 mp->needsMDRebuild = 1;
8826 __MoleculeUnlock(mp);
8831 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
8834 if (mp == NULL || where == NULL)
8836 if (mp->noModifyTopology)
8837 return -4; /* Prohibited operation */
8839 if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
8840 __MoleculeUnlock(mp);
8841 Panic("Bad argument while deleting angles");
8843 mp->nangles -= (nc = IntGroupGetCount(where));
8844 if (mp->nangles == 0) {
8848 mp->needsMDRebuild = 1;
8849 __MoleculeUnlock(mp);
8854 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
8857 if (mp == NULL || dihedrals == NULL)
8859 if (mp->noModifyTopology)
8860 return -4; /* Prohibited operation */
8862 nc = IntGroupGetCount(where);
8864 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
8870 n1 = mp->ndihedrals;
8872 if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8873 || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
8874 __MoleculeUnlock(mp);
8875 Panic("Low memory while adding dihedrals");
8877 mp->needsMDRebuild = 1;
8878 __MoleculeUnlock(mp);
8883 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
8886 if (mp == NULL || where == NULL)
8888 if (mp->noModifyTopology)
8889 return -4; /* Prohibited operation */
8891 if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
8892 __MoleculeUnlock(mp);
8893 Panic("Internal error: bad argument while deleting dihedrals");
8895 mp->ndihedrals -= (nc = IntGroupGetCount(where));
8896 if (mp->ndihedrals == 0) {
8897 free(mp->dihedrals);
8898 mp->dihedrals = NULL;
8900 mp->needsMDRebuild = 1;
8901 __MoleculeUnlock(mp);
8906 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
8909 if (mp == NULL || impropers == NULL)
8911 if (mp->noModifyTopology)
8912 return -4; /* Prohibited operation */
8914 nc = IntGroupGetCount(where);
8916 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
8922 n1 = mp->nimpropers;
8924 if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8925 || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
8926 __MoleculeUnlock(mp);
8927 Panic("Low memory while adding impropers");
8929 mp->needsMDRebuild = 1;
8930 __MoleculeUnlock(mp);
8935 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
8938 if (mp == NULL || where == NULL)
8940 if (mp->noModifyTopology)
8941 return -4; /* Prohibited operation */
8943 if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
8944 __MoleculeUnlock(mp);
8945 Panic("Internal error: bad argument while deleting impropers");
8947 mp->nimpropers -= (nc = IntGroupGetCount(where));
8948 if (mp->impropers == NULL) {
8949 free(mp->impropers);
8950 mp->impropers = NULL;
8952 __MoleculeUnlock(mp);
8957 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
8960 if (mp == NULL || mp->bonds == NULL)
8962 for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
8963 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
8970 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
8973 if (mp == NULL || mp->angles == NULL)
8975 for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8976 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
8977 (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
8984 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8987 if (mp == NULL || mp->dihedrals == NULL)
8989 for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8990 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
8991 (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
8998 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
9001 if (mp == NULL || mp->impropers == NULL)
9003 for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
9006 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
9007 (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
9008 (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
9014 /* Remove the bond at bondIndex and create two dummy atoms instead.
9015 The dummy atoms are placed at the end of atoms[], and the residue
9016 numbers are the same as the root atoms (i.e. the atoms to which
9017 the dummy atoms are connected). The indices are returned in
9018 dummyIndices[0,1]. */
9020 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
9022 Int roots[3], newBonds[5];
9028 if (mp == NULL || mp->noModifyTopology)
9030 if (bondIndex < 0 || bondIndex >= mp->nbonds)
9032 roots[0] = mp->bonds[bondIndex * 2];
9033 roots[1] = mp->bonds[bondIndex * 2 + 1];
9034 roots[2] = kInvalidIndex;
9035 rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
9036 rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
9037 VecSub(dr, rootp[0]->r, rootp[1]->r);
9038 for (i = 0; i < 2; i++) {
9041 memmove(nap, rootp[i], sizeof(na));
9042 nap->aname[0] = '*';
9043 strcpy(nap->element, "Du");
9045 nap->charge = nap->weight = 0.0;
9046 nap->atomicNumber = 0;
9047 nap->connect.count = 0;
9048 w = (i == 0 ? 0.4 : -0.4);
9049 VecScaleInc(nap->r, dr, w);
9056 /* Expand atoms array and append the dummy atoms at the end */
9058 natoms = mp->natoms;
9059 if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
9061 memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
9062 dummyIndices[0] = natoms;
9063 dummyIndices[1] = natoms + 1;
9065 /* Remove the old bond and create new bonds */
9066 ig = IntGroupNewWithPoints(bondIndex, 1, -1);
9069 MoleculeDeleteBonds(mp, NULL, ig, NULL, NULL);
9070 IntGroupRelease(ig);
9071 newBonds[0] = roots[0];
9072 newBonds[1] = dummyIndices[0];
9073 newBonds[2] = roots[1];
9074 newBonds[3] = dummyIndices[1];
9075 newBonds[4] = kInvalidIndex;
9077 i = (MoleculeAddBonds(mp, 2, newBonds, NULL, 1) < 0 ? -1 : 0);
9078 mp->needsMDRebuild = 1;
9079 __MoleculeUnlock(mp);
9083 __MoleculeUnlock(mp);
9084 Panic("Low memory during creating dummy atoms");
9088 /* Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
9089 a bond between the two root atoms. The value bondIndex is used as a
9090 hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
9091 the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
9092 is ignored and the new bond is stored at the end of bonds[]. */
9094 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
9101 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
9104 if (mol == NULL || mol->noModifyTopology)
9111 __MoleculeLock(mol);
9112 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
9113 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
9114 mol->needsMDRebuild = 1;
9115 __MoleculeUnlock(mol);
9122 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
9125 if (mol == NULL || mol->noModifyTopology)
9127 n1 = mol->ndihedrals;
9128 np1 = mol->dihedrals;
9129 mol->ndihedrals = 0;
9130 mol->dihedrals = NULL;
9131 if (ndihedrals > 0) {
9132 __MoleculeLock(mol);
9133 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
9134 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
9135 mol->needsMDRebuild = 1;
9136 __MoleculeUnlock(mol);
9138 *outDihedrals = np1;
9143 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
9146 if (mol == NULL || mol->noModifyTopology)
9148 n1 = mol->nimpropers;
9149 np1 = mol->impropers;
9150 mol->nimpropers = 0;
9151 mol->impropers = NULL;
9152 if (nimpropers > 0) {
9153 __MoleculeLock(mol);
9154 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
9155 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
9156 mol->needsMDRebuild = 1;
9157 __MoleculeUnlock(mol);
9159 *outImpropers = np1;
9165 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
9172 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
9173 return 0; /* molecule is empty */
9174 if (mol->noModifyTopology)
9178 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
9179 Int *cp = AtomConnectData(&ap->connect);
9180 if (ap->anchor != NULL)
9182 for (j = 0; j < ap->connect.count; j++) {
9184 if (ATOM_AT_INDEX(mol->atoms, j0)->anchor != NULL)
9186 for (k = j + 1; k < ap->connect.count; k++) {
9188 if (ATOM_AT_INDEX(mol->atoms, k0)->anchor != NULL)
9190 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
9191 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
9200 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
9204 if (outAngles != NULL)
9205 *outAngles = angles;
9210 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
9212 Int n1, n2, n3, n4, *ip, *cp2, *cp3;
9217 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
9218 return 0; /* molecule is empty */
9221 for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
9222 Int i1, i3, i4, *ip;
9223 if (ap2->anchor != NULL)
9225 cp2 = AtomConnectData(&ap2->connect);
9226 for (i3 = 0; i3 < ap2->connect.count; i3++) {
9230 ap3 = ATOM_AT_INDEX(mol->atoms, n3);
9231 if (ap3->anchor != NULL)
9233 cp3 = AtomConnectData(&ap3->connect);
9234 for (i1 = 0; i1 < ap2->connect.count; i1++) {
9238 if (ATOM_AT_INDEX(mol->atoms, n1)->anchor != NULL)
9240 for (i4 = 0; i4 < ap3->connect.count; i4++) {
9242 if (n2 == n4 || n1 == n4)
9244 if (ATOM_AT_INDEX(mol->atoms, n4)->anchor != NULL)
9246 if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
9247 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
9257 if (ndihedrals > 0) {
9258 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
9262 if (outDihedrals != NULL)
9263 *outDihedrals = dihedrals;
9268 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
9270 Int n1, n2, n3, n4, t1, t2, t3, t4, *ip, *cp;
9271 Parameter *par = mol->par;
9276 if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
9277 return 0; /* molecule is empty */
9278 if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
9279 return 0; /* No improper parameters are defined */
9283 for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
9284 Int i1, i2, i4, found, *ip;
9286 cp = AtomConnectData(&ap3->connect);
9287 for (i1 = 0; i1 < ap3->connect.count; i1++) {
9289 t1 = ATOM_AT_INDEX(ap, n1)->type;
9290 for (i2 = i1 + 1; i2 < ap3->connect.count; i2++) {
9292 t2 = ATOM_AT_INDEX(ap, n2)->type;
9293 for (i4 = i2 + 1; i4 < ap3->connect.count; i4++) {
9295 t4 = ATOM_AT_INDEX(ap, n4)->type;
9297 if (ParameterLookupImproperPar(par, t1, t2, t3, t4, n1, n2, n3, n4, 0) != NULL)
9299 else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, -1, -1, -1, -1, 0) != NULL)
9301 if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
9302 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
9312 if (nimpropers > 0) {
9313 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
9317 if (outImpropers != NULL)
9318 *outImpropers = impropers;
9322 #pragma mark ====== Residues ======
9325 MoleculeCleanUpResidueTable(Molecule *mp)
9329 if (mp == NULL || mp->natoms == 0)
9333 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9334 if (ap->resSeq >= maxres)
9335 maxres = ap->resSeq + 1;
9336 if (ap->resSeq < mp->nresidues) {
9337 if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
9338 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
9340 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
9343 if (maxres < mp->nresidues)
9344 mp->nresidues = maxres;
9345 __MoleculeUnlock(mp);
9348 /* Change the number of residues. If nresidues is greater than the current value,
9349 then the array mp->residues is expanded with null names. If nresidues is smaller
9350 than the current value, mp->nresidues is set to the smallest possible value
9351 that is no smaller than nresidues and larger than any of the resSeq values. */
9353 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
9358 if (mp->nresidues == nresidues)
9360 else if (mp->nresidues < nresidues) {
9363 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
9364 while (n < nresidues)
9365 mp->residues[n++][0] = 0;
9366 __MoleculeUnlock(mp);
9372 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9373 if (ap->resSeq >= n)
9382 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
9384 IntGroupIterator iter;
9385 int withArray, resSeq, maxSeq;
9389 /* If LSB of resSeqs is 1, then a constant value is used for all specified atoms */
9390 if (((uintptr_t)resSeqs & 1) == 0) {
9395 resSeq = ((uintptr_t)resSeqs - 1) / 2;
9398 IntGroupIteratorInit(group, &iter);
9400 /* Change resSeqs */
9404 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9405 ap = ATOM_AT_INDEX(mp->atoms, i);
9407 resSeq = resSeqs[j++];
9408 if (resSeq > maxSeq)
9410 ap->resSeq = resSeq;
9412 __MoleculeUnlock(mp);
9414 /* Expand array if necessary */
9415 if (maxSeq >= mp->nresidues)
9416 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
9418 /* Synchronize resName and residues[] */
9420 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9421 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9423 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
9425 IntGroupIteratorRelease(&iter);
9426 __MoleculeUnlock(mp);
9428 MoleculeIncrementModifyCount(mp);
9434 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
9436 return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(intptr_t)(resSeq * 2 + 1));
9439 /* Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
9440 specifies the mp->nresidues after modifying the residue numbers.
9441 If all atoms are modified, then the table of residue names is also shifted. Otherwise,
9442 the table of residue names is not touched. */
9444 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
9446 int i, maxSeq, nmodatoms;
9448 IntGroupIterator iter;
9449 IntGroupIteratorInit(group, &iter);
9452 nresidues = mp->nresidues;
9455 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9456 ap = ATOM_AT_INDEX(mp->atoms, i);
9457 ap->resSeq += offset;
9458 if (ap->resSeq < 0) {
9459 /* Bad argument; undo change and returns this index + 1 */
9461 ap->resSeq -= offset;
9462 while ((i = IntGroupIteratorLast(&iter)) >= 0) {
9463 ap = ATOM_AT_INDEX(mp->atoms, i);
9464 ap->resSeq -= offset;
9466 IntGroupIteratorRelease(&iter);
9467 return bad_index + 1;
9469 if (ap->resSeq > maxSeq)
9470 maxSeq = ap->resSeq;
9473 if (maxSeq >= nresidues)
9474 nresidues = maxSeq + 1;
9475 if (offset < 0 && nmodatoms == mp->natoms) {
9476 /* Shift the residue names downward */
9477 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
9479 __MoleculeUnlock(mp);
9480 MoleculeChangeNumberOfResidues(mp, nresidues);
9481 if (offset > 0 && nmodatoms == mp->natoms) {
9482 /* Shift the residue names upward */
9484 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
9485 __MoleculeUnlock(mp);
9487 IntGroupIteratorRelease(&iter);
9489 MoleculeIncrementModifyCount(mp);
9494 /* Change residue names for the specified residue numbers. Names is an array of
9495 chars containing argc*4 characters, and every 4 characters represent a
9496 residue name; characters '\x01'-'\x1f' are converted to '\0', which allow
9497 names to be handled as a C string. */
9499 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
9504 for (i = 0; i < argc; i++) {
9505 if (maxSeq < resSeqs[i])
9506 maxSeq = resSeqs[i];
9508 if (maxSeq >= mp->nresidues)
9509 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
9511 for (i = 0; i < argc; i++) {
9512 char *p = mp->residues[resSeqs[i]];
9514 strncpy(p, names + i * 4, 4);
9515 for (j = 0; j < 4; j++) {
9516 if (p[j] >= 0 && p[j] < 0x20)
9520 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9521 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
9523 __MoleculeUnlock(mp);
9525 MoleculeIncrementModifyCount(mp);
9530 /* Returns the maximum residue number actually used */
9532 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
9537 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9538 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9540 if (ap->resSeq > maxSeq)
9541 maxSeq = ap->resSeq;
9546 /* Returns the minimum residue number actually used */
9548 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
9552 minSeq = ATOMS_MAX_NUMBER;
9553 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9554 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9556 if (ap->resSeq < minSeq)
9557 minSeq = ap->resSeq;
9559 return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
9562 #pragma mark ====== Sort by Residues ======
9565 sAtomSortComparator(const void *a, const void *b)
9567 const Atom *ap, *bp;
9568 ap = *((const Atom **)a);
9569 bp = *((const Atom **)b);
9570 if (ap->resSeq == bp->resSeq) {
9571 /* Retain the original order (i.e. atom with larger pointer address is larger) */
9578 /* Compare the residue sequence. However, residue sequence 0 is always larger. */
9579 if (ap->resSeq == 0)
9581 else if (bp->resSeq == 0)
9583 else if (ap->resSeq < bp->resSeq)
9585 else if (ap->resSeq > bp->resSeq)
9592 sMoleculeReorder(Molecule *mp)
9594 int i, res, prevRes;
9598 if (mp == NULL || mp->natoms <= 1)
9601 /* Sort the atoms, bonds, etc. */
9602 apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
9603 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9604 newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9605 if (apArray == NULL || old2new == NULL || newAtoms == NULL)
9606 Panic("Low memory during reordering atoms");
9607 for (i = 0; i < mp->natoms; i++)
9608 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
9610 /* Sort the atoms. Note: apArray is an array of "Pointer to Atom" */
9611 qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
9613 /* Make a table of 'which atom becomes which' */
9614 for (i = 0; i < mp->natoms; i++) {
9615 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
9619 /* Renumber the bonds, etc. */
9620 for (i = 0; i < mp->nbonds * 2; i++) {
9621 mp->bonds[i] = old2new[mp->bonds[i]];
9623 for (i = 0; i < mp->nangles * 3; i++) {
9624 mp->angles[i] = old2new[mp->angles[i]];
9626 for (i = 0; i < mp->ndihedrals * 4; i++) {
9627 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9629 for (i = 0; i < mp->nimpropers * 4; i++) {
9630 mp->impropers[i] = old2new[mp->impropers[i]];
9632 for (i = 0; i < mp->natoms; i++) {
9634 ip = AtomConnectData(&(apArray[i]->connect));
9635 for (j = 0; j < apArray[i]->connect.count; j++, ip++)
9639 /* Renumber the residues so that the residue numbers are contiguous */
9641 for (i = 0; i < mp->natoms; i++) {
9642 if (apArray[i]->resSeq == 0)
9644 if (apArray[i]->resSeq != prevRes) {
9646 prevRes = apArray[i]->resSeq;
9647 if (prevRes != res) {
9648 strncpy(mp->residues[res], mp->residues[prevRes], 4);
9651 apArray[i]->resSeq = res;
9653 mp->nresidues = res + 1;
9655 /* Sort the atoms and copy back to atoms[] */
9656 for (i = 0; i < mp->natoms; i++) {
9657 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
9659 memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
9661 /* Free the locally allocated storage */
9667 /* Renumber atoms */
9669 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
9671 Int *old2new, i, j, retval;
9675 if (mp->noModifyTopology)
9677 if (old2new_out != NULL)
9678 old2new = old2new_out;
9680 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9681 saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9682 if (old2new == NULL || saveAtoms == NULL)
9683 Panic("Low memory during reordering atoms");
9684 memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
9686 for (i = 0; i < mp->natoms; i++)
9688 for (i = 0; i < isize && i < mp->natoms; i++) {
9690 if (j < 0 || j >= mp->natoms) {
9691 retval = 1; /* Out of range */
9694 if (old2new[j] != -1) {
9695 retval = 2; /* Duplicate entry */
9700 if (i < mp->natoms) {
9701 for (j = 0; j < mp->natoms; j++) {
9702 if (old2new[j] != -1)
9707 if (i != mp->natoms) {
9708 retval = 3; /* Internal inconsistency */
9712 /* Renumber the bonds, etc. */
9713 for (i = 0; i < mp->nbonds * 2; i++) {
9714 mp->bonds[i] = old2new[mp->bonds[i]];
9716 for (i = 0; i < mp->nangles * 3; i++) {
9717 mp->angles[i] = old2new[mp->angles[i]];
9719 for (i = 0; i < mp->ndihedrals * 4; i++) {
9720 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9722 for (i = 0; i < mp->nimpropers * 4; i++) {
9723 mp->impropers[i] = old2new[mp->impropers[i]];
9725 /* Renumber the connection table and pi anchor table */
9726 for (i = 0; i < mp->natoms; i++) {
9727 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
9728 Int *ip = AtomConnectData(&ap->connect);
9729 for (j = 0; j < ap->connect.count; j++, ip++)
9731 if (ap->anchor != NULL) {
9732 ip = AtomConnectData(&ap->anchor->connect);
9733 for (j = 0; j < ap->anchor->connect.count; j++, ip++)
9738 if (mp->par != NULL) {
9739 /* Renumber the parameters */
9741 for (j = kFirstParType; j <= kLastParType; j++) {
9742 n = ParameterGetCountForType(mp->par, j);
9743 for (i = 0; i < n; i++) {
9744 UnionPar *up = ParameterGetUnionParFromTypeAndIndex(mp->par, j, i);
9746 ParameterRenumberAtoms(j, up, mp->natoms, old2new);
9751 /* Renumber the atoms */
9752 for (i = 0; i < mp->natoms; i++)
9753 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
9756 MoleculeIncrementModifyCount(mp);
9757 mp->needsMDRebuild = 1;
9760 __MoleculeUnlock(mp);
9762 if (old2new_out == NULL)
9767 #pragma mark ====== Coordinate Transform ======
9770 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
9775 Transform rtr, symtr;
9776 if (mp == NULL || tr == NULL)
9778 TransformInvert(rtr, tr);
9780 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9781 if (group == NULL || IntGroupLookup(group, i, NULL) != 0) {
9782 TransformVec(&ap->r, tr, &ap->r);
9783 if (!SYMOP_ALIVE(ap->symop))
9785 /* Transform symop */
9786 if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9788 TransformMul(symtr, tr, symtr);
9789 if (group == NULL || IntGroupLookup(group, ap->symbase, NULL) != 0)
9790 TransformMul(symtr, symtr, rtr);
9792 if (!SYMOP_ALIVE(ap->symop))
9794 /* Transform symop if the base atom is transformed */
9795 if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
9797 if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9799 TransformMul(symtr, symtr, rtr);
9801 if (MoleculeGetSymopForTransform(mp, symtr, &new_symop, 1) != 0)
9803 ap->symop = new_symop;
9805 mp->needsMDCopyCoordinates = 1;
9806 __MoleculeUnlock(mp);
9807 sMoleculeNotifyChangeAppearance(mp);
9812 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
9816 if (mp == NULL || tr == NULL)
9819 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9820 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9822 TransformVec(&ap->r, tr, &ap->r);
9824 mp->needsMDCopyCoordinates = 1;
9825 __MoleculeUnlock(mp);
9826 sMoleculeNotifyChangeAppearance(mp);
9831 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
9834 if (mp == NULL || vp == NULL)
9836 memset(tr, 0, sizeof(tr));
9837 tr[0] = tr[4] = tr[8] = 1.0;
9841 MoleculeTransform(mp, tr, group);
9845 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
9848 TransformForRotation(tr, axis, angle, center);
9849 MoleculeTransform(mp, tr, group);
9853 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
9858 if (mp == NULL || center == NULL)
9860 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9861 return 2; /* Empty molecule */
9863 center->x = center->y = center->z = 0.0;
9864 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9865 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9867 VecScaleInc(*center, ap->r, ap->weight);
9871 return 3; /* Atomic weights are not defined? */
9873 VecScaleSelf(*center, w);
9878 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
9885 if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9886 return 2; /* Empty molecule */
9887 vmin.x = vmin.y = vmin.z = 1e50;
9888 vmax.x = vmax.y = vmax.z = -1e50;
9889 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9890 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9892 if (vmin.x > ap->r.x)
9894 if (vmin.y > ap->r.y)
9896 if (vmin.z > ap->r.z)
9898 if (vmax.x < ap->r.x)
9900 if (vmax.y < ap->r.y)
9902 if (vmax.z < ap->r.z)
9912 #pragma mark ====== Measurements ======
9915 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
9918 /* if (mp->is_xtal_coord) {
9919 TransformVec(&r1, mp->cell->tr, vp1);
9920 TransformVec(&r2, mp->cell->tr, vp2);
9926 return VecLength(r1);
9930 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
9934 /* if (mp->is_xtal_coord) {
9935 TransformVec(&r1, mp->cell->tr, vp1);
9936 TransformVec(&r2, mp->cell->tr, vp2);
9937 TransformVec(&r3, mp->cell->tr, vp3);
9945 w = VecLength(r1) * VecLength(r3);
9948 return acos(VecDot(r1, r3) / w) * kRad2Deg;
9952 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
9954 Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
9956 /* if (mp->is_xtal_coord) {
9957 TransformVec(&r1, mp->cell->tr, vp1);
9958 TransformVec(&r2, mp->cell->tr, vp2);
9959 TransformVec(&r3, mp->cell->tr, vp3);
9960 TransformVec(&r4, mp->cell->tr, vp4);
9967 VecSub(r21, r1, r2);
9968 VecSub(r32, r2, r3);
9969 VecSub(r43, r3, r4);
9970 VecCross(v1, r21, r32);
9971 VecCross(v2, r32, r43);
9972 VecCross(v3, r32, v1);
9976 if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
9982 VecScaleSelf(v1, w1);
9983 VecScaleSelf(v2, w2);
9984 VecScaleSelf(v3, w3);
9985 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
9989 #pragma mark ====== XtalCell Parameters ======
9992 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
9994 if (mp->cell != NULL) {
9995 TransformVec(dst, mp->cell->tr, src);
10000 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
10002 if (mp->cell != NULL) {
10003 TransformVec(dst, mp->cell->rtr, src);
10004 } else *dst = *src;
10008 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
10010 static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
10012 Vector *vp1, *vp2, *vp3;
10017 for (n1 = 0; n1 < 3; n1++) {
10018 if (cp->flags[n1] != 0)
10022 /* All directions are non-periodic */
10023 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
10024 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
10028 vp1 = &(cp->axes[n1]);
10029 vp2 = &(cp->axes[n2]);
10030 vp3 = &(cp->axes[n3]);
10031 cp->tr[n1*3] = vp1->x;
10032 cp->tr[n1*3+1] = vp1->y;
10033 cp->tr[n1*3+2] = vp1->z;
10034 cp->tr[9] = cp->origin.x;
10035 cp->tr[10] = cp->origin.y;
10036 cp->tr[11] = cp->origin.z;
10037 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
10038 /* 1-dimensional or 2-dimensional system */
10039 /* Create "dummy" axes, so that transforms between internal and cartesian coordinates are
10040 possible with a single matrix */
10041 if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
10042 /* 1-dimensional */
10043 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
10044 VecCross(v1, *vp1, xvec);
10045 VecCross(v2, *vp1, yvec);
10046 if (VecLength2(v1) < VecLength2(v2))
10048 VecCross(v2, *vp1, v1);
10049 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
10050 return -1; /* Non-regular transform */
10051 } else if (cp->flags[n2] == 0) {
10053 VecCross(v1, v2, *vp1);
10054 if (NormalizeVec(&v1, &v1))
10055 return -1; /* Non-regular transform */
10058 VecCross(v2, *vp1, v1);
10059 if (NormalizeVec(&v2, &v2))
10060 return -1; /* Non-regular transform */
10062 cp->tr[n2*3] = v1.x;
10063 cp->tr[n2*3+1] = v1.y;
10064 cp->tr[n2*3+2] = v1.z;
10065 cp->tr[n3*3] = v2.x;
10066 cp->tr[n3*3+1] = v2.y;
10067 cp->tr[n3*3+2] = v2.z;
10069 VecCross(v1, *vp1, *vp2);
10070 if (fabs(VecDot(v1, *vp3)) < 1e-7)
10071 return -1; /* Non-regular transform */
10072 cp->tr[n2*3] = vp2->x;
10073 cp->tr[n2*3+1] = vp2->y;
10074 cp->tr[n2*3+2] = vp2->z;
10075 cp->tr[n3*3] = vp3->x;
10076 cp->tr[n3*3+1] = vp3->y;
10077 cp->tr[n3*3+2] = vp3->z;
10080 if (TransformInvert(cp->rtr, cp->tr))
10081 return -1; /* Non-regular transform */
10083 /* Calculate the reciprocal cell parameters */
10084 cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[3] * cp->rtr[3] + cp->rtr[6] * cp->rtr[6]);
10085 cp->rcell[1] = sqrt(cp->rtr[1] * cp->rtr[1] + cp->rtr[4] * cp->rtr[4] + cp->rtr[7] * cp->rtr[7]);
10086 cp->rcell[2] = sqrt(cp->rtr[2] * cp->rtr[2] + cp->rtr[5] * cp->rtr[5] + cp->rtr[8] * cp->rtr[8]);
10087 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;
10088 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;
10089 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;
10092 /* Calculate a, b, c, alpha, beta, gamma */
10093 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[1] * cp->tr[1] + cp->tr[2] * cp->tr[2]);
10094 cp->cell[1] = sqrt(cp->tr[3] * cp->tr[3] + cp->tr[4] * cp->tr[4] + cp->tr[5] * cp->tr[5]);
10095 cp->cell[2] = sqrt(cp->tr[6] * cp->tr[6] + cp->tr[7] * cp->tr[7] + cp->tr[8] * cp->tr[8]);
10096 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;
10097 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;
10098 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;
10104 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
10112 __MoleculeLock(mp);
10113 memset(&cmat, 0, sizeof(Transform));
10114 if (mp->cell != NULL)
10115 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
10117 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
10119 if (mp->cell != NULL) {
10121 mp->needsMDRebuild = 1;
10127 cp = (XtalCell *)calloc(sizeof(XtalCell), 1);
10129 Panic("Low memory during setting cell parameters");
10131 mp->needsMDRebuild = 1;
10133 /* alpha, beta, gamma are in degree */
10137 cp->cell[3] = alpha;
10138 cp->cell[4] = beta;
10139 cp->cell[5] = gamma;
10140 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
10141 /* c unique (hexagonal etc.) */
10142 Double cosa, cosb, sinb, cosg;
10143 cosa = cos(alpha * kDeg2Rad);
10144 cosb = cos(beta * kDeg2Rad);
10145 sinb = sin(beta * kDeg2Rad);
10146 cosg = cos(gamma * kDeg2Rad);
10147 cp->axes[0].x = a * sinb;
10149 cp->axes[0].z = a * cosb;
10150 cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
10151 cp->axes[1].z = b * cosa;
10152 cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
10158 Double cosg, sing, cosa, cosb;
10159 cosa = cos(alpha * kDeg2Rad);
10160 cosb = cos(beta * kDeg2Rad);
10161 cosg = cos(gamma * kDeg2Rad);
10162 sing = sin(gamma * kDeg2Rad);
10163 cp->axes[0].x = a * sing;
10164 cp->axes[0].y = a * cosg;
10169 cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
10170 cp->axes[2].y = c * cosa;
10171 cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
10173 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
10174 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
10175 MoleculeCalculateCellFromAxes(cp, 0);
10176 TransformMul(cmat, cp->tr, cmat);
10179 /* Update the coordinates (if requested) */
10180 if (convertCoordinates) {
10181 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10182 TransformVec(&(ap->r), cmat, &(ap->r));
10186 /* Update the anisotropic parameters */
10187 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10188 Aniso *anp = ap->aniso;
10190 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
10193 __MoleculeUnlock(mp);
10194 sMoleculeNotifyChangeAppearance(mp);
10198 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x13, Double x23, const Double *sigmaptr)
10202 const Double log2 = 0.693147180559945;
10203 const Double pi22 = 19.7392088021787; /* 2*pi**2 */
10209 if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
10211 anp = mp->atoms[n1].aniso;
10212 __MoleculeLock(mp);
10214 anp = (Aniso *)calloc(sizeof(Aniso), 1);
10216 __MoleculeUnlock(mp);
10217 Panic("Low memory during setting anisotropic atom parameters");
10219 mp->atoms[n1].aniso = anp;
10222 case 1: d = 1; dx = 0.5; break;
10223 case 2: d = log2; dx = log2; break;
10224 case 3: d = log2; dx = log2 * 0.5; break;
10225 case 4: u = 1; d = 0.25; dx = 0.25; break;
10226 case 5: u = 1; d = 0.25; dx = 0.125; break;
10227 case 8: u = 1; d = pi22; dx = pi22; break;
10228 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
10229 case 10: d = pi22; dx = pi22; break;
10230 default: d = dx = 1; break;
10232 anp->bij[0] = x11 * d;
10233 anp->bij[1] = x22 * d;
10234 anp->bij[2] = x33 * d;
10235 anp->bij[3] = x12 * dx;
10236 anp->bij[4] = x13 * dx;
10237 anp->bij[5] = x23 * dx;
10238 if (sigmaptr != NULL) {
10240 anp->bsig[0] = sigmaptr[0] * d;
10241 anp->bsig[1] = sigmaptr[1] * d;
10242 anp->bsig[2] = sigmaptr[2] * d;
10243 anp->bsig[3] = sigmaptr[3] * dx;
10244 anp->bsig[4] = sigmaptr[4] * dx;
10245 anp->bsig[5] = sigmaptr[5] * dx;
10248 anp->bsig[0] = anp->bsig[1] = anp->bsig[2] = anp->bsig[3] = anp->bsig[4] = anp->bsig[5] = 0.0;
10251 if (cp != NULL && u == 1) {
10252 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
10253 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
10254 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
10255 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
10256 anp->bij[4] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[3] * kDeg2Rad); */
10257 anp->bij[5] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[4] * kDeg2Rad); */
10258 if (sigmaptr != NULL) {
10259 anp->bsig[0] *= cp->rcell[0] * cp->rcell[0];
10260 anp->bsig[1] *= cp->rcell[1] * cp->rcell[1];
10261 anp->bsig[2] *= cp->rcell[2] * cp->rcell[2];
10262 anp->bsig[3] *= cp->rcell[0] * cp->rcell[1];
10263 anp->bsig[4] *= cp->rcell[2] * cp->rcell[0];
10264 anp->bsig[5] *= cp->rcell[1] * cp->rcell[2];
10268 /* Calculate the principal axes (in Cartesian coordinates) */
10269 /* The principal axes are the eigenvectors of matrix At(B^-1)A, where
10270 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
10271 in which x and z are the crystal-space and cartesian coordinates. */
10272 m1[0] = anp->bij[0] / pi22;
10273 m1[4] = anp->bij[1] / pi22;
10274 m1[8] = anp->bij[2] / pi22;
10275 m1[1] = m1[3] = anp->bij[3] / pi22;
10276 m1[2] = m1[6] = anp->bij[4] / pi22;
10277 m1[5] = m1[7] = anp->bij[5] / pi22;
10278 MatrixInvert(m1, m1);
10280 memmove(m2, cp->rtr, sizeof(Mat33));
10281 MatrixMul(m1, m1, m2);
10282 MatrixTranspose(m2, m2);
10283 MatrixMul(m1, m2, m1);
10285 MatrixSymDiagonalize(m1, val, axis);
10286 for (u = 0; u < 3; u++) {
10287 anp->eigval[u] = val[u];
10289 fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
10292 val[u] = 1 / sqrt(val[u]);
10294 anp->pmat[u*3] = axis[u].x * val[u];
10295 anp->pmat[u*3+1] = axis[u].y * val[u];
10296 anp->pmat[u*3+2] = axis[u].z * val[u];
10298 __MoleculeUnlock(mp);
10301 /* Set the anisotropic parameter for atom idx according to the symop. If symop is not alive, nothing is done. */
10303 MoleculeSetAnisoBySymop(Molecule *mp, int idx)
10307 if (mp == NULL || idx < 0 || idx >= mp->natoms)
10309 ap = ATOM_AT_INDEX(mp->atoms, idx);
10310 if (!SYMOP_ALIVE(ap->symop))
10312 ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
10313 if (ap2->aniso == NULL) {
10314 if (ap->aniso != NULL) {
10320 if (ap->aniso == NULL)
10321 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
10322 if (ap->symop.sym == 0 || ap->symop.sym >= mp->nsyms) {
10323 /* Just copy the aniso parameters */
10324 memmove(ap->aniso, ap2->aniso, sizeof(Aniso));
10327 memmove(t1, SYMMETRY_AT_INDEX(mp->syms, ap->symop.sym), sizeof(Transform));
10328 t1[9] = t1[10] = t1[11] = 0.0;
10329 memset(t2, 0, sizeof(Transform));
10330 t2[0] = ap2->aniso->bij[0];
10331 t2[4] = ap2->aniso->bij[1];
10332 t2[8] = ap2->aniso->bij[2];
10333 t2[1] = t2[3] = ap2->aniso->bij[3];
10334 t2[2] = t2[6] = ap2->aniso->bij[4];
10335 t2[5] = t2[7] = ap2->aniso->bij[5];
10336 TransformMul(t2, t1, t2);
10337 TransformInvert(t1, t1);
10338 TransformMul(t2, t2, t1);
10339 MoleculeSetAniso(mp, idx, 0, t2[0], t2[4], t2[8], t2[1], t2[2], t2[5], (ap2->aniso->has_bsig ? ap2->aniso->bsig : NULL));
10343 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic, int convertCoordinates)
10345 static Vector zeroVec = {0, 0, 0};
10352 if (mp->cell != NULL)
10353 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
10355 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
10357 if (mp->cell != NULL) {
10359 mp->needsMDRebuild = 1;
10364 memset(&b, 0, sizeof(b));
10365 b.axes[0] = (ax != NULL ? *ax : zeroVec);
10366 b.axes[1] = (ay != NULL ? *ay : zeroVec);
10367 b.axes[2] = (az != NULL ? *az : zeroVec);
10369 memmove(b.flags, periodic, 3);
10370 if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
10372 __MoleculeLock(mp);
10373 if (mp->cell == NULL) {
10374 mp->needsMDRebuild = 1;
10376 if (mp->cell->has_sigma) {
10377 /* Keep the sigma */
10379 memmove(b.cellsigma, mp->cell->cellsigma, sizeof(mp->cell->cellsigma));
10381 if ((b.flags[0] != mp->cell->flags[0]) || (b.flags[1] != mp->cell->flags[1]) || (b.flags[2] != mp->cell->flags[2])) {
10382 mp->needsMDRebuild = 1;
10386 mp->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
10387 if (mp->cell != NULL) {
10388 memmove(mp->cell, &b, sizeof(XtalCell));
10389 TransformMul(cmat, b.tr, cmat);
10390 /* Update the coordinates (if requested) */
10391 if (convertCoordinates) {
10392 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10393 TransformVec(&(ap->r), cmat, &(ap->r));
10397 /* Update the anisotropic parameters */
10398 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10399 Aniso *anp = ap->aniso;
10401 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
10405 } else n = -2; /* Out of memory */
10406 __MoleculeUnlock(mp);
10407 sMoleculeNotifyChangeAppearance(mp);
10411 #pragma mark ====== Fragment manipulation ======
10414 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
10418 if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
10420 IntGroupAdd(result, idx, 1);
10421 ap = ATOM_AT_INDEX(mp->atoms, idx);
10422 cp = AtomConnectData(&ap->connect);
10423 for (i = 0; i < ap->connect.count; i++) {
10425 if (IntGroupLookup(result, idx2, NULL))
10427 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, idx2)->anchor != NULL)
10428 continue; /* bond between two pi_anchors is ignored */
10429 sMoleculeFragmentSub(mp, idx2, result, exatoms);
10431 if (ap->anchor != NULL) {
10432 cp = AtomConnectData(&ap->anchor->connect);
10433 for (i = 0; i < ap->anchor->connect.count; i++) {
10435 if (IntGroupLookup(result, idx2, NULL))
10437 sMoleculeFragmentSub(mp, idx2, result, exatoms);
10442 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
10443 not containing the atoms in exatoms */
10445 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
10448 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
10450 result = IntGroupNew();
10451 sMoleculeFragmentSub(mp, n1, result, exatoms);
10455 /* The molecular fragment (= interconnected atoms) containing the atom n1 and
10456 not containing the atoms n2, n3, ... (terminated by -1) */
10458 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
10461 IntGroup *exatoms, *result;
10462 if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
10464 exatoms = IntGroupNew();
10465 for (i = 0; i < argc; i++)
10466 IntGroupAdd(exatoms, argv[i], 1);
10467 result = IntGroupNew();
10468 sMoleculeFragmentSub(mp, n1, result, exatoms);
10469 IntGroupRelease(exatoms);
10473 /* The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
10474 not containing the atoms in exatoms */
10476 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
10478 IntGroupIterator iter;
10481 if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
10483 IntGroupIteratorInit(inatoms, &iter);
10484 result = IntGroupNew();
10485 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
10486 sMoleculeFragmentSub(mp, i, result, exatoms);
10488 IntGroupIteratorRelease(&iter);
10492 /* Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
10493 group is bound to the rest of the molecule via only one bond.
10494 If the result is true, then the atoms belonging to the (only) bond are returned
10495 in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
10496 and n2 can be NULL, if those informations are not needed. */
10498 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
10500 Int i, i1, i2, j, k, bond_count, nval1, nval2, *cp;
10502 if (mp == NULL || mp->natoms == 0 || group == NULL)
10503 return 0; /* Invalid arguments */
10505 for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
10506 i2 = IntGroupGetEndPoint(group, i);
10507 for (j = i1; j < i2; j++) {
10508 if (j < 0 || j >= mp->natoms)
10509 return 0; /* Invalid atom group */
10510 ap = ATOM_AT_INDEX(mp->atoms, j);
10511 cp = AtomConnectData(&ap->connect);
10512 for (k = 0; k < ap->connect.count; k++) {
10513 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, cp[k])->anchor != NULL)
10514 continue; /* Ignore bond between two pi_anchors */
10515 if (IntGroupLookup(group, cp[k], NULL) == 0) {
10519 if (bond_count > 1)
10520 return 0; /* Too many bonds */
10523 if (ap->anchor != NULL) {
10524 cp = AtomConnectData(&ap->anchor->connect);
10525 for (k = 0; k < ap->anchor->connect.count; k++) {
10526 if (IntGroupLookup(group, cp[k], NULL) == 0) {
10530 if (bond_count > 1)
10531 return 0; /* Too many bonds */
10537 if (bond_count == 1) {
10548 /* Returns non-zero if the given group is 'rotatable' in the molecule. The group
10549 is said to be 'rotatable' when either of the following conditions are met; (1)
10550 the group is detachable, or (2) the group consists of two bonded atoms that define
10551 a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
10552 (either a new IntGroup or 'group' with incremented reference count; thus the caller
10553 is responsible for releasing the returned value). */
10555 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
10558 if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
10559 if (rotGroup != NULL) {
10560 IntGroupRetain(group);
10565 if (group != NULL && IntGroupGetCount(group) == 2) {
10566 i1 = IntGroupGetNthPoint(group, 0);
10567 i2 = IntGroupGetNthPoint(group, 1);
10568 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
10569 IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
10572 i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
10574 IntGroupRelease(frag);
10575 if (rotGroup != NULL)
10579 if (rotGroup != NULL)
10581 else if (frag != NULL)
10582 IntGroupRelease(frag);
10589 #pragma mark ====== Multiple frame ======
10592 MoleculeGetNumberOfFrames(Molecule *mp)
10596 if (mp->nframes <= 0) {
10600 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10601 if (ap->nframes > n)
10608 return mp->nframes;
10612 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
10614 int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
10615 Vector *tempv, *vp;
10620 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
10623 n_old = MoleculeGetNumberOfFrames(mp);
10624 n_new = n_old + count;
10625 last_inserted = IntGroupGetNthPoint(group, count - 1);
10626 if (n_new <= last_inserted) {
10627 exframes = last_inserted - n_new + 1; /* number of extra frames that will be silently inserted */
10629 } else exframes = 0;
10631 tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4); /* "*4" for handling cells */
10635 __MoleculeLock(mp);
10637 /* Copy back the current coordinates */
10638 /* No change in the current coordinates, but the frame buffer is updated */
10639 MoleculeSelectFrame(mp, mp->cframe, 1);
10641 /* Expand ap->frames for all atoms */
10642 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10643 Int n = ap->nframes;
10644 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), n_new - 1, NULL);
10645 if (ap->frames == NULL) {
10646 __MoleculeUnlock(mp);
10649 for (j = n; j < n_new; j++)
10650 ap->frames[j] = ap->r;
10652 if (mp->cell != NULL) {
10653 j = mp->nframe_cells;
10654 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, n_new - 1, NULL);
10655 for (i = j; i < n_new; i++) {
10656 /* Set the current cell parameters to the expanded frames */
10657 mp->frame_cells[i * 4] = mp->cell->axes[0];
10658 mp->frame_cells[i * 4 + 1] = mp->cell->axes[1];
10659 mp->frame_cells[i * 4 + 2] = mp->cell->axes[2];
10660 mp->frame_cells[i * 4 + 3] = mp->cell->origin;
10664 /* Expand propvals for all properties */
10665 for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
10666 dp = (Double *)realloc(prp->propvals, sizeof(Double) * n_new);
10668 __MoleculeUnlock(mp);
10671 for (j = n_old; j < n_new; j++)
10673 prp->propvals = dp;
10676 /* group = [n0..n1-1, n2..n3-1, ...] */
10678 /* tempv[0..n0-1] <- ap[0..n0-1], s += n0,
10679 tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
10680 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
10681 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
10683 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
10684 At last, s will become n_old and t will become count. */
10685 for (i = 0, ap = mp->atoms, prp = mp->molprops; i <= mp->natoms + mp->nmolprops; i++) {
10686 int s, t, ns, ne, mult;
10689 if (i == mp->natoms) {
10690 if (mp->cell == NULL || mp->frame_cells == NULL)
10692 vp = mp->frame_cells;
10694 } else if (i < mp->natoms) {
10699 dp = prp->propvals;
10701 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10703 if (i <= mp->natoms)
10704 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
10706 memmove((Double *)tempv + ne, dp + s, sizeof(Double) * (ns - ne));
10709 ne = IntGroupGetEndPoint(group, j);
10711 if (i == mp->natoms) {
10712 if (inFrameCell != NULL) {
10713 tempv[ns * 4] = inFrameCell[t * 4];
10714 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
10715 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
10716 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
10718 tempv[ns * 4] = mp->cell->axes[0];
10719 tempv[ns * 4 + 1] = mp->cell->axes[1];
10720 tempv[ns * 4 + 2] = mp->cell->axes[2];
10721 tempv[ns * 4 + 3] = mp->cell->origin;
10723 } else if (i < mp->natoms) {
10724 if (inFrame != NULL)
10725 tempv[ns] = inFrame[natoms * t + i];
10729 ((Double *)tempv)[ns] = 0.0;
10736 if (i <= mp->natoms)
10737 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
10739 memmove((Double *)tempv + ne, dp + s, sizeof(Double) * (n_new - ne));
10742 if (i < mp->natoms)
10743 ap->nframes = n_new;
10744 if (i <= mp->natoms) {
10745 memmove(vp, tempv, sizeof(Vector) * mult * n_new);
10746 if (i < mp->natoms) {
10747 ap->nframes = n_new;
10748 ap = ATOM_NEXT(ap);
10751 memmove(dp, (Double *)tempv, sizeof(Double) * n_new);
10756 mp->nframes = n_new;
10757 MoleculeSelectFrame(mp, last_inserted, 0);
10758 MoleculeIncrementModifyCount(mp);
10759 __MoleculeUnlock(mp);
10764 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
10766 int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
10767 Vector *tempv, *vp;
10770 IntGroup *group, *group2;
10772 if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
10775 /* outFrame[] should have enough size for Vector * natoms * group.count */
10776 memset(outFrame, 0, sizeof(Vector) * natoms * count);
10777 if (mp->cell != NULL && mp->frame_cells != NULL)
10778 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
10780 n_old = MoleculeGetNumberOfFrames(mp);
10782 return -2; /* Cannot delete last frame */
10784 group = IntGroupNew();
10785 group2 = IntGroupNewWithPoints(0, n_old, -1);
10786 IntGroupIntersect(inGroup, group2, group);
10787 IntGroupRelease(group2);
10788 count = IntGroupGetCount(group);
10789 n_new = n_old - count;
10791 IntGroupRelease(group);
10792 return -2; /* Trying to delete too many frames */
10794 tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4); /* "*4" for handling cells */
10795 if (tempv == NULL) {
10796 IntGroupRelease(group);
10800 __MoleculeLock(mp);
10802 /* Copy back the current coordinates */
10803 /* No change in the current coordinates, but the frame buffer is updated */
10804 MoleculeSelectFrame(mp, mp->cframe, 1);
10806 /* Determine which frame should be selected after removal is completed */
10809 if (IntGroupLookup(group, mp->cframe, &i)) {
10810 /* cframe will be removed */
10811 n1 = IntGroupGetStartPoint(group, i) - 1;
10813 n1 = IntGroupGetEndPoint(group, i);
10814 } else n1 = mp->cframe;
10815 /* Change to that frame */
10816 MoleculeSelectFrame(mp, n1, 0);
10817 group2 = IntGroupNewFromIntGroup(group);
10818 IntGroupReverse(group2, 0, n_old);
10819 new_cframe = IntGroupLookupPoint(group2, n1);
10820 if (new_cframe < 0)
10821 return -3; /* This cannot happen */
10822 IntGroupRelease(group2);
10825 /* group = [n0..n1-1, n2..n3-1, ...] */
10827 /* tempv[0..n0-1] -> ap[0..n0-1], s += n0,
10828 tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
10829 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
10830 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
10832 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
10833 At last, s will become n_new and t will become count. */
10835 for (i = 0, ap = mp->atoms, prp = mp->molprops; i <= mp->natoms + mp->nmolprops; i++) {
10836 int s, t, j, ns, ne;
10838 /* if i == mp->natoms, mp->frame_cells is handled */
10839 if (i == mp->natoms) {
10840 if (mp->cell == NULL || mp->frame_cells == NULL)
10842 mult = 4 * sizeof(Vector);
10843 vp = mp->frame_cells;
10845 } else if (i < mp->natoms) {
10846 mult = sizeof(Vector);
10849 NewArray(&ap->frames, &ap->nframes, sizeof(Vector), n_old);
10850 if (ap->frames == NULL) {
10851 __MoleculeUnlock(mp);
10856 old_count = ap->nframes;
10858 mult = sizeof(Double);
10859 vp = (Vector *)prp->propvals;
10863 /* Copy vp to tempv */
10864 memset(tempv, 0, mult * n_old);
10865 memmove(tempv, vp, mult * (old_count > n_old ? n_old : old_count));
10866 ne = ns = s = t = 0;
10867 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10871 memmove((char *)vp + s * mult, (char *)tempv + ne * mult, mult * (ns - ne));
10874 ne = IntGroupGetEndPoint(group, j);
10878 if (i < mp->natoms)
10879 outFrame[natoms * t + i] = tempv[ns];
10880 else if (i == mp->natoms) {
10881 if (outFrameCell != NULL) {
10882 outFrameCell[t * 4] = tempv[ns * 4];
10883 outFrameCell[t * 4 + 1] = tempv[ns * 4 + 1];
10884 outFrameCell[t * 4 + 2] = tempv[ns * 4 + 2];
10885 outFrameCell[t * 4 + 3] = tempv[ns * 4 + 3];
10893 memmove((char *)vp + s * mult, (char *)tempv + ne * mult, mult * (n_old - ne));
10896 if (i < mp->natoms)
10901 if (i < mp->natoms) {
10905 } else if (i == mp->natoms) {
10906 free(mp->frame_cells);
10907 mp->frame_cells = NULL;
10908 mp->nframe_cells = 0;
10910 prp->propvals = (Double *)realloc(prp->propvals, sizeof(Double));
10913 if (i < mp->natoms) {
10914 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), s - 1, NULL);
10916 } else if (i == mp->natoms) {
10917 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, s - 1, NULL);
10918 mp->nframe_cells = s;
10920 prp->propvals = (Double *)realloc(prp->propvals, sizeof(Double) * s);
10923 if (i < mp->natoms) {
10924 ap = ATOM_NEXT(ap);
10925 } else if (i > mp->natoms) {
10930 mp->nframes = nframes;
10932 /* Select the "last" frame; do not "copy back" the coordinates to the frame table */
10933 /* i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
10934 MoleculeSelectFrame(mp, new_cframe, 0);
10936 IntGroupRelease(group);
10938 MoleculeIncrementModifyCount(mp);
10939 __MoleculeUnlock(mp);
10944 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
10946 int i, cframe, nframes, modified;
10948 cframe = mp->cframe;
10949 nframes = MoleculeGetNumberOfFrames(mp);
10951 frame = mp->cframe;
10952 if (mp == NULL || mp->natoms == 0 || frame < 0 || frame >= nframes)
10955 __MoleculeLock(mp);
10956 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10957 if (copyback && cframe >= 0 && cframe < ap->nframes) {
10958 /* Write the current coordinate back to the frame array */
10959 ap->frames[cframe] = ap->r;
10961 if ((frame != cframe || copyback == 0) && frame >= 0 && frame < ap->nframes) {
10962 /* Read the coordinate from the frame array */
10963 ap->r = ap->frames[frame];
10968 if (mp->cell != NULL && mp->frame_cells != NULL) {
10969 /* Write the current cell back to the frame_cells array */
10970 if (copyback && cframe >= 0) {
10971 Vector *vp = (Vector *)AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, cframe, NULL);
10972 vp[0] = mp->cell->axes[0];
10973 vp[1] = mp->cell->axes[1];
10974 vp[2] = mp->cell->axes[2];
10975 vp[3] = mp->cell->origin;
10977 /* Set the cell from the frame array */
10978 if ((frame != cframe || copyback == 0) && frame >= 0 && frame < mp->nframe_cells) {
10979 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);
10981 MoleculeAmendBySymmetry(mp, NULL, NULL, NULL);
10984 mp->cframe = frame;
10986 mp->needsMDCopyCoordinates = 1;
10987 __MoleculeUnlock(mp);
10988 sMoleculeNotifyChangeAppearance(mp);
10992 /* If molecule is multi-frame, then flush the current information to the frame buffer.
10993 Returns the number of frames. */
10995 MoleculeFlushFrames(Molecule *mp)
10997 int nframes = MoleculeGetNumberOfFrames(mp);
10999 MoleculeSelectFrame(mp, mp->cframe, 1);
11004 MoleculeReorderFrames(Molecule *mp, const Int *old_idx)
11006 Int *ip, i, j, n, nframes;
11010 if (mp == NULL || old_idx == NULL)
11012 nframes = MoleculeGetNumberOfFrames(mp);
11013 MoleculeFlushFrames(mp);
11014 ip = (Int *)malloc(sizeof(Int) * nframes);
11016 return -1; /* Out of memory */
11017 memset(ip, 0, sizeof(Int) * nframes);
11018 /* Check the argument */
11019 for (i = 0; i < nframes; i++) {
11021 if (j < 0 || j >= nframes || ip[j] != 0) {
11023 return -2; /* Bad argument */
11028 dp = (Double *)malloc(sizeof(Double) * nframes * 12);
11029 for (i = 0, ap = mp->atoms, prp = mp->molprops; i <= mp->natoms + mp->nmolprops; i++) {
11030 for (j = 0; j < nframes; j++) {
11032 if (i < mp->natoms) {
11033 ((Vector *)dp)[j] = (n < ap->nframes ? ap->frames[n] : ap->r);
11034 } else if (i == mp->natoms) {
11035 if (mp->cell != NULL) {
11036 if (n < mp->nframe_cells && mp->frame_cells != NULL)
11037 memmove(dp + j * 12, mp->frame_cells + n * 4, sizeof(Vector) * 4);
11039 ((Vector *)dp)[j * 4] = mp->cell->axes[0];
11040 ((Vector *)dp)[j * 4] = mp->cell->axes[1];
11041 ((Vector *)dp)[j * 4] = mp->cell->axes[2];
11042 ((Vector *)dp)[j * 4] = mp->cell->origin;
11046 dp[j] = prp->propvals[n];
11049 for (j = 0; j < nframes; j++) {
11050 if (i < mp->natoms) {
11051 if (ap->nframes <= j)
11052 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes - 1, NULL);
11053 ap->frames[j] = ((Vector *)dp)[j];
11054 } else if (i == mp->natoms) {
11055 if (mp->cell != NULL) {
11056 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, nframes - 1, NULL);
11057 memmove(mp->frame_cells + j * 4, dp + j * 12, sizeof(Vector) * 4);
11060 prp->propvals[j] = dp[j];
11063 if (i < mp->natoms)
11064 ap = ATOM_NEXT(ap);
11065 else if (i > mp->natoms)
11069 MoleculeSelectFrame(mp, mp->cframe, 0);
11073 #pragma mark ====== Molecule Propeties ======
11076 MoleculeCreateProperty(Molecule *mp, const char *name)
11080 for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
11081 if (strcmp(prp->propname, name) == 0)
11084 prp = (MolProp *)calloc(sizeof(MolProp), 1);
11087 prp->propname = strdup(name);
11088 if (prp->propname == NULL)
11090 i = MoleculeGetNumberOfFrames(mp);
11091 prp->propvals = (Double *)calloc(sizeof(Double), i);
11092 if (prp->propvals == NULL)
11094 AssignArray(&mp->molprops, &mp->nmolprops, sizeof(MolProp), mp->nmolprops, prp);
11096 return mp->nmolprops - 1;
11100 MoleculeLookUpProperty(Molecule *mp, const char *name)
11104 for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
11105 if (strcmp(prp->propname, name) == 0)
11112 MoleculeDeletePropertyAtIndex(Molecule *mp, int idx)
11114 if (idx >= 0 && idx < mp->nmolprops) {
11115 free(mp->molprops[idx].propname);
11116 free(mp->molprops[idx].propvals);
11117 DeleteArray(&mp->molprops, &mp->nmolprops, sizeof(MolProp), idx, 1, NULL);
11124 MoleculeSetProperty(Molecule *mp, int idx, IntGroup *ig, const Double *values)
11126 IntGroupIterator iter;
11128 if (idx < 0 || idx >= mp->nmolprops)
11130 IntGroupIteratorInit(ig, &iter);
11131 nframes = MoleculeGetNumberOfFrames(mp);
11133 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
11136 mp->molprops[idx].propvals[i] = values[n];
11139 IntGroupIteratorRelease(&iter);
11144 MoleculeGetProperty(Molecule *mp, int idx, IntGroup *ig, Double *outValues)
11146 IntGroupIterator iter;
11148 if (idx < 0 || idx >= mp->nmolprops)
11150 IntGroupIteratorInit(ig, &iter);
11151 nframes = MoleculeGetNumberOfFrames(mp);
11153 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
11156 outValues[n] = mp->molprops[idx].propvals[i];
11159 IntGroupIteratorRelease(&iter);
11163 #pragma mark ====== Pi Atoms ======
11166 sMoleculeCalculatePiAnchorPosition(Atom *ap, Atom *atoms)
11170 cp = AtomConnectData(&ap->anchor->connect);
11171 n = ap->anchor->connect.count;
11173 for (j = 0; j < n; j++) {
11174 Double w = ap->anchor->coeffs[j];
11175 ap2 = ATOM_AT_INDEX(atoms, cp[j]);
11176 VecScaleInc(ap->r, ap2->r, w);
11181 MoleculeUpdatePiAnchorPositions(Molecule *mol)
11185 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
11186 if (ap->anchor == NULL)
11188 sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
11193 MoleculeCalculatePiAnchorPosition(Molecule *mol, int idx)
11196 if (mol == NULL || idx < 0 || idx >= mol->natoms)
11198 ap = ATOM_AT_INDEX(mol->atoms, idx);
11199 if (ap->anchor == NULL)
11201 sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
11205 MoleculeSetPiAnchorList(Molecule *mol, Int idx, Int nentries, Int *entries, Double *weights, Int *nUndoActions, struct MolAction ***undoActions)
11208 Int *ip, i, j, n, *np;
11210 if (mol == NULL || idx < 0 || idx >= mol->natoms || nentries <= 1)
11211 return -1; /* Invalid argument */
11212 if (weights != NULL) {
11214 for (i = 0; i < nentries; i++) {
11215 if (weights[i] <= 0.0) {
11216 return 10; /* Weights must be positive */
11221 } else d = 1.0 / nentries;
11222 ap = ATOM_AT_INDEX(mol->atoms, idx);
11223 if (ap->anchor != NULL) {
11224 /* Already an anchor: check if bonds/angles/dihedrals have entries related to this anchor */
11225 IntGroup *bg, *ag, *dg, *ig;
11226 Int *ibuf, ibufsize;
11228 bg = ag = dg = ig = NULL;
11229 ip = AtomConnectData(&ap->anchor->connect);
11230 for (i = 0; i < ap->anchor->connect.count; i++) {
11232 for (j = 0; j < nentries; j++) {
11233 if (n == entries[j])
11236 if (j == nentries) {
11237 /* This entry will disappear: if any bond/angle/dihedral has idx-n pair, that should be removed. */
11238 for (j = 0, np = mol->bonds; j < mol->nbonds; j++, np += 2) {
11239 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[0])) {
11241 bg = IntGroupNew();
11242 IntGroupAdd(bg, j, 1);
11245 for (j = 0, np = mol->angles; j < mol->nangles; j++, np += 3) {
11246 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) ||
11247 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1])) {
11249 ag = IntGroupNew();
11250 IntGroupAdd(ag, j, 1);
11253 for (j = 0, np = mol->dihedrals; j < mol->ndihedrals; j++, np += 4) {
11254 if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) || (idx == np[2] && n == np[3]) ||
11255 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[3] && n == np[2])) {
11257 dg = IntGroupNew();
11258 IntGroupAdd(dg, j, 1);
11261 for (j = 0, np = mol->impropers; j < mol->nimpropers; j++, np += 4) {
11262 if ((idx == np[0] && n == np[2]) || (idx == np[1] && n == np[2]) || (idx == np[3] && n == np[2]) ||
11263 (idx == np[2] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[2] && n == np[3])) {
11265 ig = IntGroupNew();
11266 IntGroupAdd(ig, j, 1);
11274 /* Delete impropers (with undo info) */
11275 i = IntGroupGetCount(ig);
11276 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
11277 MoleculeDeleteImpropers(mol, ibuf, ig);
11278 if (nUndoActions != NULL && undoActions != NULL) {
11279 act = MolActionNew(gMolActionAddImpropers, i * 4, ibuf, ig);
11280 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11282 IntGroupRelease(ig);
11285 /* Delete dihedrals (with undo info) */
11286 i = IntGroupGetCount(dg);
11287 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
11288 MoleculeDeleteDihedrals(mol, ibuf, dg);
11289 if (nUndoActions != NULL && undoActions != NULL) {
11290 act = MolActionNew(gMolActionAddDihedrals, i * 4, ibuf, dg);
11291 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11293 IntGroupRelease(dg);
11296 /* Delete angles (with undo info) */
11297 i = IntGroupGetCount(ag);
11298 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 3 - 1, NULL);
11299 MoleculeDeleteAngles(mol, ibuf, ag);
11300 if (nUndoActions != NULL && undoActions != NULL) {
11301 act = MolActionNew(gMolActionAddAngles, i * 3, ibuf, ag);
11302 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11304 IntGroupRelease(ag);
11307 /* Delete bonds (with undo info) */
11308 i = IntGroupGetCount(bg);
11309 AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 2 - 1, NULL);
11310 MoleculeDeleteBonds(mol, ibuf, bg, NULL, NULL);
11311 if (nUndoActions != NULL && undoActions != NULL) {
11312 act = MolActionNew(gMolActionAddBondsForUndo, i * 2, ibuf, bg);
11313 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11315 IntGroupRelease(bg);
11318 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
11320 AtomConnectResize(&ap->anchor->connect, nentries);
11321 memmove(AtomConnectData(&ap->anchor->connect), entries, sizeof(Int) * nentries);
11322 AssignArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), nentries - 1, NULL);
11323 if (weights != NULL) {
11324 memmove(ap->anchor->coeffs, weights, sizeof(Double) * nentries);
11325 for (i = 0; i < nentries; i++)
11326 ap->anchor->coeffs[i] *= d; /* Normalize weight */
11328 for (i = 0; i < nentries; i++)
11329 ap->anchor->coeffs[i] = d;
11331 MoleculeCalculatePiAnchorPosition(mol, idx);
11335 #pragma mark ====== MO calculation ======
11337 /* Calculate an MO value for a single point. */
11338 /* Index is the MO number (1-based); 0 denotes "arbitrary vector" */
11339 /* tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom. */
11341 sCalcMOPoint(Molecule *mp, const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
11345 Double val, tval, *cnp, *tmpp, *mobasep, *mop;
11347 /* Cache dr and |dr|^2 */
11349 index = bset->nmos + 1;
11350 for (i = 0; i < mp->natoms; i++) {
11352 r = ATOM_AT_INDEX(mp->atoms, i)->r;
11353 tmp[i * 4] = r.x = (vp->x - r.x) * kAngstrom2Bohr;
11354 tmp[i * 4 + 1] = r.y = (vp->y - r.y) * kAngstrom2Bohr;
11355 tmp[i * 4 + 2] = r.z = (vp->z - r.z) * kAngstrom2Bohr;
11356 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
11358 /* Iterate over all shells */
11360 mobasep = bset->mo + (index - 1) * bset->ncomps;
11361 for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
11362 pp = bset->priminfos + sp->p_idx;
11363 cnp = bset->cns + sp->cn_idx;
11364 if (sp->a_idx >= mp->natoms)
11365 return 0.0; /* This may happen when molecule is edited after setting up MO info */
11366 tmpp = tmp + sp->a_idx * 4;
11367 mop = mobasep + sp->m_idx;
11371 for (j = 0; j < sp->nprim; j++) {
11372 tval += *cnp++ * exp(-pp->A * tmpp[3]);
11375 val += mop[0] * tval;
11381 for (j = 0; j < sp->nprim; j++) {
11382 tval = exp(-pp->A * tmpp[3]);
11383 x += *cnp++ * tval;
11384 y += *cnp++ * tval;
11385 z += *cnp++ * tval;
11388 x *= mop[0] * tmpp[0];
11389 y *= mop[1] * tmpp[1];
11390 z *= mop[2] * tmpp[2];
11394 case kGTOType_SP: {
11397 for (j = 0; j < sp->nprim; j++) {
11398 tval = exp(-pp->A * tmpp[3]);
11399 t += *cnp++ * tval;
11400 x += *cnp++ * tval;
11401 y += *cnp++ * tval;
11402 z += *cnp++ * tval;
11406 x *= mop[1] * tmpp[0];
11407 y *= mop[2] * tmpp[1];
11408 z *= mop[3] * tmpp[2];
11409 val += t + x + y + z;
11413 Double xx, yy, zz, xy, xz, yz;
11414 xx = yy = zz = xy = xz = yz = 0;
11415 for (j = 0; j < sp->nprim; j++) {
11416 tval = exp(-pp->A * tmpp[3]);
11417 xx += *cnp++ * tval;
11418 yy += *cnp++ * tval;
11419 zz += *cnp++ * tval;
11420 xy += *cnp++ * tval;
11421 xz += *cnp++ * tval;
11422 yz += *cnp++ * tval;
11425 xx *= mop[0] * tmpp[0] * tmpp[0];
11426 yy *= mop[1] * tmpp[1] * tmpp[1];
11427 zz *= mop[2] * tmpp[2] * tmpp[2];
11428 xy *= mop[3] * tmpp[0] * tmpp[1];
11429 xz *= mop[4] * tmpp[0] * tmpp[2];
11430 yz *= mop[5] * tmpp[1] * tmpp[2];
11431 val += xx + yy + zz + xy + xz + yz;
11434 case kGTOType_D5: {
11435 Double d0, d1p, d1n, d2p, d2n;
11436 d0 = d1p = d1n = d2p = d2n = 0;
11437 for (j = 0; j < sp->nprim; j++) {
11438 tval = exp(-pp->A * tmpp[3]);
11439 d0 += *cnp++ * tval;
11440 d1p += *cnp++ * tval;
11441 d1n += *cnp++ * tval;
11442 d2p += *cnp++ * tval;
11443 d2n += *cnp++ * tval;
11446 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
11447 d1p *= mop[1] * tmpp[0] * tmpp[2];
11448 d1n *= mop[2] * tmpp[1] * tmpp[2];
11449 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
11450 d2n *= mop[4] * tmpp[0] * tmpp[1];
11451 val += d0 + d1p + d1n + d2p + d2n;
11454 /* TODO: Support F/F7 and G/G9 type orbitals */
11460 /* Calculate one MO. The input vectors are angstrom unit (changed from bohr unit: 20140520) */
11461 /* mono is the MO number (1-based); 0 denotes "arbitrary vector" */
11463 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)
11465 int ix, iy, iz, n, nn;
11468 if (mp == NULL || mp->bset == NULL)
11470 if (mp->bset->cns == NULL) {
11471 if (sSetupGaussianCoefficients(mp->bset) != 0)
11474 if (mp->bset->natoms_bs > mp->natoms)
11475 return -3; /* Number of atoms is smaller than expected (internal error) */
11477 cp = (Cube *)calloc(sizeof(Cube), 1);
11481 cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
11482 if (cp->dp == NULL) {
11495 /* TODO: use multithread */
11496 tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms_bs * 4);
11503 for (ix = 0; ix < nx; ix++) {
11505 for (iy = 0; iy < ny; iy++) {
11506 for (iz = 0; iz < nz; iz++) {
11507 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
11508 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
11509 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
11510 cp->dp[n++] = sCalcMOPoint(mp, mp->bset, mono, &p, tmp);
11512 if (callback != NULL && n - nn > 100) {
11514 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
11518 return -2; /* User interrupt */
11525 AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
11526 return mp->bset->ncubes - 1;
11529 /* Output values are in angstrom unit (changed from bohr unit: 20140520) */
11531 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
11534 Vector rmin, rmax, r;
11535 Double dr, dx, dy, dz;
11537 if (mp == NULL || mp->bset == NULL)
11541 rmin.x = rmin.y = rmin.z = 1e10;
11542 rmax.x = rmax.y = rmax.z = -1e10;
11543 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
11544 dr = RadiusForAtomicNumber(ap->atomicNumber);
11548 dr = dr * 3.0 + 2.0;
11549 if (rmin.x > r.x - dr)
11551 if (rmin.y > r.y - dr)
11553 if (rmin.z > r.z - dr)
11555 if (rmax.x < r.x + dr)
11557 if (rmax.y < r.y + dr)
11559 if (rmax.z < r.z + dr)
11562 dx = rmax.x - rmin.x;
11563 dy = rmax.y - rmin.y;
11564 dz = rmax.z - rmin.z;
11565 dr = pow(dx * dy * dz / npoints, 1.0/3.0);
11566 *nx = floor(dx / dr + 0.5);
11567 *ny = floor(dy / dr + 0.5);
11568 *nz = floor(dz / dr + 0.5);
11576 xp->x = yp->y = zp->z = dr;
11577 xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
11582 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
11584 if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
11586 return mp->bset->cubes[index];
11590 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
11593 if (mp == NULL || mp->bset == NULL)
11595 for (i = 0; i < mp->bset->ncubes; i++) {
11596 if (mp->bset->cubes[i]->idn == mono)
11603 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
11606 if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
11608 CubeRelease(mp->bset->cubes[index]);
11610 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
11611 if (--(mp->bset->ncubes) == 0) {
11612 free(mp->bset->cubes);
11613 mp->bset->cubes = NULL;
11615 return mp->bset->ncubes;
11619 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
11624 if (mp == NULL || mp->bset == NULL)
11625 return -1; /* Molecule or the basis set information is empty */
11626 cp = MoleculeGetCubeAtIndex(mp, index);
11628 return -2; /* MO not yet calculated */
11629 fp = fopen(fname, "wb");
11631 return -3; /* Cannot create file */
11633 /* Comment lines */
11634 fprintf(fp, "%s MO=%d\n", comment, cp->idn);
11635 fprintf(fp, " MO coefficients\n");
11637 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms_bs),
11638 cp->origin.x * kAngstrom2Bohr, cp->origin.y * kAngstrom2Bohr, cp->origin.z * kAngstrom2Bohr);
11639 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx,
11640 cp->dx.x * kAngstrom2Bohr, cp->dx.y * kAngstrom2Bohr, cp->dx.z * kAngstrom2Bohr);
11641 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny,
11642 cp->dy.x * kAngstrom2Bohr, cp->dy.y * kAngstrom2Bohr, cp->dy.z * kAngstrom2Bohr);
11643 fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz,
11644 cp->dz.x * kAngstrom2Bohr, cp->dz.y * kAngstrom2Bohr, cp->dz.z * kAngstrom2Bohr);
11646 /* Atomic information */
11647 for (i = 0; i < mp->natoms; i++) {
11648 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
11649 /* The second number should actually be the effective charge */
11650 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber,
11651 ap->r.x * kAngstrom2Bohr, ap->r.y * kAngstrom2Bohr, ap->r.z * kAngstrom2Bohr);
11653 fprintf(fp, "%5d%5d\n", 1, 1);
11656 for (i = n = 0; i < cp->nx; i++) {
11657 for (j = 0; j < cp->ny; j++) {
11658 for (k = 0; k < cp->nz; k++) {
11659 /* On Windows, the "%e" format writes the exponent in 3 digits, but
11660 this is not standard. So we avoid using %e */
11661 Double d = cp->dp[n++];
11664 if (d >= -1.0e-90 && d <= 1.0e-90) {
11668 exponent = (int)floor(log10(fabs(d)));
11669 base = d * pow(10, -1.0 * exponent);
11671 fprintf(fp, " %8.5fe%+03d", base, exponent);
11672 /* fprintf(fp, " %12.5e", d); */
11673 if (k == cp->nz - 1 || k % 6 == 5)
11682 #pragma mark ====== Marching Cube (for isosurface) ======
11685 MoleculeClearMCube(Molecule *mol, Int nx, Int ny, Int nz, const Vector *origin, Double dx, Double dy, Double dz)
11687 MCube *mc = mol->mcube;
11689 float rgba[8] = { 1, 1, 1, 0.6, 0, 0, 1, 0.6 };
11694 free(mc->c[0].cubepoints);
11695 free(mc->c[0].triangles);
11697 free(mc->c[1].cubepoints);
11698 free(mc->c[1].triangles);
11699 memmove(rgba, mc->c[0].rgba, sizeof(float) * 4);
11700 memmove(rgba + 4, mc->c[1].rgba, sizeof(float) * 4);
11704 if (nx > 0 && ny > 0 && nz > 0) {
11705 mc = (MCube *)calloc(sizeof(MCube), 1);
11707 /* round up to nearest 4N+1 integer */
11711 mc->nx = (nx + 2) / 4 * 4 + 1;
11712 mc->ny = (ny + 2) / 4 * 4 + 1;
11713 mc->nz = (nz + 2) / 4 * 4 + 1;
11714 mc->dx = dx / mc->nx;
11715 mc->dy = dy / mc->ny;
11716 mc->dz = dz / mc->nz;
11717 mc->origin = *origin;
11718 mc->dp = (Double *)malloc(sizeof(Double) * mc->nx * mc->ny * mc->nz);
11719 if (mc->dp == NULL) {
11723 mc->radii = (Double *)calloc(sizeof(Double), mol->natoms);
11724 if (mc->radii == NULL) {
11729 mc->nradii = mol->natoms;
11730 mc->c[0].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
11731 mc->c[1].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
11732 if (mc->c[0].fp == NULL || mc->c[1].fp == NULL) {
11740 for (i = 0; i < mc->nx * mc->ny * mc->nz; i++) {
11741 mc->dp[i] = DBL_MAX;
11743 memmove(mc->c[0].rgba, rgba, sizeof(float) * 4);
11744 memmove(mc->c[1].rgba, rgba + 4, sizeof(float) * 4);
11747 MoleculeCallback_notifyModification(mol, 0);
11751 static int sMarchingCubeTable[256][16] = {
11752 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11753 {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11754 {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11755 {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11756 {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11757 {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11758 {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11759 {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
11760 {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11761 {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11762 {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11763 {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
11764 {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11765 {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
11766 {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
11767 {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11768 {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11769 {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11770 {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11771 {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
11772 {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11773 {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
11774 {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
11775 {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
11776 {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11777 {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
11778 {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
11779 {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
11780 {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
11781 {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
11782 {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
11783 {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
11784 {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11785 {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11786 {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11787 {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
11788 {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11789 {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
11790 {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
11791 {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
11792 {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11793 {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
11794 {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
11795 {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
11796 {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
11797 {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
11798 {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
11799 {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
11800 {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11801 {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
11802 {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
11803 {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11804 {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
11805 {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
11806 {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
11807 {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
11808 {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
11809 {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
11810 {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
11811 {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
11812 {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
11813 {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
11814 {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
11815 {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11816 {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11817 {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11818 {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11819 {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
11820 {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11821 {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
11822 {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
11823 {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
11824 {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11825 {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
11826 {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
11827 {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
11828 {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
11829 {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
11830 {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
11831 {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
11832 {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11833 {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
11834 {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
11835 {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
11836 {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
11837 {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
11838 {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
11839 {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
11840 {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
11841 {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
11842 {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
11843 {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
11844 {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
11845 {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
11846 {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
11847 {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
11848 {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11849 {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
11850 {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
11851 {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
11852 {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
11853 {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
11854 {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11855 {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
11856 {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
11857 {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
11858 {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
11859 {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
11860 {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
11861 {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
11862 {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
11863 {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11864 {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
11865 {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
11866 {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
11867 {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
11868 {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
11869 {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
11870 {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
11871 {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11872 {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
11873 {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
11874 {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
11875 {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
11876 {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
11877 {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11878 {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
11879 {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11880 {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11881 {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11882 {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11883 {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
11884 {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11885 {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
11886 {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
11887 {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
11888 {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11889 {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
11890 {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
11891 {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
11892 {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
11893 {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
11894 {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
11895 {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
11896 {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11897 {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
11898 {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
11899 {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
11900 {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
11901 {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
11902 {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
11903 {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
11904 {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
11905 {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11906 {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
11907 {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
11908 {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
11909 {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
11910 {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
11911 {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11912 {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11913 {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
11914 {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
11915 {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
11916 {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
11917 {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
11918 {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
11919 {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
11920 {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
11921 {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
11922 {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
11923 {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
11924 {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
11925 {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
11926 {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
11927 {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
11928 {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
11929 {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
11930 {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
11931 {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
11932 {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
11933 {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
11934 {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
11935 {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
11936 {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
11937 {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
11938 {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
11939 {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11940 {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
11941 {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
11942 {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11943 {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11944 {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11945 {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
11946 {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
11947 {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
11948 {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
11949 {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
11950 {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
11951 {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
11952 {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
11953 {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
11954 {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
11955 {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
11956 {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11957 {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
11958 {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
11959 {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11960 {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
11961 {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
11962 {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
11963 {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
11964 {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
11965 {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
11966 {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
11967 {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11968 {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
11969 {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
11970 {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
11971 {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
11972 {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
11973 {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11974 {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
11975 {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11976 {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
11977 {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
11978 {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
11979 {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
11980 {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
11981 {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
11982 {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
11983 {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
11984 {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
11985 {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
11986 {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
11987 {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11988 {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
11989 {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
11990 {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11991 {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11992 {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11993 {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
11994 {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
11995 {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11996 {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
11997 {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
11998 {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11999 {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12000 {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
12001 {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12002 {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
12003 {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12004 {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12005 {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12006 {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12007 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
12010 /* Recalculate the MCube */
12011 /* If idn < 0, then the current grid settings and values are unchanged, and */
12012 /* only the marching cubes are regenerated. */
12014 MoleculeUpdateMCube(Molecule *mol, int idn)
12016 Int retval, step, sn;
12017 Int n, ix, iy, iz, nx, ny, nz;
12018 Int nn, iix, iiy, iiz;
12019 Int ncubepoints, c1, c2, c3;
12021 Double thres, *tmp, dd;
12027 if (mol == NULL || mol->bset == NULL || mol->mcube == NULL)
12029 if (mol->bset->cns == NULL) {
12030 if (sSetupGaussianCoefficients(mol->bset) != 0)
12033 if (mol->bset->natoms_bs > mol->natoms)
12034 return -1; /* Number of atoms is smaller than expected */
12039 Double *mobasep, *mop, mopmax;
12040 Double xmin, xmax, ymin, ymax, zmin, zmax;
12041 /* Clear mcube values */
12042 for (ix = 0; ix < mc->nx * mc->ny * mc->nz; ix++) {
12043 mc->dp[ix] = DBL_MAX;
12044 mc->c[0].fp[ix] = 0;
12045 mc->c[1].fp[ix] = 0;
12048 /* Estimate the orbital sizes */
12049 mc->radii = (Double *)realloc(mc->radii, sizeof(Double) * mol->natoms);
12050 if (mc->radii == NULL)
12051 return -2; /* Out of memory */
12052 mc->nradii = mol->natoms;
12053 if (mc->idn == mol->bset->nmos + 1) {
12054 /* Total electron density */
12055 for (ix = 0; ix < mol->natoms; ix++)
12056 mc->radii[ix] = 1.0;
12059 memset(mc->radii, 0, sizeof(Double) * mc->nradii);
12060 mobasep = mol->bset->mo + (mc->idn == 0 ? mol->bset->nmos : mc->idn - 1) * mol->bset->ncomps;
12062 for (ix = 0, sp = mol->bset->shells; ix < mol->bset->nshells; ix++, sp++) {
12063 if (sp->a_idx >= mol->natoms)
12064 continue; /* This may happen when molecule is edited after setting up MO info */
12065 mop = mobasep + sp->m_idx;
12066 for (iy = 0; iy < sp->ncomp; iy++) {
12067 dd = fabs(mop[iy]);
12068 if (dd > mc->radii[sp->a_idx])
12069 mc->radii[sp->a_idx] = dd;
12075 xmin = ymin = zmin = 1e10;
12076 xmax = ymax = zmax = -1e10;
12077 for (ix = 0, ap = mol->atoms; ix < mol->natoms; ix++, ap = ATOM_NEXT(ap)) {
12078 dd = RadiusForAtomicNumber(ap->atomicNumber);
12079 dd = (dd * 2.0 + 1.0) * (mc->radii[ix] / mopmax) * (mc->expand > 0.0 ? mc->expand : 1.0);
12080 mc->radii[ix] = dd;
12083 if (p.x - dd < xmin)
12085 if (p.y - dd < ymin)
12087 if (p.z - dd < zmin)
12089 if (p.x + dd > xmax)
12091 if (p.y + dd > ymax)
12093 if (p.z + dd > zmax)
12096 mc->origin.x = xmin;
12097 mc->origin.y = ymin;
12098 mc->origin.z = zmin;
12099 mc->dx = (xmax - xmin) / mc->nx;
12100 mc->dy = (ymax - ymin) / mc->ny;
12101 mc->dz = (zmax - zmin) / mc->nz;
12104 /* Temporary work area */
12105 tmp = (Double *)calloc(sizeof(Double), mol->bset->natoms_bs * 4);
12109 /* TODO: use multithread */
12116 /* Calculate points within certain distances from atoms */
12117 for (nn = 0, ap = mol->atoms; nn < mol->natoms; nn++, ap = ATOM_NEXT(ap)) {
12118 /* dd = RadiusForAtomicNumber(ap->atomicNumber);
12121 dd = dd * 1.5 + 1.0; */
12122 dd = mc->radii[nn];
12123 p.x = ap->r.x - dd - mc->origin.x;
12124 p.y = ap->r.y - dd - mc->origin.y;
12125 p.z = ap->r.z - dd - mc->origin.z;
12129 iix = c1 + ceil(dd * 2.0 / mc->dx);
12130 iiy = c2 + ceil(dd * 2.0 / mc->dy);
12131 iiz = c3 + ceil(dd * 2.0 / mc->dz);
12144 for (ix = c1; ix <= iix; ix++) {
12145 p.x = mc->origin.x + mc->dx * ix;
12146 for (iy = c2; iy <= iiy; iy++) {
12147 p.y = mc->origin.y + mc->dy * iy;
12148 for (iz = c3; iz <= iiz; iz++) {
12149 n = (ix * ny + iy) * nz + iz;
12150 if (mc->dp[n] == DBL_MAX) {
12151 p.z = mc->origin.z + mc->dz * iz;
12152 if (mc->idn == mol->bset->nmos + 1) {
12153 /* Total electron density */
12154 Int ne_alpha, ne_beta;
12156 ne_alpha = mol->bset->ne_alpha;
12157 ne_beta = mol->bset->ne_beta;
12158 if (mol->bset->rflag == 2 && ne_alpha < ne_beta) {
12159 /* ROHF case: ensure ne_alpha >= ne_beta */
12160 ne_beta = ne_alpha;
12161 ne_alpha = mol->bset->ne_beta;
12163 for (sn = 1; sn <= ne_alpha; sn++) {
12164 dd = sCalcMOPoint(mol, mol->bset, sn, &p, tmp);
12166 if (mol->bset->rflag != 0 && sn <= ne_beta)
12170 if (mol->bset->rflag == 0) {
12171 for (sn = 1; sn <= ne_beta; sn++) {
12172 dd = sCalcMOPoint(mol, mol->bset, sn + mol->bset->ncomps, &p, tmp);
12173 mc->dp[n] += dd * dd;
12177 mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
12186 /* (i * step, j * step, k * step) */
12187 for (ix = 0; ix < nx; ix += step) {
12188 for (iy = 0; iy < ny; iy += step) {
12189 for (iz = 0; iz < nz; iz += step) {
12190 n = (ix * ny + iy) * nz + iz;
12191 if (mc->dp[n] == DBL_MAX) {
12192 p.x = mc->origin.x + mc->dx * ix;
12193 p.y = mc->origin.y + mc->dy * iy;
12194 p.z = mc->origin.z + mc->dz * iz;
12195 mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
12202 /* Intermediate points */
12203 for (step = 4; step > 1; step /= 2) {
12205 for (sn = 0; sn <= 1; sn++) {
12207 for (ix = 0; ix < nx - 1; ix += step) {
12208 for (iy = 0; iy < ny - 1; iy += step) {
12209 for (iz = 0; iz < nz - 1; iz += step) {
12211 thres = mc->thres * (sn == 0 ? 1 : -1);
12212 n = (ix * ny + iy) * nz + iz;
12213 if (mc->dp[n] == DBL_MAX || mc->dp[n + step * (nz * (ny + 1) + 1)] == DBL_MAX)
12216 if (mc->dp[n] >= thres)
12218 /* (ix + step, iy, iz) */
12219 if (mc->dp[n + step * ny * nz] >= thres)
12221 /* (ix, iy + step, iz) */
12222 if (mc->dp[n + step * nz] >= thres)
12224 /* (ix + 4, iy + step, iz) */
12225 if (mc->dp[n + step * nz * (ny + 1)] >= thres)
12227 /* (ix, iy, iz + step) */
12228 if (mc->dp[n + step] >= thres)
12230 if (mc->dp[n + step * (ny * nz + 1)] >= thres)
12232 /* (ix, iy + step, iz + step) */
12233 if (mc->dp[n + step * (nz + 1)] >= thres)
12235 /* (ix + step, iy + step, iz + step) */
12236 if (mc->dp[n + step * (nz * (ny + 1) + 1)] >= thres)
12238 if (flags != 0 && flags != 255) {
12239 /* Calc the intermediate points */
12240 for (iix = 0; iix <= step; iix += hstep) {
12241 for (iiy = 0; iiy <= step; iiy += hstep) {
12242 for (iiz = 0; iiz <= step; iiz += hstep) {
12243 if (iix % step == 0 && iiy % step == 0 && iiz % step == 0)
12245 nn = n + (iix * ny + iiy) * nz + iiz;
12246 if (mc->dp[nn] == DBL_MAX) {
12247 p.x = mc->origin.x + mc->dx * (ix + iix);
12248 p.y = mc->origin.y + mc->dy * (iy + iiy);
12249 p.z = mc->origin.z + mc->dz * (iz + iiz);
12250 mc->dp[nn] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
12266 /* Calculate vertex positions and normal vectors */
12267 for (sn = 0; sn <= 1; sn++) {
12269 thres = mc->thres * (sn == 0 ? 1 : -1);
12271 for (ix = 0; ix < nx - 1; ix++) {
12272 for (iy = 0; iy < ny - 1; iy++) {
12273 for (iz = 0; iz < nz - 1; iz++) {
12275 nn = (ix * ny + iy) * nz + iz;
12277 if (dd0 == DBL_MAX)
12280 dd1 = mc->dp[nn + ny * nz];
12281 if (dd1 != DBL_MAX)
12282 p.x = (dd1 - dd0) / mc->dx;
12283 else if (ix > 0 && (dd1 = mc->dp[nn - ny * nz]) != DBL_MAX)
12284 p.x = (dd0 - dd1) / mc->dx;
12285 else continue; /* Cannot define gradient */
12286 dd1 = mc->dp[nn + nz];
12287 if (dd1 != DBL_MAX)
12288 p.y = (dd1 - dd0) / mc->dy;
12289 else if (iy > 0 && (dd1 = mc->dp[nn - nz]) != DBL_MAX)
12290 p.y = (dd0 - dd1) / mc->dy;
12292 dd1 = mc->dp[nn + 1];
12293 if (dd1 != DBL_MAX)
12294 p.z = (dd1 - dd0) / mc->dz;
12295 else if (iz > 0 && (dd1 = mc->dp[nn - 1]) != DBL_MAX)
12296 p.z = (dd0 - dd1) / mc->dz;
12298 NormalizeVec(&p, &p);
12300 if (n + 3 >= mc->c[sn].ncubepoints) {
12301 /* Expand cubepoints[] array */
12302 mc->c[sn].cubepoints = (MCubePoint *)realloc(mc->c[sn].cubepoints, sizeof(MCubePoint) * (mc->c[sn].ncubepoints + 8192));
12303 if (mc->c[sn].cubepoints == NULL) {
12304 mc->c[sn].ncubepoints = 0;
12308 mc->c[sn].ncubepoints += 8192;
12310 mcp = mc->c[sn].cubepoints + n;
12311 iix = (dd0 >= thres ? 1 : -1);
12312 /* (x, y, z)->(x + 1, y, z) */
12313 dd1 = mc->dp[nn + ny * nz];
12314 if (dd1 != DBL_MAX) {
12315 iiy = (dd1 >= thres ? 1 : -1);
12319 mcp->d = (thres - dd0) / (dd1 - dd0);
12320 mcp->pos[0] = mc->origin.x + mc->dx * (ix + mcp->d);
12321 mcp->pos[1] = mc->origin.y + mc->dy * iy;
12322 mcp->pos[2] = mc->origin.z + mc->dz * iz;
12323 mcp->grad[0] = p.x;
12324 mcp->grad[1] = p.y;
12325 mcp->grad[2] = p.z;
12330 /* (x, y, z)->(x, y + 1, z) */
12331 dd1 = mc->dp[nn + nz];
12332 if (dd1 != DBL_MAX) {
12333 iiy = (dd1 >= thres ? 1 : -1);
12336 mcp->key = nn * 3 + 1;
12337 mcp->d = (thres - dd0) / (dd1 - dd0);
12338 mcp->pos[0] = mc->origin.x + mc->dx * ix;
12339 mcp->pos[1] = mc->origin.y + mc->dy * (iy + mcp->d);
12340 mcp->pos[2] = mc->origin.z + mc->dz * iz;
12341 mcp->grad[0] = p.x;
12342 mcp->grad[1] = p.y;
12343 mcp->grad[2] = p.z;
12348 /* (x, y, z)->(x, y, z + 1) */
12349 dd1 = mc->dp[nn + 1];
12350 if (dd1 != DBL_MAX) {
12351 iiy = (dd1 >= thres ? 1 : -1);
12354 mcp->key = nn * 3 + 2;
12355 mcp->d = (thres - dd0) / (dd1 - dd0);
12356 mcp->pos[0] = mc->origin.x + mc->dx * ix;
12357 mcp->pos[1] = mc->origin.y + mc->dy * iy;
12358 mcp->pos[2] = mc->origin.z + mc->dz * (iz + mcp->d);
12359 mcp->grad[0] = p.x;
12360 mcp->grad[1] = p.y;
12361 mcp->grad[2] = p.z;
12369 if (n < mc->c[sn].ncubepoints)
12370 mc->c[sn].cubepoints[n].key = -1; /* End mark */
12372 if (ncubepoints < 3) {
12373 /* Less than 3 points: no triangles */
12374 if (mc->c[sn].ntriangles > 0)
12375 mc->c[sn].triangles[0] = -1; /* End mark */
12379 /* Create triangle table */
12381 for (ix = 0; ix < nx - 1; ix++) {
12382 for (iy = 0; iy < ny - 1; iy++) {
12383 for (iz = 0; iz < nz - 1; iz++) {
12384 nn = (ix * ny + iy) * nz + iz;
12386 if ((dd = mc->dp[nn]) == DBL_MAX)
12388 else if (dd >= thres)
12390 if ((dd = mc->dp[nn + ny * nz]) == DBL_MAX)
12392 else if (dd >= thres)
12394 if ((dd = mc->dp[nn + ny * nz + nz]) == DBL_MAX)
12396 else if (dd >= thres)
12398 if ((dd = mc->dp[nn + nz]) == DBL_MAX)
12400 else if (dd >= thres)
12402 if ((dd = mc->dp[nn + 1]) == DBL_MAX)
12404 else if (dd >= thres)
12406 if ((dd = mc->dp[nn + ny * nz + 1]) == DBL_MAX)
12408 else if (dd >= thres)
12410 if ((dd = mc->dp[nn + ny * nz + nz + 1]) == DBL_MAX)
12412 else if (dd >= thres)
12414 if ((dd = mc->dp[nn + nz + 1]) == DBL_MAX)
12416 else if (dd >= thres)
12418 for (iiy = 0; iiy < 15; iiy++) {
12419 nn = sMarchingCubeTable[iix][iiy];
12422 /* key index for edges 0-11 */
12424 case 0: iiz = (( ix * ny + iy ) * nz + iz ) * 3; break;
12425 case 1: iiz = (((ix + 1) * ny + iy ) * nz + iz ) * 3 + 1; break;
12426 case 2: iiz = (( ix * ny + iy + 1) * nz + iz ) * 3; break;
12427 case 3: iiz = (( ix * ny + iy ) * nz + iz ) * 3 + 1; break;
12428 case 4: iiz = (( ix * ny + iy ) * nz + iz + 1) * 3; break;
12429 case 5: iiz = (((ix + 1) * ny + iy ) * nz + iz + 1) * 3 + 1; break;
12430 case 6: iiz = (( ix * ny + iy + 1) * nz + iz + 1) * 3; break;
12431 case 7: iiz = (( ix * ny + iy ) * nz + iz + 1) * 3 + 1; break;
12432 case 8: iiz = (( ix * ny + iy ) * nz + iz ) * 3 + 2; break;
12433 case 9: iiz = (((ix + 1) * ny + iy ) * nz + iz ) * 3 + 2; break;
12434 case 10: iiz = (((ix + 1) * ny + iy + 1) * nz + iz ) * 3 + 2; break;
12435 case 11: iiz = (( ix * ny + iy + 1) * nz + iz ) * 3 + 2; break;
12437 /* Skip this triangle */
12438 iiy = (iiy - iiy % 3) + 2;
12442 /* Look for the key index in cubepoints */
12444 c3 = ncubepoints - 1;
12445 mcp = mc->c[sn].cubepoints;
12448 /* c1 is always less than c3 */
12449 if (c1 + 1 == c3) {
12450 /* end of search */
12451 if (mcp[c1].key == iiz) {
12453 } else if (mcp[c3].key == iiz) {
12460 c2 = (c1 + c3) / 2;
12461 w = mcp[c2].key - iiz;
12471 /* Not found: skip this triangle */
12472 iiy = (iiy - iiy % 3) + 2;
12476 if (n + 1 >= mc->c[sn].ntriangles) {
12477 /* Expand triangles[] array */
12478 mc->c[sn].triangles = (Int *)realloc(mc->c[sn].triangles, sizeof(Int) * (mc->c[sn].ntriangles + 8192));
12479 if (mc->c[sn].triangles == NULL) {
12480 mc->c[sn].ntriangles = 0;
12484 mc->c[sn].ntriangles += 8192;
12486 mc->c[sn].triangles[n] = c2;
12492 if (n < mc->c[sn].ntriangles)
12493 mc->c[sn].triangles[n] = -1; /* End mark */
12495 /* Estimate the normal vector */
12496 for (n = 0, ip = mc->c[sn].triangles; ip[n] >= 0; n += 3) {
12498 for (ix = 0; ix < 3; ix++) {
12499 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
12500 v[ix].x = mcp->pos[0];
12501 v[ix].y = mcp->pos[1];
12502 v[ix].z = mcp->pos[2];
12504 VecDec(v[2], v[0]);
12505 VecDec(v[1], v[0]);
12506 VecCross(v[0], v[1], v[2]);
12507 NormalizeVec(v, v);
12508 for (ix = 0; ix < 3; ix++) {
12509 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
12510 mcp->grad[0] += v[0].x;
12511 mcp->grad[1] += v[0].y;
12512 mcp->grad[2] += v[0].z;
12515 for (n = 0, mcp = mc->c[sn].cubepoints; mcp->key >= 0; mcp++) {
12516 if (mcp->grad[0] != 0.0 || mcp->grad[1] != 0.0 || mcp->grad[2] != 0.0) {
12517 dd = 1.0 / sqrt(mcp->grad[0] * mcp->grad[0] + mcp->grad[1] * mcp->grad[1] + mcp->grad[2] * mcp->grad[2]);
12518 if (mc->thres < 0.0)
12520 mcp->grad[0] *= dd;
12521 mcp->grad[1] *= dd;
12522 mcp->grad[2] *= dd;
12527 MoleculeCallback_notifyModification(mol, 0);
12531 char *MyAppCallback_getDocumentHomeDir(void);
12535 asprintf(&s, "%s/%s", MyAppCallback_getDocumentHomeDir(), "mcube_log.txt");
12536 fp = fopen(s, "w");
12539 for (n = 0; n < mc->nx * mc->ny * mc->nz; n++) {
12540 if (mc->dp[n] == DBL_MAX)
12542 if (dmax < mc->dp[n])
12544 if (dmin > mc->dp[n])
12551 dmax = 1.001 * dmax;
12552 fprintf(fp, "thres = %g = 100\n", mc->thres);
12553 for (iz = 0; iz < mc->nz; iz++) {
12554 fprintf(fp, "z = %d\n", iz);
12555 for (iy = 0; iy < mc->ny; iy++) {
12556 for (ix = 0; ix < mc->nx; ix++) {
12557 n = (ix * ny + iy) * nz + iz;
12560 fprintf(fp, " XXX ");
12562 dd = dd * 100 / mc->thres;
12565 else if (dd < -999.0)
12567 fprintf(fp, "%4d ", (int)(dd));
12575 for (sn = 0; sn <= 1; sn++) {
12576 for (n = 0; n < mc->c[sn].ncubepoints; n++) {
12577 MCubePoint *mcp = mc->c[sn].cubepoints + n;
12582 iz = nn / 3 % mc->nz;
12583 iy = nn / (3 * mc->nz) % mc->ny;
12584 ix = nn / (3 * mc->nz * mc->ny);
12585 fprintf(fp, "%c%d:[%d,%d,%d,%d] (%g,[%g,%g,%g],[%g,%g,%g])\n", (sn == 0 ? 'p' : 'P'),
12586 n, ix, iy, iz, iix,
12587 mcp->d, mcp->pos[0], mcp->pos[1], mcp->pos[2], mcp->grad[0], mcp->grad[1], mcp->grad[2]);
12589 for (n = 0; n < mc->c[sn].ntriangles; n += 3) {
12590 if (mc->c[sn].triangles[n] < 0)
12592 fprintf(fp, "%c%d:(%d,%d,%d)\n", (sn == 0 ? 't' : 'T'), n / 3,
12593 mc->c[sn].triangles[n], mc->c[sn].triangles[n + 1], mc->c[sn].triangles[n + 2]);
12603 MoleculeDeallocateMCube(MCube *mcube)
12606 free(mcube->radii);
12607 free(mcube->c[0].cubepoints);
12608 free(mcube->c[0].triangles);
12609 free(mcube->c[1].cubepoints);
12610 free(mcube->c[1].triangles);